Commit 277ca4ed authored by Dionysis Zindros's avatar Dionysis Zindros
Browse files

Prepare to merge into develop

parent e5769dae
......@@ -60,8 +60,8 @@ Showcase: show details for flavor with id 43
name : C4R2048D10
ram : 2048
image (Compute/Cyclades + Glance)
---------------------------------
image (Compute/Cyclades + Plankton)
-----------------------------------
.. code-block:: text
......
......@@ -6,7 +6,7 @@ Kamaki features a clients API for building third-party client applications that
A good example of an application build on kamaki.clients is kamaki.cli, the command line interface of kamaki.
Since synnefo services are build as OpenStack extensions, an inheritance approach has been chosen for implementing clients for both. In specific, the *compute*, *storage* and *image* modules are clients of the OS compute, OS storage and Glance APIs, respectively. On the contrary, all the other modules are Synnefo extensions (*cyclades* extents *compute*, *pithos* and *pithos_rest_api* extent *storage*) or novel synnefo services (e.g. *astakos*).
Since synnefo services are build as OpenStack extensions, an inheritance approach has been chosen for implementing clients for both. In specific, the *compute*, *storage* and *image* modules are clients of the OS compute, OS storage, respectively. On the contrary, all the other modules are Synnefo extensions (*cyclades* extents *compute*, *pithos* and *pithos_rest_api* extent *storage*) or novel synnefo services (e.g. *astakos*, *image* for *plankton*).
Setup a client instance
-----------------------
......
......@@ -50,7 +50,7 @@ network
image
Manage compute API and glance images.
Manage compute API and Plankton images.
store
......
......@@ -123,10 +123,10 @@ The [global] group is treated by kamaki as a generic group for arbitrary options
* network.cli <UI command specifications for virtual networks>
a special package that is used to load cyclades virtual network commands to kamaki UIs. Don't touch this unless you know what you are doing.
* image.url <Glance image service url>
the url of the Glance service. Set to Okeanos.grnet.gr Plankton service be default. Users should set a different value if they need to use a different service.
* image.url <Plankton image service url>
the url of the Plankton service. Set to Okeanos.grnet.gr Plankton service be default. Users should set a different value if they need to use a different service.
* image.cli <UI command specifications for Glance and Cyclades image service>
* image.cli <UI command specifications for Plankton and Cyclades image service>
a special package that is used to load image-related commands to kamaki UIs. Don't touch this unless you know what you are doing.
* astakos.url <Astakos authentication service url>
......
......@@ -128,7 +128,7 @@ To see the command groups, users should use -h or --help like in example 1.3.1.
config : Configuration commands
flavor : Compute/Cyclades API flavor commands
history: Command history
image : Compute/Cyclades or Glance API image commands
image : Compute/Cyclades or Plankton API image commands
network: Compute/Cyclades API network commands
server : Compute/Cyclades API server commands
store : Pithos+ storage commands
......
......@@ -46,6 +46,7 @@ _commands = [test_cmds]
class _test_init(_command_init):
def main(self, client, method=None):
tests.cnf = self.config
if method:
tests.main([client, method])
else:
......
......@@ -32,8 +32,8 @@
# or implied, of GRNET S.A.
from urlparse import urlparse
#from .pool.http import get_http_connection
from pool.http import get_http_connection
#from objpool.http import get_http_connection
from objpool.http import get_http_connection
from kamaki.clients.connection import HTTPConnection, HTTPResponse
from kamaki.clients.connection.errors import HTTPConnectionError
from kamaki.clients.connection.errors import HTTPResponseError
......
......@@ -110,7 +110,7 @@ class PithosClient(PithosRestAPI):
:param public: (bool)
"""
self.assert_container()
self._assert_container()
if withHashFile:
data = f.read()
......@@ -159,7 +159,7 @@ class PithosClient(PithosRestAPI):
:param public: (bool)
"""
self.assert_container()
self._assert_container()
r = self.object_put(obj,
content_length=0,
etag=etag,
......@@ -328,7 +328,7 @@ class PithosClient(PithosRestAPI):
:param public: (bool)
"""
self.assert_container()
self._assert_container()
#init
block_info = (blocksize, blockhash, size, nblocks) =\
......@@ -727,7 +727,7 @@ class PithosClient(PithosRestAPI):
:raises ClientError: 409 Container is not empty
"""
self.assert_container()
self._assert_container()
r = self.container_delete(until=until,
delimiter=delimiter,
success=(204, 404, 409))
......@@ -822,7 +822,7 @@ class PithosClient(PithosRestAPI):
:param delimiter: (str)
"""
self.assert_container()
self._assert_container()
r = self.object_delete(obj, until=until, delimiter=delimiter)
r.release()
......@@ -938,7 +938,7 @@ class PithosClient(PithosRestAPI):
:param upload_db: progress.bar for uploading
"""
self.assert_container()
self._assert_container()
meta = self.get_container_info()
blocksize = int(meta['x-container-block-size'])
filesize = fstat(source_file.fileno()).st_size
......@@ -993,7 +993,7 @@ class PithosClient(PithosRestAPI):
:param upload_db: progress.bar for uploading
"""
self.assert_container()
self._assert_container()
meta = self.get_container_info()
blocksize = int(meta['x-container-block-size'])
filesize = fstat(source_file.fileno()).st_size
......@@ -1041,7 +1041,7 @@ class PithosClient(PithosRestAPI):
:param delimiter: (str)
"""
self.assert_account()
self._assert_account()
self.container = dst_container
dst_object = dst_object or src_object
src_path = path4url(src_container, src_object)
......@@ -1078,7 +1078,7 @@ class PithosClient(PithosRestAPI):
:param delimiter: (str)
"""
self.assert_account()
self._assert_account()
self.container = dst_container
dst_object = dst_object or src_object
src_path = path4url(src_container, src_object)
......@@ -1101,7 +1101,7 @@ class PithosClient(PithosRestAPI):
:returns: (dict)
"""
self.assert_account()
self._assert_account()
self.set_param('format', 'json')
self.set_param('limit', limit, iff=limit is not None)
......@@ -1118,6 +1118,6 @@ class PithosClient(PithosRestAPI):
:returns: (list)
"""
self.assert_container()
self._assert_container()
r = self.object_get(obj, format='json', version='list')
return r.json['versions']
......@@ -60,7 +60,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_account()
self._assert_account()
path = path4url(self.account)
self.set_param('until', until, iff=until is not None)
......@@ -109,7 +109,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_account()
self._assert_account()
self.set_param('format', format, iff=format is not None)
self.set_param('limit', limit, iff=limit is not None)
......@@ -155,7 +155,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_account()
self._assert_account()
self.set_param('update', iff=update)
......@@ -195,7 +195,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('until', until, iff=until is not None)
......@@ -260,7 +260,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('format', format, iff=format is not None)
self.set_param('limit', limit, iff=limit is not None)
......@@ -302,7 +302,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
if metadata is not None:
for metaname, metaval in metadata.items():
......@@ -350,7 +350,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('format', format, iff=format is not None)
self.set_param('update', iff=update)
......@@ -379,7 +379,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('until', until, iff=until is not None)
self.set_param('delimiter', delimiter, iff=delimiter is not None)
......@@ -419,7 +419,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('version', version, iff=version is not None)
......@@ -475,7 +475,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('format', format, iff=format is not None)
self.set_param('version', version, iff=version is not None)
......@@ -572,7 +572,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('format', format, iff=format is not None)
self.set_param('hashmap', hashmap, iff=hashmap)
......@@ -601,7 +601,7 @@ class PithosRestAPI(StorageClient):
if len(perms):
perms += ';'
perms += '%s=%s'\
% (permission_type, list2str(permission_list, seperator=','))
% (permission_type, list2str(permission_list, separator=','))
self.set_header('X-Object-Sharing', perms)
self.set_header('X-Object-Public', public)
if metadata is not None:
......@@ -673,7 +673,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('format', format, iff=format is not None)
self.set_param('ignore_content_type', iff=ignore_content_type)
......@@ -696,7 +696,7 @@ class PithosRestAPI(StorageClient):
if len(perms):
perms += ';'
perms += '%s=%s'\
% (permission_type, list2str(permission_list, seperator=','))
% (permission_type, list2str(permission_list, separator=','))
self.set_header('X-Object-Sharing', perms)
self.set_header('X-Object-Public', public)
if metadata is not None:
......@@ -764,7 +764,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('format', format, iff=format is not None)
self.set_param('ignore_content_type', iff=ignore_content_type)
......@@ -785,7 +785,7 @@ class PithosRestAPI(StorageClient):
if len(perms):
perms += ';'
perms += '%s=%s'\
% (permission_type, list2str(permission_list, seperator=','))
% (permission_type, list2str(permission_list, separator=','))
self.set_header('X-Object-Sharing', perms)
self.set_header('X-Object-Public', public)
for key, val in metadata.items():
......@@ -869,7 +869,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('format', format, iff=format is not None)
self.set_param('update', iff=update)
......@@ -898,7 +898,7 @@ class PithosRestAPI(StorageClient):
if len(perms):
perms += ';'
perms += '%s=%s'\
% (permission_type, list2str(permission_list, seperator=','))
% (permission_type, list2str(permission_list, separator=','))
self.set_header('X-Object-Sharing', perms)
self.set_header('X-Object-Public', public)
for key, val in metadata.items():
......@@ -921,7 +921,7 @@ class PithosRestAPI(StorageClient):
:returns: ConnectionResponse
"""
self.assert_container()
self._assert_container()
self.set_param('until', until, iff=until is not None)
self.set_param('delimiter', delimiter, iff=delimiter is not None)
......
......@@ -43,17 +43,20 @@ class StorageClient(Client):
self.account = account
self.container = container
def assert_account(self):
def _assert_account(self):
if not self.account:
raise ClientError("No account provided")
def assert_container(self):
self.assert_account()
def _assert_container(self):
self._assert_account()
if not self.container:
raise ClientError("No container provided")
def get_account_info(self):
self.assert_account()
"""
:returns: (dict)
"""
self._assert_account()
path = path4url(self.account)
r = self.head(path, success=(204, 401))
if r.status_code == 401:
......@@ -62,7 +65,10 @@ class StorageClient(Client):
return reply
def replace_account_meta(self, metapairs):
self.assert_account()
"""
:param metapais: (dict) key:val metadata pairs
"""
self._assert_account()
path = path4url(self.account)
for key, val in metapairs:
self.set_header('X-Account-Meta-' + key, val)
......@@ -70,6 +76,9 @@ class StorageClient(Client):
r.release()
def del_account_meta(self, metakey):
"""
:param metakey: (str) metadatum key
"""
headers = self.get_account_info()
self.headers = filter_out(headers,
'X-Account-Meta-' + metakey,
......@@ -81,14 +90,27 @@ class StorageClient(Client):
r.release()
def create_container(self, container):
self.assert_account()
"""
:param container: (str)
:raises ClientError: 202 Container already exists
"""
self._assert_account()
path = path4url(self.account, container)
r = self.put(path, success=(201, 202))
r.release()
if r.status_code == 202:
raise ClientError("Container already exists", r.status_code)
def get_container_info(self, container):
self.assert_account()
"""
:param container: (str)
:returns: (dict)
:raises ClientError: 404 Container does not exist
"""
self._assert_account()
path = path4url(self.account, container)
r = self.head(path, success=(204, 404))
if r.status_code == 404:
......@@ -97,7 +119,13 @@ class StorageClient(Client):
return reply
def delete_container(self, container):
self.assert_account()
"""
:param container: (str)
:raises ClientError: 404 Container does not exist
:raises ClientError: 409 Container not empty
"""
self._assert_account()
path = path4url(self.account, container)
r = self.delete(path, success=(204, 404, 409))
if r.status_code == 404:
......@@ -106,39 +134,61 @@ class StorageClient(Client):
raise ClientError("Container is not empty", r.status_code)
def list_containers(self):
self.assert_account()
"""
:returns: (dict)
"""
self._assert_account()
self.set_param('format', 'json')
path = path4url(self.account)
r = self.get(path, success=(200, 204))
reply = r.json
return reply
def upload_object(self, object, f, size=None):
# This is a naive implementation, it loads the whole file in memory
#Look in pithos for a nice implementation
self.assert_container()
path = path4url(self.account, self.container, object)
def upload_object(self, obj, f, size=None):
""" A simple (naive) implementation.
:param obj: (str)
:param f: an open for reading file descriptor
:param size: (int) number of bytes to upload
"""
self._assert_container()
path = path4url(self.account, self.container, obj)
data = f.read(size) if size is not None else f.read()
r = self.put(path, data=data, success=201)
r.release()
def create_directory(self, object):
self.assert_container()
path = path4url(self.account, self.container, object)
def create_directory(self, obj):
"""
:param obj: (str) directory-object name
"""
self._assert_container()
path = path4url(self.account, self.container, obj)
self.set_header('Content-Type', 'application/directory')
self.set_header('Content-length', '0')
r = self.put(path, success=201)
r.release()
def get_object_info(self, object):
self.assert_container()
path = path4url(self.account, self.container, object)
def get_object_info(self, obj):
"""
:param obj: (str)
:returns: (dict)
"""
self._assert_container()
path = path4url(self.account, self.container, obj)
r = self.head(path, success=200)
reply = r.headers
return reply
def get_object_meta(self, object):
r = filter_in(self.get_object_info(object), 'X-Object-Meta-')
def get_object_meta(self, obj):
"""
:param obj: (str)
:returns: (dict)
"""
r = filter_in(self.get_object_info(obj), 'X-Object-Meta-')
reply = {}
for (key, val) in r.items():
metakey = key.split('-')[-1]
......@@ -146,23 +196,36 @@ class StorageClient(Client):
return reply
def del_object_meta(self, obj, metakey):
self.assert_container()
"""
:param obj: (str)
:param metakey: (str) the metadatum key
"""
self._assert_container()
self.set_header('X-Object-Meta-' + metakey, '')
path = path4url(self.account, self.container, obj)
r = self.post(path, success=202)
r.release()
def replace_object_meta(self, metapairs):
self.assert_container()
"""
:param metapairs: (dict) key:val metadata
"""
self._assert_container()
path = path4url(self.account, self.container)
for key, val in metapairs:
self.set_header('X-Object-Meta-' + key, val)
r = self.post(path, success=202)
r.release()
def get_object(self, object):
self.assert_container()
path = path4url(self.account, self.container, object)
def get_object(self, obj):
"""
:param obj: (str)
:returns: (int, int) # of objects, size in bytes
"""
self._assert_container()
path = path4url(self.account, self.container, obj)
r = self.get(path, success=200)
size = int(r.headers['content-length'])
cnt = r.content
......@@ -170,7 +233,18 @@ class StorageClient(Client):
def copy_object(self, src_container, src_object, dst_container,
dst_object=False):
self.assert_account()
"""Copy an objects from src_contaier:src_object to
dst_container[:dst_object]
:param src_container: (str)
:param src_object: (str)
:param dst_container: (str)
:param dst_object: (str)
"""
self._assert_account()
dst_object = dst_object or src_object
dst_path = path4url(self.account, dst_container, dst_object)
self.set_header('X-Copy-From', path4url(src_container, src_object))
......@@ -180,7 +254,18 @@ class StorageClient(Client):
def move_object(self, src_container, src_object, dst_container,
dst_object=False):
self.assert_account()
"""Move an objects from src_contaier:src_object to
dst_container[:dst_object]
:param src_container: (str)
:param src_object: (str)
:param dst_container: (str)
:param dst_object: (str)
"""
self._assert_account()
dst_object = dst_object or src_object
dst_path = path4url(self.account, dst_container, dst_object)
self.set_header('X-Move-From', path4url(src_container, src_object))
......@@ -188,21 +273,31 @@ class StorageClient(Client):
r = self.put(dst_path, success=201)
r.release()
def delete_object(self, object):
self.assert_container()
path = path4url(self.account, self.container, object)
def delete_object(self, obj):
"""
:param obj: (str)
:raises ClientError: 404 Object not found
"""
self._assert_container()
path = path4url(self.account, self.container, obj)
r = self.delete(path, success=(204, 404))
if r.status_code == 404:
raise ClientError("Object %s not found" % object, r.status_code)
raise ClientError("Object %s not found" % obj, r.status_code)
def list_objects(self):
self.assert_container()
"""
:returns: (dict)
:raises ClientError: 404 Invalid account
"""
self._assert_container()
path = path4url(self.account, self.container)
self.set_param('format', 'json')
r = self.get(path, success=(200, 204, 304, 404), )
if r.status_code == 404:
raise ClientError(\
"Incorrect account (%s) for that container" % self.account,
"Invalid account (%s) for that container" % self.account,
r.status_code)
elif r.status_code == 304:
return []
......@@ -210,14 +305,21 @@ class StorageClient(Client):
return reply
def list_objects_in_path(self, path_prefix):
self.assert_container()
"""
:param path_prefix: (str)
:raises ClientError: 404 Invalid account
:returns: (dict)