diff --git a/code/daisyclient/daisyclient/openstack/common/_i18n.py b/code/daisyclient/daisyclient/openstack/common/_i18n.py index cee8f013..304c6125 100755 --- a/code/daisyclient/daisyclient/openstack/common/_i18n.py +++ b/code/daisyclient/daisyclient/openstack/common/_i18n.py @@ -1,3 +1,6 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. +# # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at diff --git a/code/daisyclient/daisyclient/openstack/common/apiclient/auth.py b/code/daisyclient/daisyclient/openstack/common/apiclient/auth.py index 03ad9e70..eb744a8d 100755 --- a/code/daisyclient/daisyclient/openstack/common/apiclient/auth.py +++ b/code/daisyclient/daisyclient/openstack/common/apiclient/auth.py @@ -1,5 +1,4 @@ # Copyright 2013 OpenStack Foundation -# Copyright 2013 Spanish National Research Council. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/code/daisyclient/daisyclient/openstack/common/apiclient/base.py b/code/daisyclient/daisyclient/openstack/common/apiclient/base.py index 01b74722..1d5c89a0 100755 --- a/code/daisyclient/daisyclient/openstack/common/apiclient/base.py +++ b/code/daisyclient/daisyclient/openstack/common/apiclient/base.py @@ -1,7 +1,4 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 OpenStack Foundation -# Copyright 2012 Grid Dynamics -# Copyright 2013 OpenStack Foundation +# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/code/daisyclient/daisyclient/openstack/common/apiclient/client.py b/code/daisyclient/daisyclient/openstack/common/apiclient/client.py index e1d95f67..bcbbabf3 100755 --- a/code/daisyclient/daisyclient/openstack/common/apiclient/client.py +++ b/code/daisyclient/daisyclient/openstack/common/apiclient/client.py @@ -1,9 +1,4 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 OpenStack Foundation -# Copyright 2011 Piston Cloud Computing, Inc. -# Copyright 2013 Alessio Ababilov -# Copyright 2013 Grid Dynamics -# Copyright 2013 OpenStack Foundation +# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/code/daisyclient/daisyclient/openstack/common/apiclient/exceptions.py b/code/daisyclient/daisyclient/openstack/common/apiclient/exceptions.py index 3fcfe6a2..540a4120 100755 --- a/code/daisyclient/daisyclient/openstack/common/apiclient/exceptions.py +++ b/code/daisyclient/daisyclient/openstack/common/apiclient/exceptions.py @@ -1,7 +1,4 @@ -# Copyright 2010 Jacob Kaplan-Moss -# Copyright 2011 Nebula, Inc. -# Copyright 2013 Alessio Ababilov -# Copyright 2013 OpenStack Foundation +# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/code/daisyclient/daisyclient/openstack/common/apiclient/utils.py b/code/daisyclient/daisyclient/openstack/common/apiclient/utils.py index 0f40c76c..f259c579 100755 --- a/code/daisyclient/daisyclient/openstack/common/apiclient/utils.py +++ b/code/daisyclient/daisyclient/openstack/common/apiclient/utils.py @@ -1,3 +1,5 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain diff --git a/code/daisyclient/daisyclient/v1/param_helper.py b/code/daisyclient/daisyclient/v1/param_helper.py index 362cd90c..89906cb7 100755 --- a/code/daisyclient/daisyclient/v1/param_helper.py +++ b/code/daisyclient/daisyclient/v1/param_helper.py @@ -1,3 +1,18 @@ +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + # -*- coding:utf-8 -*- import os diff --git a/code/daisyclient/daisyclient/v2/__init__.py b/code/daisyclient/daisyclient/v2/__init__.py deleted file mode 100755 index e69de29b..00000000 diff --git a/code/daisyclient/daisyclient/v2/client.py b/code/daisyclient/daisyclient/v2/client.py deleted file mode 100755 index 738ed543..00000000 --- a/code/daisyclient/daisyclient/v2/client.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - - -from daisyclient.common import http -from daisyclient.common import utils -from daisyclient.v2 import image_members -from daisyclient.v2 import image_tags -from daisyclient.v2 import images -from daisyclient.v2 import metadefs -from daisyclient.v2 import schemas -from daisyclient.v2 import tasks - - -class Client(object): - """Client for the OpenStack Images v2 API. - - :param string endpoint: A user-supplied endpoint URL for the glance - service. - :param string token: Token for authentication. - :param integer timeout: Allows customization of the timeout for client - http requests. (optional) - """ - - def __init__(self, endpoint, *args, **kwargs): - endpoint, version = utils.strip_version(endpoint) - self.version = version or 2.0 - self.http_client = http.HTTPClient(endpoint, *args, **kwargs) - - self.schemas = schemas.Controller(self.http_client) - - self.images = images.Controller(self.http_client, self.schemas) - self.image_tags = image_tags.Controller(self.http_client, - self.schemas) - self.image_members = image_members.Controller(self.http_client, - self.schemas) - - self.tasks = tasks.Controller(self.http_client, self.schemas) - - self.metadefs_resource_type = ( - metadefs.ResourceTypeController(self.http_client, self.schemas)) - - self.metadefs_property = ( - metadefs.PropertyController(self.http_client, self.schemas)) - - self.metadefs_object = ( - metadefs.ObjectController(self.http_client, self.schemas)) - - self.metadefs_namespace = ( - metadefs.NamespaceController(self.http_client, self.schemas)) diff --git a/code/daisyclient/daisyclient/v2/image_members.py b/code/daisyclient/daisyclient/v2/image_members.py deleted file mode 100755 index 15ff2601..00000000 --- a/code/daisyclient/daisyclient/v2/image_members.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import warlock - -from daisyclient.common import utils -from daisyclient.v2 import schemas - - -MEMBER_STATUS_VALUES = ('accepted', 'rejected', 'pending') - - -class Controller(object): - def __init__(self, http_client, schema_client): - self.http_client = http_client - self.schema_client = schema_client - - @utils.memoized_property - def model(self): - schema = self.schema_client.get('member') - return warlock.model_factory(schema.raw(), schemas.SchemaBasedModel) - - def list(self, image_id): - url = '/v2/images/%s/members' % image_id - resp, body = self.http_client.get(url) - for member in body['members']: - yield self.model(member) - - def delete(self, image_id, member_id): - self.http_client.delete('/v2/images/%s/members/%s' % - (image_id, member_id)) - - def update(self, image_id, member_id, member_status): - url = '/v2/images/%s/members/%s' % (image_id, member_id) - body = {'status': member_status} - resp, updated_member = self.http_client.put(url, data=body) - return self.model(updated_member) - - def create(self, image_id, member_id): - url = '/v2/images/%s/members' % image_id - body = {'member': member_id} - resp, created_member = self.http_client.post(url, data=body) - return self.model(created_member) diff --git a/code/daisyclient/daisyclient/v2/image_tags.py b/code/daisyclient/daisyclient/v2/image_tags.py deleted file mode 100755 index 7012ce61..00000000 --- a/code/daisyclient/daisyclient/v2/image_tags.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import warlock - -from daisyclient.common import utils -from daisyclient.v2 import schemas - - -class Controller(object): - def __init__(self, http_client, schema_client): - self.http_client = http_client - self.schema_client = schema_client - - @utils.memoized_property - def model(self): - schema = self.schema_client.get('image') - return warlock.model_factory(schema.raw(), schemas.SchemaBasedModel) - - def update(self, image_id, tag_value): - """ - Update an image with the given tag. - - :param image_id: image to be updated with the given tag. - :param tag_value: value of the tag. - """ - url = '/v2/images/%s/tags/%s' % (image_id, tag_value) - self.http_client.put(url) - - def delete(self, image_id, tag_value): - """ - Delete the tag associated with the given image. - - :param image_id: Image whose tag to be deleted. - :param tag_value: tag value to be deleted. - """ - url = '/v2/images/%s/tags/%s' % (image_id, tag_value) - self.http_client.delete(url) diff --git a/code/daisyclient/daisyclient/v2/images.py b/code/daisyclient/daisyclient/v2/images.py deleted file mode 100755 index b3f8fc70..00000000 --- a/code/daisyclient/daisyclient/v2/images.py +++ /dev/null @@ -1,358 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import json - -from oslo_utils import encodeutils -import six -from six.moves.urllib import parse -import warlock - -from daisyclient.common import utils -from daisyclient import exc -from daisyclient.v2 import schemas - -DEFAULT_PAGE_SIZE = 20 - -SORT_DIR_VALUES = ('asc', 'desc') -SORT_KEY_VALUES = ('name', 'status', 'container_format', 'disk_format', - 'size', 'id', 'created_at', 'updated_at') - - -class Controller(object): - - def __init__(self, http_client, schema_client): - self.http_client = http_client - self.schema_client = schema_client - - @utils.memoized_property - def model(self): - schema = self.schema_client.get('image') - return warlock.model_factory(schema.raw(), schemas.SchemaBasedModel) - - @staticmethod - def _wrap(value): - if isinstance(value, six.string_types): - return [value] - return value - - @staticmethod - def _validate_sort_param(sort): - """Validates sorting argument for invalid keys and directions values. - - :param sort: comma-separated list of sort keys with optional <:dir> - after each key - """ - for sort_param in sort.strip().split(','): - key, _sep, dir = sort_param.partition(':') - if dir and dir not in SORT_DIR_VALUES: - msg = ('Invalid sort direction: %(sort_dir)s.' - ' It must be one of the following: %(available)s.' - ) % {'sort_dir': dir, - 'available': ', '.join(SORT_DIR_VALUES)} - raise exc.HTTPBadRequest(msg) - if key not in SORT_KEY_VALUES: - msg = ('Invalid sort key: %(sort_key)s.' - ' It must be one of the following: %(available)s.' - ) % {'sort_key': key, - 'available': ', '.join(SORT_KEY_VALUES)} - raise exc.HTTPBadRequest(msg) - return sort - - def list(self, **kwargs): - """Retrieve a listing of Image objects - - :param page_size: Number of images to request in each paginated request - :returns generator over list of Images - """ - - ori_validate_fun = self.model.validate - empty_fun = lambda *args, **kwargs: None - - limit = kwargs.get('limit') - # NOTE(flaper87): Don't use `get('page_size', DEFAULT_SIZE)` otherwise, - # it could be possible to send invalid data to the server by passing - # page_size=None. - page_size = kwargs.get('page_size') or DEFAULT_PAGE_SIZE - - def paginate(url, page_size, limit=None): - next_url = url - - while True: - if limit and page_size > limit: - # NOTE(flaper87): Avoid requesting 2000 images when limit - # is 1 - next_url = next_url.replace("limit=%s" % page_size, - "limit=%s" % limit) - - resp, body = self.http_client.get(next_url) - for image in body['images']: - # NOTE(bcwaldon): remove 'self' for now until we have - # an elegant way to pass it into the model constructor - # without conflict. - image.pop('self', None) - yield self.model(**image) - # NOTE(zhiyan): In order to resolve the performance issue - # of JSON schema validation for image listing case, we - # don't validate each image entry but do it only on first - # image entry for each page. - self.model.validate = empty_fun - - if limit: - limit -= 1 - if limit <= 0: - raise StopIteration - - # NOTE(zhiyan); Reset validation function. - self.model.validate = ori_validate_fun - - try: - next_url = body['next'] - except KeyError: - return - - filters = kwargs.get('filters', {}) - # NOTE(flaper87): We paginate in the client, hence we use - # the page_size as Glance's limit. - filters['limit'] = page_size - - tags = filters.pop('tag', []) - tags_url_params = [] - - for tag in tags: - if isinstance(tag, six.string_types): - tags_url_params.append({'tag': encodeutils.safe_encode(tag)}) - - for param, value in six.iteritems(filters): - if isinstance(value, six.string_types): - filters[param] = encodeutils.safe_encode(value) - - url = '/v2/images?%s' % parse.urlencode(filters) - - for param in tags_url_params: - url = '%s&%s' % (url, parse.urlencode(param)) - - if 'sort' in kwargs: - if 'sort_key' in kwargs or 'sort_dir' in kwargs: - raise exc.HTTPBadRequest("The 'sort' argument is not supported" - " with 'sort_key' or 'sort_dir'.") - url = '%s&sort=%s' % (url, - self._validate_sort_param( - kwargs['sort'])) - else: - sort_dir = self._wrap(kwargs.get('sort_dir', [])) - sort_key = self._wrap(kwargs.get('sort_key', [])) - - if len(sort_key) != len(sort_dir) and len(sort_dir) > 1: - raise exc.HTTPBadRequest( - "Unexpected number of sort directions: " - "either provide a single sort direction or an equal " - "number of sort keys and sort directions.") - for key in sort_key: - url = '%s&sort_key=%s' % (url, key) - - for dir in sort_dir: - url = '%s&sort_dir=%s' % (url, dir) - - for image in paginate(url, page_size, limit): - yield image - - def get(self, image_id): - url = '/v2/images/%s' % image_id - resp, body = self.http_client.get(url) - # NOTE(bcwaldon): remove 'self' for now until we have an elegant - # way to pass it into the model constructor without conflict - body.pop('self', None) - return self.model(**body) - - def data(self, image_id, do_checksum=True): - """ - Retrieve data of an image. - - :param image_id: ID of the image to download. - :param do_checksum: Enable/disable checksum validation. - """ - url = '/v2/images/%s/file' % image_id - resp, body = self.http_client.get(url) - checksum = resp.headers.get('content-md5', None) - content_length = int(resp.headers.get('content-length', 0)) - - if do_checksum and checksum is not None: - body = utils.integrity_iter(body, checksum) - - return utils.IterableWithLength(body, content_length) - - def upload(self, image_id, image_data, image_size=None): - """ - Upload the data for an image. - - :param image_id: ID of the image to upload data for. - :param image_data: File-like object supplying the data to upload. - :param image_size: Total size in bytes of image to be uploaded. - """ - url = '/v2/images/%s/file' % image_id - hdrs = {'Content-Type': 'application/octet-stream'} - if image_size: - body = {'image_data': image_data, - 'image_size': image_size} - else: - body = image_data - self.http_client.put(url, headers=hdrs, data=body) - - def delete(self, image_id): - """Delete an image.""" - url = '/v2/images/%s' % image_id - self.http_client.delete(url) - - def create(self, **kwargs): - """Create an image.""" - url = '/v2/images' - - image = self.model() - for (key, value) in kwargs.items(): - try: - setattr(image, key, value) - except warlock.InvalidOperation as e: - raise TypeError(utils.exception_to_str(e)) - - resp, body = self.http_client.post(url, data=image) - # NOTE(esheffield): remove 'self' for now until we have an elegant - # way to pass it into the model constructor without conflict - body.pop('self', None) - return self.model(**body) - - def update(self, image_id, remove_props=None, **kwargs): - """ - Update attributes of an image. - - :param image_id: ID of the image to modify. - :param remove_props: List of property names to remove - :param **kwargs: Image attribute names and their new values. - """ - image = self.get(image_id) - for (key, value) in kwargs.items(): - try: - setattr(image, key, value) - except warlock.InvalidOperation as e: - raise TypeError(utils.exception_to_str(e)) - - if remove_props is not None: - cur_props = image.keys() - new_props = kwargs.keys() - # NOTE(esheffield): Only remove props that currently exist on the - # image and are NOT in the properties being updated / added - props_to_remove = set(cur_props).intersection( - set(remove_props).difference(new_props)) - - for key in props_to_remove: - delattr(image, key) - - url = '/v2/images/%s' % image_id - hdrs = {'Content-Type': 'application/openstack-images-v2.1-json-patch'} - self.http_client.patch(url, headers=hdrs, data=image.patch) - - # NOTE(bcwaldon): calling image.patch doesn't clear the changes, so - # we need to fetch the image again to get a clean history. This is - # an obvious optimization for warlock - return self.get(image_id) - - def _get_image_with_locations_or_fail(self, image_id): - image = self.get(image_id) - if getattr(image, 'locations', None) is None: - raise exc.HTTPBadRequest('The administrator has disabled ' - 'API access to image locations') - return image - - def _send_image_update_request(self, image_id, patch_body): - url = '/v2/images/%s' % image_id - hdrs = {'Content-Type': 'application/openstack-images-v2.1-json-patch'} - self.http_client.patch(url, headers=hdrs, data=json.dumps(patch_body)) - - def add_location(self, image_id, url, metadata): - """Add a new location entry to an image's list of locations. - - It is an error to add a URL that is already present in the list of - locations. - - :param image_id: ID of image to which the location is to be added. - :param url: URL of the location to add. - :param metadata: Metadata associated with the location. - :returns: The updated image - """ - image = self._get_image_with_locations_or_fail(image_id) - url_list = [l['url'] for l in image.locations] - if url in url_list: - err_str = 'A location entry at %s already exists' % url - raise exc.HTTPConflict(err_str) - - add_patch = [{'op': 'add', 'path': '/locations/-', - 'value': {'url': url, 'metadata': metadata}}] - self._send_image_update_request(image_id, add_patch) - return self.get(image_id) - - def delete_locations(self, image_id, url_set): - """Remove one or more location entries of an image. - - :param image_id: ID of image from which locations are to be removed. - :param url_set: set of URLs of location entries to remove. - :returns: None - """ - image = self._get_image_with_locations_or_fail(image_id) - current_urls = [l['url'] for l in image.locations] - - missing_locs = url_set.difference(set(current_urls)) - if missing_locs: - raise exc.HTTPNotFound('Unknown URL(s): %s' % list(missing_locs)) - - # NOTE: warlock doesn't generate the most efficient patch for remove - # operations (it shifts everything up and deletes the tail elements) so - # we do it ourselves. - url_indices = [current_urls.index(url) for url in url_set] - url_indices.sort(reverse=True) - patches = [{'op': 'remove', 'path': '/locations/%s' % url_idx} - for url_idx in url_indices] - self._send_image_update_request(image_id, patches) - - def update_location(self, image_id, url, metadata): - """Update an existing location entry in an image's list of locations. - - The URL specified must be already present in the image's list of - locations. - - :param image_id: ID of image whose location is to be updated. - :param url: URL of the location to update. - :param metadata: Metadata associated with the location. - :returns: The updated image - """ - image = self._get_image_with_locations_or_fail(image_id) - url_map = dict([(l['url'], l) for l in image.locations]) - if url not in url_map: - raise exc.HTTPNotFound('Unknown URL: %s' % url) - - if url_map[url]['metadata'] == metadata: - return image - - # NOTE: The server (as of now) doesn't support modifying individual - # location entries. So we must: - # 1. Empty existing list of locations. - # 2. Send another request to set 'locations' to the new list - # of locations. - url_map[url]['metadata'] = metadata - patches = [{'op': 'replace', - 'path': '/locations', - 'value': p} for p in ([], list(url_map.values()))] - self._send_image_update_request(image_id, patches) - - return self.get(image_id) diff --git a/code/daisyclient/daisyclient/v2/metadefs.py b/code/daisyclient/daisyclient/v2/metadefs.py deleted file mode 100755 index a0bc9ce6..00000000 --- a/code/daisyclient/daisyclient/v2/metadefs.py +++ /dev/null @@ -1,387 +0,0 @@ -# Copyright 2014 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_utils import encodeutils -import six -from six.moves.urllib import parse -import warlock - -from daisyclient.common import utils -from daisyclient.v2 import schemas - -DEFAULT_PAGE_SIZE = 20 -SORT_DIR_VALUES = ('asc', 'desc') -SORT_KEY_VALUES = ('created_at', 'namespace') - - -class NamespaceController(object): - def __init__(self, http_client, schema_client): - self.http_client = http_client - self.schema_client = schema_client - - @utils.memoized_property - def model(self): - schema = self.schema_client.get('metadefs/namespace') - return warlock.model_factory(schema.raw(), schemas.SchemaBasedModel) - - def create(self, **kwargs): - """Create a namespace. - - :param kwargs: Unpacked namespace object. - """ - url = '/v2/metadefs/namespaces' - try: - namespace = self.model(kwargs) - except (warlock.InvalidOperation, ValueError) as e: - raise TypeError(utils.exception_to_str(e)) - - resp, body = self.http_client.post(url, data=namespace) - body.pop('self', None) - return self.model(**body) - - def update(self, namespace_name, **kwargs): - """Update a namespace. - - :param namespace_name: Name of a namespace (old one). - :param kwargs: Unpacked namespace object. - """ - namespace = self.get(namespace_name) - for (key, value) in six.iteritems(kwargs): - try: - setattr(namespace, key, value) - except warlock.InvalidOperation as e: - raise TypeError(utils.exception_to_str(e)) - - # Remove read-only parameters. - read_only = ['schema', 'updated_at', 'created_at'] - for elem in read_only: - if elem in namespace: - del namespace[elem] - - url = '/v2/metadefs/namespaces/{0}'.format(namespace_name) - self.http_client.put(url, data=namespace) - - return self.get(namespace.namespace) - - def get(self, namespace, **kwargs): - """Get one namespace.""" - query_params = parse.urlencode(kwargs) - if kwargs: - query_params = '?%s' % query_params - - url = '/v2/metadefs/namespaces/{0}{1}'.format(namespace, query_params) - resp, body = self.http_client.get(url) - # NOTE(bcwaldon): remove 'self' for now until we have an elegant - # way to pass it into the model constructor without conflict - body.pop('self', None) - return self.model(**body) - - def list(self, **kwargs): - """Retrieve a listing of Namespace objects - :param page_size: Number of items to request in each paginated request - :param limit: Use to request a specific page size. Expect a response - to a limited request to return between zero and limit - items. - :param marker: Specifies the namespace of the last-seen namespace. - The typical pattern of limit and marker is to make an - initial limited request and then to use the last - namespace from the response as the marker parameter - in a subsequent limited request. - :param sort_key: The field to sort on (for example, 'created_at') - :param sort_dir: The direction to sort ('asc' or 'desc') - :returns generator over list of Namespaces - """ - - ori_validate_fun = self.model.validate - empty_fun = lambda *args, **kwargs: None - - def paginate(url): - resp, body = self.http_client.get(url) - for namespace in body['namespaces']: - # NOTE(bcwaldon): remove 'self' for now until we have - # an elegant way to pass it into the model constructor - # without conflict. - namespace.pop('self', None) - yield self.model(**namespace) - # NOTE(zhiyan): In order to resolve the performance issue - # of JSON schema validation for image listing case, we - # don't validate each image entry but do it only on first - # image entry for each page. - self.model.validate = empty_fun - - # NOTE(zhiyan); Reset validation function. - self.model.validate = ori_validate_fun - - try: - next_url = body['next'] - except KeyError: - return - else: - for namespace in paginate(next_url): - yield namespace - - filters = kwargs.get('filters', {}) - filters = {} if filters is None else filters - - if not kwargs.get('page_size'): - filters['limit'] = DEFAULT_PAGE_SIZE - else: - filters['limit'] = kwargs['page_size'] - - if 'marker' in kwargs: - filters['marker'] = kwargs['marker'] - - sort_key = kwargs.get('sort_key') - if sort_key is not None: - if sort_key in SORT_KEY_VALUES: - filters['sort_key'] = sort_key - else: - raise ValueError('sort_key must be one of the following: %s.' - % ', '.join(SORT_KEY_VALUES)) - - sort_dir = kwargs.get('sort_dir') - if sort_dir is not None: - if sort_dir in SORT_DIR_VALUES: - filters['sort_dir'] = sort_dir - else: - raise ValueError('sort_dir must be one of the following: %s.' - % ', '.join(SORT_DIR_VALUES)) - - for param, value in six.iteritems(filters): - if isinstance(value, list): - filters[param] = encodeutils.safe_encode(','.join(value)) - elif isinstance(value, six.string_types): - filters[param] = encodeutils.safe_encode(value) - - url = '/v2/metadefs/namespaces?%s' % parse.urlencode(filters) - - for namespace in paginate(url): - yield namespace - - def delete(self, namespace): - """Delete a namespace.""" - url = '/v2/metadefs/namespaces/{0}'.format(namespace) - self.http_client.delete(url) - - -class ResourceTypeController(object): - def __init__(self, http_client, schema_client): - self.http_client = http_client - self.schema_client = schema_client - - @utils.memoized_property - def model(self): - schema = self.schema_client.get('metadefs/resource_type') - return warlock.model_factory(schema.raw(), schemas.SchemaBasedModel) - - def associate(self, namespace, **kwargs): - """Associate a resource type with a namespace.""" - try: - res_type = self.model(kwargs) - except (warlock.InvalidOperation, ValueError) as e: - raise TypeError(utils.exception_to_str(e)) - - url = '/v2/metadefs/namespaces/{0}/resource_types'.format(namespace, - res_type) - resp, body = self.http_client.post(url, data=res_type) - body.pop('self', None) - return self.model(**body) - - def deassociate(self, namespace, resource): - """Deasociate a resource type with a namespace.""" - url = '/v2/metadefs/namespaces/{0}/resource_types/{1}'. \ - format(namespace, resource) - self.http_client.delete(url) - - def list(self): - """Retrieve a listing of available resource types - - :returns generator over list of resource_types - """ - - url = '/v2/metadefs/resource_types' - resp, body = self.http_client.get(url) - for resource_type in body['resource_types']: - yield self.model(**resource_type) - - def get(self, namespace): - url = '/v2/metadefs/namespaces/{0}/resource_types'.format(namespace) - resp, body = self.http_client.get(url) - body.pop('self', None) - for resource_type in body['resource_type_associations']: - yield self.model(**resource_type) - - -class PropertyController(object): - def __init__(self, http_client, schema_client): - self.http_client = http_client - self.schema_client = schema_client - - @utils.memoized_property - def model(self): - schema = self.schema_client.get('metadefs/property') - return warlock.model_factory(schema.raw(), schemas.SchemaBasedModel) - - def create(self, namespace, **kwargs): - """Create a property. - - :param namespace: Name of a namespace the property will belong. - :param kwargs: Unpacked property object. - """ - try: - prop = self.model(kwargs) - except (warlock.InvalidOperation, ValueError) as e: - raise TypeError(utils.exception_to_str(e)) - - url = '/v2/metadefs/namespaces/{0}/properties'.format(namespace) - - resp, body = self.http_client.post(url, data=prop) - body.pop('self', None) - return self.model(**body) - - def update(self, namespace, prop_name, **kwargs): - """Update a property. - - :param namespace: Name of a namespace the property belongs. - :param prop_name: Name of a property (old one). - :param kwargs: Unpacked property object. - """ - prop = self.get(namespace, prop_name) - for (key, value) in kwargs.items(): - try: - setattr(prop, key, value) - except warlock.InvalidOperation as e: - raise TypeError(utils.exception_to_str(e)) - - url = '/v2/metadefs/namespaces/{0}/properties/{1}'.format(namespace, - prop_name) - self.http_client.put(url, data=prop) - - return self.get(namespace, prop.name) - - def get(self, namespace, prop_name): - url = '/v2/metadefs/namespaces/{0}/properties/{1}'.format(namespace, - prop_name) - resp, body = self.http_client.get(url) - body.pop('self', None) - body['name'] = prop_name - return self.model(**body) - - def list(self, namespace, **kwargs): - """Retrieve a listing of metadata properties - - :returns generator over list of objects - """ - url = '/v2/metadefs/namespaces/{0}/properties'.format(namespace) - - resp, body = self.http_client.get(url) - - for key, value in body['properties'].items(): - value['name'] = key - yield self.model(value) - - def delete(self, namespace, prop_name): - """Delete a property.""" - url = '/v2/metadefs/namespaces/{0}/properties/{1}'.format(namespace, - prop_name) - self.http_client.delete(url) - - def delete_all(self, namespace): - """Delete all properties in a namespace.""" - url = '/v2/metadefs/namespaces/{0}/properties'.format(namespace) - self.http_client.delete(url) - - -class ObjectController(object): - def __init__(self, http_client, schema_client): - self.http_client = http_client - self.schema_client = schema_client - - @utils.memoized_property - def model(self): - schema = self.schema_client.get('metadefs/object') - return warlock.model_factory(schema.raw(), schemas.SchemaBasedModel) - - def create(self, namespace, **kwargs): - """Create an object. - - :param namespace: Name of a namespace the object belongs. - :param kwargs: Unpacked object. - """ - try: - obj = self.model(kwargs) - except (warlock.InvalidOperation, ValueError) as e: - raise TypeError(utils.exception_to_str(e)) - - url = '/v2/metadefs/namespaces/{0}/objects'.format(namespace) - - resp, body = self.http_client.post(url, data=obj) - body.pop('self', None) - return self.model(**body) - - def update(self, namespace, object_name, **kwargs): - """Update an object. - - :param namespace: Name of a namespace the object belongs. - :param prop_name: Name of an object (old one). - :param kwargs: Unpacked object. - """ - obj = self.get(namespace, object_name) - for (key, value) in kwargs.items(): - try: - setattr(obj, key, value) - except warlock.InvalidOperation as e: - raise TypeError(utils.exception_to_str(e)) - - # Remove read-only parameters. - read_only = ['schema', 'updated_at', 'created_at'] - for elem in read_only: - if elem in namespace: - del namespace[elem] - - url = '/v2/metadefs/namespaces/{0}/objects/{1}'.format(namespace, - object_name) - self.http_client.put(url, data=obj) - - return self.get(namespace, obj.name) - - def get(self, namespace, object_name): - url = '/v2/metadefs/namespaces/{0}/objects/{1}'.format(namespace, - object_name) - resp, body = self.http_client.get(url) - body.pop('self', None) - return self.model(**body) - - def list(self, namespace, **kwargs): - """Retrieve a listing of metadata objects - - :returns generator over list of objects - """ - url = '/v2/metadefs/namespaces/{0}/objects'.format(namespace,) - resp, body = self.http_client.get(url) - - for obj in body['objects']: - yield self.model(obj) - - def delete(self, namespace, object_name): - """Delete an object.""" - url = '/v2/metadefs/namespaces/{0}/objects/{1}'.format(namespace, - object_name) - self.http_client.delete(url) - - def delete_all(self, namespace): - """Delete all objects in a namespace.""" - url = '/v2/metadefs/namespaces/{0}/objects'.format(namespace) - self.http_client.delete(url) diff --git a/code/daisyclient/daisyclient/v2/schemas.py b/code/daisyclient/daisyclient/v2/schemas.py deleted file mode 100755 index 77f49c41..00000000 --- a/code/daisyclient/daisyclient/v2/schemas.py +++ /dev/null @@ -1,105 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import copy -import json -import jsonpatch -import six -import warlock.model as warlock - - -class SchemaBasedModel(warlock.Model): - """Glance specific subclass of the warlock Model - - This implementation alters the function of the patch property - to take into account the schema's core properties. With this version - undefined properties which are core will generated 'replace' - operations rather than 'add' since this is what the Glance API - expects. - """ - - def _make_custom_patch(self, new, original): - if not self.get('tags'): - tags_patch = [] - else: - tags_patch = [{"path": "/tags", - "value": self.get('tags'), - "op": "replace"}] - - patch_string = jsonpatch.make_patch(original, new).to_string() - patch = json.loads(patch_string) - if not patch: - return json.dumps(tags_patch) - else: - return json.dumps(patch + tags_patch) - - @warlock.Model.patch.getter - def patch(self): - """Return a jsonpatch object representing the delta.""" - original = copy.deepcopy(self.__dict__['__original__']) - new = dict(self) - if self.schema: - for (name, prop) in six.iteritems(self.schema['properties']): - if (name not in original and name in new and - prop.get('is_base', True)): - original[name] = None - - original['tags'] = None - new['tags'] = None - return self._make_custom_patch(new, original) - - -class SchemaProperty(object): - def __init__(self, name, **kwargs): - self.name = name - self.description = kwargs.get('description') - - -def translate_schema_properties(schema_properties): - """Parse the properties dictionary of a schema document - - :returns list of SchemaProperty objects - """ - properties = [] - for (name, prop) in schema_properties.items(): - properties.append(SchemaProperty(name, **prop)) - return properties - - -class Schema(object): - def __init__(self, raw_schema): - self._raw_schema = raw_schema - self.name = raw_schema['name'] - raw_properties = raw_schema['properties'] - self.properties = translate_schema_properties(raw_properties) - - def is_core_property(self, property_name): - for prop in self.properties: - if property_name == prop.name: - return True - return False - - def raw(self): - return copy.deepcopy(self._raw_schema) - - -class Controller(object): - def __init__(self, http_client): - self.http_client = http_client - - def get(self, schema_name): - uri = '/v2/schemas/%s' % schema_name - _, raw_schema = self.http_client.get(uri) - return Schema(raw_schema) diff --git a/code/daisyclient/daisyclient/v2/shell.py b/code/daisyclient/daisyclient/v2/shell.py deleted file mode 100755 index 583db784..00000000 --- a/code/daisyclient/daisyclient/v2/shell.py +++ /dev/null @@ -1,822 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from daisyclient.common import progressbar -from daisyclient.common import utils -from daisyclient import exc -from daisyclient.v2.image_members import MEMBER_STATUS_VALUES -from daisyclient.v2 import images -from daisyclient.v2 import tasks -import json -import os -from os.path import expanduser - -IMAGE_SCHEMA = None - - -def get_image_schema(): - global IMAGE_SCHEMA - if IMAGE_SCHEMA is None: - schema_path = expanduser("~/.glanceclient/image_schema.json") - if os.path.isfile(schema_path): - with open(schema_path, "r") as f: - schema_raw = f.read() - IMAGE_SCHEMA = json.loads(schema_raw) - return IMAGE_SCHEMA - - -@utils.schema_args(get_image_schema, omit=['created_at', 'updated_at', 'file', - 'checksum', 'virtual_size', 'size', - 'status', 'schema', 'direct_url']) -@utils.arg('--property', metavar="", action='append', - default=[], help=('Arbitrary property to associate with image.' - ' May be used multiple times.')) -@utils.arg('--file', metavar='', - help='Local file that contains disk image to be uploaded ' - 'during creation. Alternatively, images can be passed ' - 'to the client via stdin.') -@utils.arg('--progress', action='store_true', default=False, - help='Show upload progress bar.') -def do_image_create(gc, args): - """Create a new image.""" - schema = gc.schemas.get("image") - _args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()] - fields = dict(filter(lambda x: x[1] is not None and - (x[0] == 'property' or - schema.is_core_property(x[0])), - _args)) - - raw_properties = fields.pop('property', []) - for datum in raw_properties: - key, value = datum.split('=', 1) - fields[key] = value - - file_name = fields.pop('file', None) - if file_name is not None and os.access(file_name, os.R_OK) is False: - utils.exit("File %s does not exist or user does not have read " - "privileges to it" % file_name) - image = gc.images.create(**fields) - try: - if utils.get_data_file(args) is not None: - args.id = image['id'] - args.size = None - do_image_upload(gc, args) - image = gc.images.get(args.id) - finally: - utils.print_image(image) - - -@utils.arg('id', metavar='', help='ID of image to update.') -@utils.schema_args(get_image_schema, omit=['id', 'locations', 'created_at', - 'updated_at', 'file', 'checksum', - 'virtual_size', 'size', 'status', - 'schema', 'direct_url', 'tags']) -@utils.arg('--property', metavar="", action='append', - default=[], help=('Arbitrary property to associate with image.' - ' May be used multiple times.')) -@utils.arg('--remove-property', metavar="key", action='append', default=[], - help="Name of arbitrary property to remove from the image.") -def do_image_update(gc, args): - """Update an existing image.""" - schema = gc.schemas.get("image") - _args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()] - fields = dict(filter(lambda x: x[1] is not None and - (x[0] in ['property', 'remove_property'] or - schema.is_core_property(x[0])), - _args)) - - raw_properties = fields.pop('property', []) - for datum in raw_properties: - key, value = datum.split('=', 1) - fields[key] = value - - remove_properties = fields.pop('remove_property', None) - - image_id = fields.pop('id') - image = gc.images.update(image_id, remove_properties, **fields) - utils.print_image(image) - - -@utils.arg('--limit', metavar='', default=None, type=int, - help='Maximum number of images to get.') -@utils.arg('--page-size', metavar='', default=None, type=int, - help='Number of images to request in each paginated request.') -@utils.arg('--visibility', metavar='', - help='The visibility of the images to display.') -@utils.arg('--member-status', metavar='', - help='The status of images to display.') -@utils.arg('--owner', metavar='', - help='Display images owned by .') -@utils.arg('--property-filter', metavar='', - help="Filter images by a user-defined image property.", - action='append', dest='properties', default=[]) -@utils.arg('--checksum', metavar='', - help='Displays images that match the checksum.') -@utils.arg('--tag', metavar='', action='append', - help="Filter images by a user-defined tag.") -@utils.arg('--sort-key', default=[], action='append', - choices=images.SORT_KEY_VALUES, - help='Sort image list by specified fields.') -@utils.arg('--sort-dir', default=[], action='append', - choices=images.SORT_DIR_VALUES, - help='Sort image list in specified directions.') -@utils.arg('--sort', metavar='[:]', default=None, - help=(("Comma-separated list of sort keys and directions in the " - "form of [:]. Valid keys: %s. OPTIONAL: " - "Default='name:asc'.") % ', '.join(images.SORT_KEY_VALUES))) -def do_image_list(gc, args): - """List images you can access.""" - filter_keys = ['visibility', 'member_status', 'owner', 'checksum', 'tag'] - filter_items = [(key, getattr(args, key)) for key in filter_keys] - if args.properties: - filter_properties = [prop.split('=', 1) for prop in args.properties] - if False in (len(pair) == 2 for pair in filter_properties): - utils.exit('Argument --property-filter expected properties in the' - ' format KEY=VALUE') - filter_items += filter_properties - filters = dict([item for item in filter_items if item[1] is not None]) - - kwargs = {'filters': filters} - if args.limit is not None: - kwargs['limit'] = args.limit - if args.page_size is not None: - kwargs['page_size'] = args.page_size - - if args.sort_key: - kwargs['sort_key'] = args.sort_key - if args.sort_dir: - kwargs['sort_dir'] = args.sort_dir - if args.sort is not None: - kwargs['sort'] = args.sort - elif not args.sort_dir and not args.sort_key: - kwargs['sort'] = 'name:asc' - - images = gc.images.list(**kwargs) - columns = ['ID', 'Name'] - utils.print_list(images, columns) - - -@utils.arg('id', metavar='', help='ID of image to describe.') -@utils.arg('--max-column-width', metavar='', default=80, - help='The max column width of the printed table.') -def do_image_show(gc, args): - """Describe a specific image.""" - image = gc.images.get(args.id) - utils.print_image(image, int(args.max_column_width)) - - -@utils.arg('--image-id', metavar='', required=True, - help='Image to display members of.') -def do_member_list(gc, args): - """Describe sharing permissions by image.""" - - members = gc.image_members.list(args.image_id) - columns = ['Image ID', 'Member ID', 'Status'] - utils.print_list(members, columns) - - -@utils.arg('image_id', metavar='', - help='Image from which to remove member.') -@utils.arg('member_id', metavar='', - help='Tenant to remove as member.') -def do_member_delete(gc, args): - """Delete image member.""" - if not (args.image_id and args.member_id): - utils.exit('Unable to delete member. Specify image_id and member_id') - else: - gc.image_members.delete(args.image_id, args.member_id) - - -@utils.arg('image_id', metavar='', - help='Image from which to update member.') -@utils.arg('member_id', metavar='', - help='Tenant to update.') -@utils.arg('member_status', metavar='', - choices=MEMBER_STATUS_VALUES, - help='Updated status of member.' - ' Valid Values: %s' % - ', '.join(str(val) for val in MEMBER_STATUS_VALUES)) -def do_member_update(gc, args): - """Update the status of a member for a given image.""" - if not (args.image_id and args.member_id and args.member_status): - utils.exit('Unable to update member. Specify image_id, member_id and' - ' member_status') - else: - member = gc.image_members.update(args.image_id, args.member_id, - args.member_status) - member = [member] - columns = ['Image ID', 'Member ID', 'Status'] - utils.print_list(member, columns) - - -@utils.arg('image_id', metavar='', - help='Image with which to create member.') -@utils.arg('member_id', metavar='', - help='Tenant to add as member.') -def do_member_create(gc, args): - """Create member for a given image.""" - if not (args.image_id and args.member_id): - utils.exit('Unable to create member. Specify image_id and member_id') - else: - member = gc.image_members.create(args.image_id, args.member_id) - member = [member] - columns = ['Image ID', 'Member ID', 'Status'] - utils.print_list(member, columns) - - -@utils.arg('model', metavar='', help='Name of model to describe.') -def do_explain(gc, args): - """Describe a specific model.""" - try: - schema = gc.schemas.get(args.model) - except exc.HTTPNotFound: - utils.exit('Unable to find requested model \'%s\'' % args.model) - else: - formatters = {'Attribute': lambda m: m.name} - columns = ['Attribute', 'Description'] - utils.print_list(schema.properties, columns, formatters) - - -@utils.arg('--file', metavar='', - help='Local file to save downloaded image data to. ' - 'If this is not specified the image data will be ' - 'written to stdout.') -@utils.arg('id', metavar='', help='ID of image to download.') -@utils.arg('--progress', action='store_true', default=False, - help='Show download progress bar.') -def do_image_download(gc, args): - """Download a specific image.""" - body = gc.images.data(args.id) - if args.progress: - body = progressbar.VerboseIteratorWrapper(body, len(body)) - utils.save_image(body, args.file) - - -@utils.arg('--file', metavar='', - help=('Local file that contains disk image to be uploaded.' - ' Alternatively, images can be passed' - ' to the client via stdin.')) -@utils.arg('--size', metavar='', type=int, - help='Size in bytes of image to be uploaded. Default is to get ' - 'size from provided data object but this is supported in case ' - 'where size cannot be inferred.', - default=None) -@utils.arg('--progress', action='store_true', default=False, - help='Show upload progress bar.') -@utils.arg('id', metavar='', - help='ID of image to upload data to.') -def do_image_upload(gc, args): - """Upload data for a specific image.""" - image_data = utils.get_data_file(args) - if args.progress: - filesize = utils.get_file_size(image_data) - if filesize is not None: - # NOTE(kragniz): do not show a progress bar if the size of the - # input is unknown (most likely a piped input) - image_data = progressbar.VerboseFileWrapper(image_data, filesize) - gc.images.upload(args.id, image_data, args.size) - - -@utils.arg('id', metavar='', help='ID of image to delete.') -def do_image_delete(gc, args): - """Delete specified image.""" - image = gc.images.get(args.id) - if image and image.status == "deleted": - msg = "No image with an ID of '%s' exists." % image.id - utils.exit(msg) - gc.images.delete(args.id) - - -@utils.arg('image_id', metavar='', - help='Image to be updated with the given tag.') -@utils.arg('tag_value', metavar='', - help='Value of the tag.') -def do_image_tag_update(gc, args): - """Update an image with the given tag.""" - if not (args.image_id and args.tag_value): - utils.exit('Unable to update tag. Specify image_id and tag_value') - else: - gc.image_tags.update(args.image_id, args.tag_value) - image = gc.images.get(args.image_id) - image = [image] - columns = ['ID', 'Tags'] - utils.print_list(image, columns) - - -@utils.arg('image_id', metavar='', - help='ID of the image from which to delete tag.') -@utils.arg('tag_value', metavar='', - help='Value of the tag.') -def do_image_tag_delete(gc, args): - """Delete the tag associated with the given image.""" - if not (args.image_id and args.tag_value): - utils.exit('Unable to delete tag. Specify image_id and tag_value') - else: - gc.image_tags.delete(args.image_id, args.tag_value) - - -@utils.arg('--url', metavar='', required=True, - help='URL of location to add.') -@utils.arg('--metadata', metavar='', default='{}', - help=('Metadata associated with the location. ' - 'Must be a valid JSON object (default: %(default)s)')) -@utils.arg('id', metavar='', - help='ID of image to which the location is to be added.') -def do_location_add(gc, args): - """Add a location (and related metadata) to an image.""" - try: - metadata = json.loads(args.metadata) - except ValueError: - utils.exit('Metadata is not a valid JSON object.') - else: - image = gc.images.add_location(args.id, args.url, metadata) - utils.print_dict(image) - - -@utils.arg('--url', metavar='', action='append', required=True, - help='URL of location to remove. May be used multiple times.') -@utils.arg('id', metavar='', - help='ID of image whose locations are to be removed.') -def do_location_delete(gc, args): - """Remove locations (and related metadata) from an image.""" - gc.images.delete_locations(args.id, set(args.url)) - - -@utils.arg('--url', metavar='', required=True, - help='URL of location to update.') -@utils.arg('--metadata', metavar='', default='{}', - help=('Metadata associated with the location. ' - 'Must be a valid JSON object (default: %(default)s)')) -@utils.arg('id', metavar='', - help='ID of image whose location is to be updated.') -def do_location_update(gc, args): - """Update metadata of an image's location.""" - try: - metadata = json.loads(args.metadata) - except ValueError: - utils.exit('Metadata is not a valid JSON object.') - else: - image = gc.images.update_location(args.id, args.url, metadata) - utils.print_dict(image) - - -# Metadata - catalog -NAMESPACE_SCHEMA = None - - -def get_namespace_schema(): - global NAMESPACE_SCHEMA - if NAMESPACE_SCHEMA is None: - schema_path = expanduser("~/.glanceclient/namespace_schema.json") - if os.path.isfile(schema_path): - with open(schema_path, "r") as f: - schema_raw = f.read() - NAMESPACE_SCHEMA = json.loads(schema_raw) - return NAMESPACE_SCHEMA - - -def _namespace_show(namespace, max_column_width=None): - namespace = dict(namespace) # Warlock objects are compatible with dicts - # Flatten dicts for display - if 'properties' in namespace: - props = [k for k in namespace['properties']] - namespace['properties'] = props - if 'resource_type_associations' in namespace: - assocs = [assoc['name'] - for assoc in namespace['resource_type_associations']] - namespace['resource_type_associations'] = assocs - if 'objects' in namespace: - objects = [obj['name'] for obj in namespace['objects']] - namespace['objects'] = objects - - if max_column_width: - utils.print_dict(namespace, max_column_width) - else: - utils.print_dict(namespace) - - -@utils.arg('namespace', metavar='', help='Name of the namespace.') -@utils.schema_args(get_namespace_schema, omit=['namespace', 'property_count', - 'properties', 'tag_count', - 'tags', 'object_count', - 'objects', 'resource_types']) -def do_md_namespace_create(gc, args): - """Create a new metadata definitions namespace.""" - schema = gc.schemas.get('metadefs/namespace') - _args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()] - fields = dict(filter(lambda x: x[1] is not None and - (schema.is_core_property(x[0])), - _args)) - namespace = gc.metadefs_namespace.create(**fields) - - _namespace_show(namespace) - - -@utils.arg('--file', metavar='', - help='Path to file with namespace schema to import. Alternatively, ' - 'namespaces schema can be passed to the client via stdin.') -def do_md_namespace_import(gc, args): - """Import a metadata definitions namespace from file or standard input.""" - namespace_data = utils.get_data_file(args) - if not namespace_data: - utils.exit('No metadata definition namespace passed via stdin or ' - '--file argument.') - - try: - namespace_json = json.load(namespace_data) - except ValueError: - utils.exit('Schema is not a valid JSON object.') - else: - namespace = gc.metadefs_namespace.create(**namespace_json) - _namespace_show(namespace) - - -@utils.arg('id', metavar='', help='Name of namespace to update.') -@utils.schema_args(get_namespace_schema, omit=['property_count', 'properties', - 'tag_count', 'tags', - 'object_count', 'objects', - 'resource_type_associations', - 'schema']) -def do_md_namespace_update(gc, args): - """Update an existing metadata definitions namespace.""" - schema = gc.schemas.get('metadefs/namespace') - - _args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()] - fields = dict(filter(lambda x: x[1] is not None and - (schema.is_core_property(x[0])), - _args)) - namespace = gc.metadefs_namespace.update(args.id, **fields) - - _namespace_show(namespace) - - -@utils.arg('namespace', metavar='', - help='Name of namespace to describe.') -@utils.arg('--resource-type', metavar='', - help='Applies prefix of given resource type associated to a ' - 'namespace to all properties of a namespace.', default=None) -@utils.arg('--max-column-width', metavar='', default=80, - help='The max column width of the printed table.') -def do_md_namespace_show(gc, args): - """Describe a specific metadata definitions namespace. - - Lists also the namespace properties, objects and resource type - associations. - """ - kwargs = {} - if args.resource_type: - kwargs['resource_type'] = args.resource_type - - namespace = gc.metadefs_namespace.get(args.namespace, **kwargs) - _namespace_show(namespace, int(args.max_column_width)) - - -@utils.arg('--resource-types', metavar='', action='append', - help='Resource type to filter namespaces.') -@utils.arg('--visibility', metavar='', - help='Visibility parameter to filter namespaces.') -@utils.arg('--page-size', metavar='', default=None, type=int, - help='Number of namespaces to request in each paginated request.') -def do_md_namespace_list(gc, args): - """List metadata definitions namespaces.""" - filter_keys = ['resource_types', 'visibility'] - filter_items = [(key, getattr(args, key, None)) for key in filter_keys] - filters = dict([item for item in filter_items if item[1] is not None]) - - kwargs = {'filters': filters} - if args.page_size is not None: - kwargs['page_size'] = args.page_size - - namespaces = gc.metadefs_namespace.list(**kwargs) - columns = ['namespace'] - utils.print_list(namespaces, columns) - - -@utils.arg('namespace', metavar='', - help='Name of namespace to delete.') -def do_md_namespace_delete(gc, args): - """Delete specified metadata definitions namespace with its contents.""" - gc.metadefs_namespace.delete(args.namespace) - - -# Metadata - catalog -RESOURCE_TYPE_SCHEMA = None - - -def get_resource_type_schema(): - global RESOURCE_TYPE_SCHEMA - if RESOURCE_TYPE_SCHEMA is None: - schema_path = expanduser("~/.glanceclient/resource_type_schema.json") - if os.path.isfile(schema_path): - with open(schema_path, "r") as f: - schema_raw = f.read() - RESOURCE_TYPE_SCHEMA = json.loads(schema_raw) - return RESOURCE_TYPE_SCHEMA - - -@utils.arg('namespace', metavar='', help='Name of namespace.') -@utils.schema_args(get_resource_type_schema) -def do_md_resource_type_associate(gc, args): - """Associate resource type with a metadata definitions namespace.""" - schema = gc.schemas.get('metadefs/resource_type') - _args = [(x[0].replace('-', '_'), x[1]) for x in vars(args).items()] - fields = dict(filter(lambda x: x[1] is not None and - (schema.is_core_property(x[0])), - _args)) - resource_type = gc.metadefs_resource_type.associate(args.namespace, - **fields) - utils.print_dict(resource_type) - - -@utils.arg('namespace', metavar='', help='Name of namespace.') -@utils.arg('resource_type', metavar='', - help='Name of resource type.') -def do_md_resource_type_deassociate(gc, args): - """Deassociate resource type with a metadata definitions namespace.""" - gc.metadefs_resource_type.deassociate(args.namespace, args.resource_type) - - -def do_md_resource_type_list(gc, args): - """List available resource type names.""" - resource_types = gc.metadefs_resource_type.list() - utils.print_list(resource_types, ['name']) - - -@utils.arg('namespace', metavar='', help='Name of namespace.') -def do_md_namespace_resource_type_list(gc, args): - """List resource types associated to specific namespace.""" - resource_types = gc.metadefs_resource_type.get(args.namespace) - utils.print_list(resource_types, ['name', 'prefix', 'properties_target']) - - -@utils.arg('namespace', metavar='', - help='Name of namespace the property will belong.') -@utils.arg('--name', metavar='', required=True, - help='Internal name of a property.') -@utils.arg('--title', metavar='', required=True, - help='Property name displayed to the user.') -@utils.arg('--schema', metavar='<SCHEMA>', required=True, - help='Valid JSON schema of a property.') -def do_md_property_create(gc, args): - """Create a new metadata definitions property inside a namespace.""" - try: - schema = json.loads(args.schema) - except ValueError: - utils.exit('Schema is not a valid JSON object.') - else: - fields = {'name': args.name, 'title': args.title} - fields.update(schema) - new_property = gc.metadefs_property.create(args.namespace, **fields) - utils.print_dict(new_property) - - -@utils.arg('namespace', metavar='<NAMESPACE>', - help='Name of namespace the property belongs.') -@utils.arg('property', metavar='<PROPERTY>', help='Name of a property.') -@utils.arg('--name', metavar='<NAME>', default=None, - help='New name of a property.') -@utils.arg('--title', metavar='<TITLE>', default=None, - help='Property name displayed to the user.') -@utils.arg('--schema', metavar='<SCHEMA>', default=None, - help='Valid JSON schema of a property.') -def do_md_property_update(gc, args): - """Update metadata definitions property inside a namespace.""" - fields = {} - if args.name: - fields['name'] = args.name - if args.title: - fields['title'] = args.title - if args.schema: - try: - schema = json.loads(args.schema) - except ValueError: - utils.exit('Schema is not a valid JSON object.') - else: - fields.update(schema) - - new_property = gc.metadefs_property.update(args.namespace, args.property, - **fields) - utils.print_dict(new_property) - - -@utils.arg('namespace', metavar='<NAMESPACE>', - help='Name of namespace the property belongs.') -@utils.arg('property', metavar='<PROPERTY>', help='Name of a property.') -@utils.arg('--max-column-width', metavar='<integer>', default=80, - help='The max column width of the printed table.') -def do_md_property_show(gc, args): - """Describe a specific metadata definitions property inside a namespace.""" - prop = gc.metadefs_property.get(args.namespace, args.property) - utils.print_dict(prop, int(args.max_column_width)) - - -@utils.arg('namespace', metavar='<NAMESPACE>', - help='Name of namespace the property belongs.') -@utils.arg('property', metavar='<PROPERTY>', help='Name of a property.') -def do_md_property_delete(gc, args): - """Delete a specific metadata definitions property inside a namespace.""" - gc.metadefs_property.delete(args.namespace, args.property) - - -@utils.arg('namespace', metavar='<NAMESPACE>', help='Name of namespace.') -def do_md_namespace_properties_delete(gc, args): - """Delete all metadata definitions property inside a specific namespace.""" - gc.metadefs_property.delete_all(args.namespace) - - -@utils.arg('namespace', metavar='<NAMESPACE>', help='Name of namespace.') -def do_md_property_list(gc, args): - """List metadata definitions properties inside a specific namespace.""" - properties = gc.metadefs_property.list(args.namespace) - columns = ['name', 'title', 'type'] - utils.print_list(properties, columns) - - -def _object_show(obj, max_column_width=None): - obj = dict(obj) # Warlock objects are compatible with dicts - # Flatten dicts for display - if 'properties' in obj: - objects = [k for k in obj['properties']] - obj['properties'] = objects - - if max_column_width: - utils.print_dict(obj, max_column_width) - else: - utils.print_dict(obj) - - -@utils.arg('namespace', metavar='<NAMESPACE>', - help='Name of namespace the object will belong.') -@utils.arg('--name', metavar='<NAME>', required=True, - help='Internal name of an object.') -@utils.arg('--schema', metavar='<SCHEMA>', required=True, - help='Valid JSON schema of an object.') -def do_md_object_create(gc, args): - """Create a new metadata definitions object inside a namespace.""" - try: - schema = json.loads(args.schema) - except ValueError: - utils.exit('Schema is not a valid JSON object.') - else: - fields = {'name': args.name} - fields.update(schema) - new_object = gc.metadefs_object.create(args.namespace, **fields) - _object_show(new_object) - - -@utils.arg('namespace', metavar='<NAMESPACE>', - help='Name of namespace the object belongs.') -@utils.arg('object', metavar='<OBJECT>', help='Name of an object.') -@utils.arg('--name', metavar='<NAME>', default=None, - help='New name of an object.') -@utils.arg('--schema', metavar='<SCHEMA>', default=None, - help='Valid JSON schema of an object.') -def do_md_object_update(gc, args): - """Update metadata definitions object inside a namespace.""" - fields = {} - if args.name: - fields['name'] = args.name - if args.schema: - try: - schema = json.loads(args.schema) - except ValueError: - utils.exit('Schema is not a valid JSON object.') - else: - fields.update(schema) - - new_object = gc.metadefs_object.update(args.namespace, args.object, - **fields) - _object_show(new_object) - - -@utils.arg('namespace', metavar='<NAMESPACE>', - help='Name of namespace the object belongs.') -@utils.arg('object', metavar='<OBJECT>', help='Name of an object.') -@utils.arg('--max-column-width', metavar='<integer>', default=80, - help='The max column width of the printed table.') -def do_md_object_show(gc, args): - """Describe a specific metadata definitions object inside a namespace.""" - obj = gc.metadefs_object.get(args.namespace, args.object) - _object_show(obj, int(args.max_column_width)) - - -@utils.arg('namespace', metavar='<NAMESPACE>', - help='Name of namespace the object belongs.') -@utils.arg('object', metavar='<OBJECT>', help='Name of an object.') -@utils.arg('property', metavar='<PROPERTY>', help='Name of a property.') -@utils.arg('--max-column-width', metavar='<integer>', default=80, - help='The max column width of the printed table.') -def do_md_object_property_show(gc, args): - """Describe a specific metadata definitions property inside an object.""" - obj = gc.metadefs_object.get(args.namespace, args.object) - try: - prop = obj['properties'][args.property] - prop['name'] = args.property - except KeyError: - utils.exit('Property %s not found in object %s.' % (args.property, - args.object)) - utils.print_dict(prop, int(args.max_column_width)) - - -@utils.arg('namespace', metavar='<NAMESPACE>', - help='Name of namespace the object belongs.') -@utils.arg('object', metavar='<OBJECT>', help='Name of an object.') -def do_md_object_delete(gc, args): - """Delete a specific metadata definitions object inside a namespace.""" - gc.metadefs_object.delete(args.namespace, args.object) - - -@utils.arg('namespace', metavar='<NAMESPACE>', help='Name of namespace.') -def do_md_namespace_objects_delete(gc, args): - """Delete all metadata definitions objects inside a specific namespace.""" - gc.metadefs_object.delete_all(args.namespace) - - -@utils.arg('namespace', metavar='<NAMESPACE>', help='Name of namespace.') -def do_md_object_list(gc, args): - """List metadata definitions objects inside a specific namespace.""" - objects = gc.metadefs_object.list(args.namespace) - columns = ['name', 'description'] - column_settings = { - "description": { - "max_width": 50, - "align": "l" - } - } - utils.print_list(objects, columns, field_settings=column_settings) - - -@utils.arg('--sort-key', default='status', - choices=tasks.SORT_KEY_VALUES, - help='Sort task list by specified field.') -@utils.arg('--sort-dir', default='desc', - choices=tasks.SORT_DIR_VALUES, - help='Sort task list in specified direction.') -@utils.arg('--page-size', metavar='<SIZE>', default=None, type=int, - help='Number of tasks to request in each paginated request.') -@utils.arg('--type', metavar='<TYPE>', - help='Filter tasks to those that have this type.') -@utils.arg('--status', metavar='<STATUS>', - help='Filter tasks to those that have this status.') -def do_task_list(gc, args): - """List tasks you can access.""" - filter_keys = ['type', 'status'] - filter_items = [(key, getattr(args, key)) for key in filter_keys] - filters = dict([item for item in filter_items if item[1] is not None]) - - kwargs = {'filters': filters} - if args.page_size is not None: - kwargs['page_size'] = args.page_size - - kwargs['sort_key'] = args.sort_key - kwargs['sort_dir'] = args.sort_dir - - tasks = gc.tasks.list(**kwargs) - - columns = ['ID', 'Type', 'Status', 'Owner'] - utils.print_list(tasks, columns) - - -@utils.arg('id', metavar='<TASK_ID>', help='ID of task to describe.') -def do_task_show(gc, args): - """Describe a specific task.""" - task = gc.tasks.get(args.id) - ignore = ['self', 'schema'] - task = dict([item for item in task.iteritems() if item[0] not in ignore]) - utils.print_dict(task) - - -@utils.arg('--type', metavar='<TYPE>', - help='Type of Task. Please refer to Glance schema or documentation' - ' to see which tasks are supported.') -@utils.arg('--input', metavar='<STRING>', default='{}', - help='Parameters of the task to be launched') -def do_task_create(gc, args): - """Create a new task.""" - if not (args.type and args.input): - utils.exit('Unable to create task. Specify task type and input.') - else: - try: - input = json.loads(args.input) - except ValueError: - utils.exit('Failed to parse the "input" parameter. Must be a ' - 'valid JSON object.') - - task_values = {'type': args.type, 'input': input} - task = gc.tasks.create(**task_values) - ignore = ['self', 'schema'] - task = dict([item for item in task.iteritems() - if item[0] not in ignore]) - utils.print_dict(task) diff --git a/code/daisyclient/daisyclient/v2/tasks.py b/code/daisyclient/daisyclient/v2/tasks.py deleted file mode 100755 index b0189c44..00000000 --- a/code/daisyclient/daisyclient/v2/tasks.py +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 2013 OpenStack LLC. -# Copyright 2013 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo_utils import encodeutils -import six -import warlock - -from daisyclient.common import utils -from daisyclient.v2 import schemas - -DEFAULT_PAGE_SIZE = 20 - -SORT_DIR_VALUES = ('asc', 'desc') -SORT_KEY_VALUES = ('id', 'type', 'status') - - -class Controller(object): - - def __init__(self, http_client, schema_client): - self.http_client = http_client - self.schema_client = schema_client - - @utils.memoized_property - def model(self): - schema = self.schema_client.get('task') - return warlock.model_factory(schema.raw(), schemas.SchemaBasedModel) - - def list(self, **kwargs): - """Retrieve a listing of Task objects - - :param page_size: Number of tasks to request in each paginated request - :returns generator over list of Tasks - """ - def paginate(url): - resp, body = self.http_client.get(url) - for task in body['tasks']: - yield task - try: - next_url = body['next'] - except KeyError: - return - else: - for task in paginate(next_url): - yield task - - filters = kwargs.get('filters', {}) - - if not kwargs.get('page_size'): - filters['limit'] = DEFAULT_PAGE_SIZE - else: - filters['limit'] = kwargs['page_size'] - - if 'marker' in kwargs: - filters['marker'] = kwargs['marker'] - - sort_key = kwargs.get('sort_key') - if sort_key is not None: - if sort_key in SORT_KEY_VALUES: - filters['sort_key'] = sort_key - else: - raise ValueError('sort_key must be one of the following: %s.' - % ', '.join(SORT_KEY_VALUES)) - - sort_dir = kwargs.get('sort_dir') - if sort_dir is not None: - if sort_dir in SORT_DIR_VALUES: - filters['sort_dir'] = sort_dir - else: - raise ValueError('sort_dir must be one of the following: %s.' - % ', '.join(SORT_DIR_VALUES)) - - for param, value in filters.items(): - if isinstance(value, six.string_types): - filters[param] = encodeutils.safe_encode(value) - - url = '/v2/tasks?%s' % six.moves.urllib.parse.urlencode(filters) - for task in paginate(url): - # NOTE(flwang): remove 'self' for now until we have an elegant - # way to pass it into the model constructor without conflict - task.pop('self', None) - yield self.model(**task) - - def get(self, task_id): - """Get a task based on given task id.""" - url = '/v2/tasks/%s' % task_id - resp, body = self.http_client.get(url) - # NOTE(flwang): remove 'self' for now until we have an elegant - # way to pass it into the model constructor without conflict - body.pop('self', None) - return self.model(**body) - - def create(self, **kwargs): - """Create a new task.""" - url = '/v2/tasks' - task = self.model() - - for (key, value) in kwargs.items(): - try: - setattr(task, key, value) - except warlock.InvalidOperation as e: - raise TypeError(unicode(e)) - - resp, body = self.http_client.post(url, data=task) - # NOTE(flwang): remove 'self' for now until we have an elegant - # way to pass it into the model constructor without conflict - body.pop('self', None) - return self.model(**body) diff --git a/code/daisyclient/setup.py b/code/daisyclient/setup.py index 73637574..d1fcc1ed 100755 --- a/code/daisyclient/setup.py +++ b/code/daisyclient/setup.py @@ -1,18 +1,18 @@ #!/usr/bin/env python -# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. # -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# http://www.apache.org/licenses/LICENSE-2.0 +# http://www.apache.org/licenses/LICENSE-2.0 # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -# implied. -# See the License for the specific language governing permissions and -# limitations under the License. +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools diff --git a/code/daisyclient/tests/keystone_client_fixtures.py b/code/daisyclient/tests/keystone_client_fixtures.py index b28b5e21..3c503f98 100755 --- a/code/daisyclient/tests/keystone_client_fixtures.py +++ b/code/daisyclient/tests/keystone_client_fixtures.py @@ -1,14 +1,17 @@ -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at +# Copyright 2012 OpenStack Foundation +# All Rights Reserved. # -# http://www.apache.org/licenses/LICENSE-2.0 +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at # -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. import copy import json diff --git a/code/daisyclient/tests/test_base.py b/code/daisyclient/tests/test_base.py index 4a97de80..f8b61265 100755 --- a/code/daisyclient/tests/test_base.py +++ b/code/daisyclient/tests/test_base.py @@ -1,5 +1,4 @@ -# Copyright 2013 OpenStack Foundation -# Copyright (C) 2013 Yahoo! Inc. +# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/code/daisyclient/tests/test_client.py b/code/daisyclient/tests/test_client.py index 62daaf56..55a55f49 100755 --- a/code/daisyclient/tests/test_client.py +++ b/code/daisyclient/tests/test_client.py @@ -1,4 +1,4 @@ -# Copyright 2014 Red Hat, Inc. +# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/code/daisyclient/tests/test_shell.py b/code/daisyclient/tests/test_shell.py index a440a536..7b150f43 100755 --- a/code/daisyclient/tests/test_shell.py +++ b/code/daisyclient/tests/test_shell.py @@ -1,5 +1,4 @@ -# Copyright 2013 OpenStack Foundation -# Copyright (C) 2013 Yahoo! Inc. +# Copyright 2012 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may diff --git a/code/daisyclient/tests/v1/test_image_members.py b/code/daisyclient/tests/v1/test_image_members.py deleted file mode 100755 index 6cd73f05..00000000 --- a/code/daisyclient/tests/v1/test_image_members.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import testtools - -import glanceclient.v1.image_members -import glanceclient.v1.images -from tests import utils - - -fixtures = { - '/v1/images/1/members': { - 'GET': ( - {}, - {'members': [ - {'member_id': '1', 'can_share': False}, - ]}, - ), - 'PUT': ({}, None), - }, - '/v1/images/1/members/1': { - 'GET': ( - {}, - {'member': { - 'member_id': '1', - 'can_share': False, - }}, - ), - 'PUT': ({}, None), - 'DELETE': ({}, None), - }, - '/v1/shared-images/1': { - 'GET': ( - {}, - {'shared_images': [ - {'image_id': '1', 'can_share': False}, - ]}, - ), - }, -} - - -class ImageMemberManagerTest(testtools.TestCase): - - def setUp(self): - super(ImageMemberManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) - self.mgr = glanceclient.v1.image_members.ImageMemberManager(self.api) - self.image = glanceclient.v1.images.Image(self.api, {'id': '1'}, True) - - def test_list_by_image(self): - members = self.mgr.list(image=self.image) - expect = [('GET', '/v1/images/1/members', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual(1, len(members)) - self.assertEqual('1', members[0].member_id) - self.assertEqual('1', members[0].image_id) - self.assertFalse(members[0].can_share) - - def test_list_by_member(self): - resource_class = glanceclient.v1.image_members.ImageMember - member = resource_class(self.api, {'member_id': '1'}, True) - self.mgr.list(member=member) - expect = [('GET', '/v1/shared-images/1', {}, None)] - self.assertEqual(expect, self.api.calls) - - def test_get(self): - member = self.mgr.get(self.image, '1') - expect = [('GET', '/v1/images/1/members/1', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('1', member.member_id) - self.assertEqual('1', member.image_id) - self.assertFalse(member.can_share) - - def test_delete(self): - self.mgr.delete('1', '1') - expect = [('DELETE', '/v1/images/1/members/1', {}, None)] - self.assertEqual(expect, self.api.calls) - - def test_create(self): - self.mgr.create(self.image, '1', can_share=True) - expect_body = {'member': {'can_share': True}} - expect = [('PUT', '/v1/images/1/members/1', {}, - sorted(expect_body.items()))] - self.assertEqual(expect, self.api.calls) - - def test_replace(self): - body = [ - {'member_id': '2', 'can_share': False}, - {'member_id': '3'}, - ] - self.mgr.replace(self.image, body) - expect = [('PUT', '/v1/images/1/members', {}, - sorted({'memberships': body}.items()))] - self.assertEqual(expect, self.api.calls) - - def test_replace_objects(self): - body = [ - glanceclient.v1.image_members.ImageMember( - self.mgr, {'member_id': '2', 'can_share': False}, True), - glanceclient.v1.image_members.ImageMember( - self.mgr, {'member_id': '3', 'can_share': True}, True), - ] - self.mgr.replace(self.image, body) - expect_body = { - 'memberships': [ - {'member_id': '2', 'can_share': False}, - {'member_id': '3', 'can_share': True}, - ], - } - expect = [('PUT', '/v1/images/1/members', {}, - sorted(expect_body.items()))] - self.assertEqual(expect, self.api.calls) diff --git a/code/daisyclient/tests/v1/test_images.py b/code/daisyclient/tests/v1/test_images.py deleted file mode 100755 index 137868cb..00000000 --- a/code/daisyclient/tests/v1/test_images.py +++ /dev/null @@ -1,963 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import errno -import json -import testtools - -import six -from six.moves.urllib import parse - -from glanceclient.v1 import client -from glanceclient.v1 import images -from glanceclient.v1 import shell -from tests import utils - - -fixtures = { - '/v1/images': { - 'POST': ( - { - 'location': '/v1/images/1', - 'x-openstack-request-id': 'req-1234', - }, - json.dumps( - {'image': { - 'id': '1', - 'name': 'image-1', - 'container_format': 'ovf', - 'disk_format': 'vhd', - 'owner': 'asdf', - 'size': '1024', - 'min_ram': '512', - 'min_disk': '10', - 'properties': {'a': 'b', 'c': 'd'}, - 'is_public': False, - 'protected': False, - 'deleted': False, - }}, - ), - ), - }, - '/v1/images/detail?limit=20': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'a', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?is_public=None&limit=20': { - 'GET': ( - {'x-openstack-request-id': 'req-1234'}, - {'images': [ - { - 'id': 'a', - 'owner': 'A', - 'is_public': 'True', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'owner': 'B', - 'is_public': 'False', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'c', - 'is_public': 'False', - 'name': 'image-3', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?is_public=None&limit=5': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'a', - 'owner': 'A', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'owner': 'B', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b2', - 'owner': 'B', - 'name': 'image-3', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'c', - 'name': 'image-3', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=5': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'a', - 'owner': 'A', - 'is_public': 'False', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'owner': 'A', - 'is_public': 'False', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b2', - 'owner': 'B', - 'name': 'image-3', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'c', - 'is_public': 'True', - 'name': 'image-3', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=20&marker=a': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'b', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'c', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=1': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'a', - 'name': 'image-0', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=1&marker=a': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'b', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=2': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'a', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=2&marker=b': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'c', - 'name': 'image-3', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=20&name=foo': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'a', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=20&property-ping=pong': - { - 'GET': ( - {}, - {'images': [ - { - 'id': '1', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=20&sort_dir=desc': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'a', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - '/v1/images/detail?limit=20&sort_key=name': { - 'GET': ( - {}, - {'images': [ - { - 'id': 'a', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - ]}, - ), - }, - - '/v1/images/1': { - 'HEAD': ( - { - 'x-image-meta-id': '1', - 'x-image-meta-name': 'image-1', - 'x-image-meta-property-arch': 'x86_64', - 'x-image-meta-is_public': 'false', - 'x-image-meta-protected': 'false', - 'x-image-meta-deleted': 'false', - }, - None), - 'GET': ( - {}, - 'XXX', - ), - 'PUT': ( - {}, - json.dumps( - {'image': { - 'id': '1', - 'name': 'image-2', - 'container_format': 'ovf', - 'disk_format': 'vhd', - 'owner': 'asdf', - 'size': '1024', - 'min_ram': '512', - 'min_disk': '10', - 'properties': {'a': 'b', 'c': 'd'}, - 'is_public': False, - 'protected': False, - }}, - ), - ), - 'DELETE': ({}, None), - }, - '/v1/images/2': { - 'HEAD': ( - { - 'x-image-meta-id': '2' - }, - None, - ), - 'GET': ( - { - 'x-image-meta-checksum': 'wrong' - }, - 'YYY', - ), - }, - '/v1/images/3': { - 'HEAD': ( - { - 'x-image-meta-id': '3', - 'x-image-meta-name': u"ni\xf1o" - }, - None, - ), - 'GET': ( - { - 'x-image-meta-checksum': '0745064918b49693cca64d6b6a13d28a' - }, - 'ZZZ', - ), - }, - '/v1/images/4': { - 'HEAD': ( - { - 'x-image-meta-id': '4', - 'x-image-meta-name': 'image-4', - 'x-image-meta-property-arch': 'x86_64', - 'x-image-meta-is_public': 'false', - 'x-image-meta-protected': 'false', - 'x-image-meta-deleted': 'false', - 'x-openstack-request-id': 'req-1234', - }, - None), - 'GET': ( - { - 'x-openstack-request-id': 'req-1234', - }, - 'XXX', - ), - 'PUT': ( - { - 'x-openstack-request-id': 'req-1234', - }, - json.dumps( - {'image': { - 'id': '4', - 'name': 'image-4', - 'container_format': 'ovf', - 'disk_format': 'vhd', - 'owner': 'asdf', - 'size': '1024', - 'min_ram': '512', - 'min_disk': '10', - 'properties': {'a': 'b', 'c': 'd'}, - 'is_public': False, - 'protected': False, - }}, - ), - ), - 'DELETE': ( - { - 'x-openstack-request-id': 'req-1234', - }, - None), - }, - '/v1/images/v2_created_img': { - 'PUT': ( - {}, - json.dumps({ - "image": { - "status": "queued", - "deleted": False, - "container_format": "bare", - "min_ram": 0, - "updated_at": "2013-12-20T01:51:45", - "owner": "foo", - "min_disk": 0, - "is_public": False, - "deleted_at": None, - "id": "v2_created_img", - "size": None, - "name": "bar", - "checksum": None, - "created_at": "2013-12-20T01:50:38", - "disk_format": "qcow2", - "properties": {}, - "protected": False - } - }) - ), - }, -} - - -class ImageManagerTest(testtools.TestCase): - - def setUp(self): - super(ImageManagerTest, self).setUp() - self.api = utils.FakeAPI(fixtures) - self.mgr = images.ImageManager(self.api) - - def test_paginated_list(self): - images = list(self.mgr.list(page_size=2)) - expect = [ - ('GET', '/v1/images/detail?limit=2', {}, None), - ('GET', '/v1/images/detail?limit=2&marker=b', {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual(3, len(images)) - self.assertEqual('a', images[0].id) - self.assertEqual('b', images[1].id) - self.assertEqual('c', images[2].id) - - def test_list_with_limit_less_than_page_size(self): - results = list(self.mgr.list(page_size=2, limit=1)) - expect = [('GET', '/v1/images/detail?limit=2', {}, None)] - self.assertEqual(1, len(results)) - self.assertEqual(expect, self.api.calls) - - def test_list_with_limit_greater_than_page_size(self): - images = list(self.mgr.list(page_size=1, limit=2)) - expect = [ - ('GET', '/v1/images/detail?limit=1', {}, None), - ('GET', '/v1/images/detail?limit=1&marker=a', {}, None), - ] - self.assertEqual(2, len(images)) - self.assertEqual('a', images[0].id) - self.assertEqual('b', images[1].id) - self.assertEqual(expect, self.api.calls) - - def test_list_with_marker(self): - list(self.mgr.list(marker='a')) - url = '/v1/images/detail?limit=20&marker=a' - expect = [('GET', url, {}, None)] - self.assertEqual(expect, self.api.calls) - - def test_list_with_filter(self): - list(self.mgr.list(filters={'name': "foo"})) - url = '/v1/images/detail?limit=20&name=foo' - expect = [('GET', url, {}, None)] - self.assertEqual(expect, self.api.calls) - - def test_list_with_property_filters(self): - list(self.mgr.list(filters={'properties': {'ping': 'pong'}})) - url = '/v1/images/detail?limit=20&property-ping=pong' - expect = [('GET', url, {}, None)] - self.assertEqual(expect, self.api.calls) - - def test_list_with_sort_dir(self): - list(self.mgr.list(sort_dir='desc')) - url = '/v1/images/detail?limit=20&sort_dir=desc' - expect = [('GET', url, {}, None)] - self.assertEqual(expect, self.api.calls) - - def test_list_with_sort_key(self): - list(self.mgr.list(sort_key='name')) - url = '/v1/images/detail?limit=20&sort_key=name' - expect = [('GET', url, {}, None)] - self.assertEqual(expect, self.api.calls) - - def test_get(self): - image = self.mgr.get('1') - expect = [('HEAD', '/v1/images/1', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('1', image.id) - self.assertEqual('image-1', image.name) - self.assertFalse(image.is_public) - self.assertFalse(image.protected) - self.assertFalse(image.deleted) - self.assertEqual({u'arch': u'x86_64'}, image.properties) - - def test_get_int(self): - image = self.mgr.get(1) - expect = [('HEAD', '/v1/images/1', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('1', image.id) - self.assertEqual('image-1', image.name) - self.assertFalse(image.is_public) - self.assertFalse(image.protected) - self.assertFalse(image.deleted) - self.assertEqual({u'arch': u'x86_64'}, image.properties) - - def test_get_encoding(self): - image = self.mgr.get('3') - self.assertEqual(u"ni\xf1o", image.name) - - def test_get_req_id(self): - params = {'return_req_id': []} - self.mgr.get('4', **params) - expect_req_id = ['req-1234'] - self.assertEqual(expect_req_id, params['return_req_id']) - - def test_data(self): - data = ''.join([b for b in self.mgr.data('1', do_checksum=False)]) - expect = [('GET', '/v1/images/1', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('XXX', data) - - expect += [('GET', '/v1/images/1', {}, None)] - data = ''.join([b for b in self.mgr.data('1')]) - self.assertEqual(expect, self.api.calls) - self.assertEqual('XXX', data) - - def test_data_with_wrong_checksum(self): - data = ''.join([b for b in self.mgr.data('2', do_checksum=False)]) - expect = [('GET', '/v1/images/2', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('YYY', data) - - expect += [('GET', '/v1/images/2', {}, None)] - data = self.mgr.data('2') - self.assertEqual(expect, self.api.calls) - try: - data = ''.join([b for b in data]) - self.fail('data did not raise an error.') - except IOError as e: - self.assertEqual(errno.EPIPE, e.errno) - msg = 'was fd7c5c4fdaa97163ee4ba8842baa537a expected wrong' - self.assertIn(msg, str(e)) - - def test_data_req_id(self): - params = { - 'do_checksum': False, - 'return_req_id': [], - } - ''.join([b for b in self.mgr.data('4', **params)]) - expect_req_id = ['req-1234'] - self.assertEqual(expect_req_id, params['return_req_id']) - - def test_data_with_checksum(self): - data = ''.join([b for b in self.mgr.data('3', do_checksum=False)]) - expect = [('GET', '/v1/images/3', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('ZZZ', data) - - expect += [('GET', '/v1/images/3', {}, None)] - data = ''.join([b for b in self.mgr.data('3')]) - self.assertEqual(expect, self.api.calls) - self.assertEqual('ZZZ', data) - - def test_delete(self): - self.mgr.delete('1') - expect = [('DELETE', '/v1/images/1', {}, None)] - self.assertEqual(expect, self.api.calls) - - def test_delete_req_id(self): - params = { - 'return_req_id': [] - } - self.mgr.delete('4', **params) - expect = [('DELETE', '/v1/images/4', {}, None)] - self.assertEqual(self.api.calls, expect) - expect_req_id = ['req-1234'] - self.assertEqual(expect_req_id, params['return_req_id']) - - def test_create_without_data(self): - params = { - 'id': '1', - 'name': 'image-1', - 'container_format': 'ovf', - 'disk_format': 'vhd', - 'owner': 'asdf', - 'size': 1024, - 'min_ram': 512, - 'min_disk': 10, - 'copy_from': 'http://example.com', - 'properties': {'a': 'b', 'c': 'd'}, - } - image = self.mgr.create(**params) - expect_headers = { - 'x-image-meta-id': '1', - 'x-image-meta-name': 'image-1', - 'x-image-meta-container_format': 'ovf', - 'x-image-meta-disk_format': 'vhd', - 'x-image-meta-owner': 'asdf', - 'x-image-meta-size': '1024', - 'x-image-meta-min_ram': '512', - 'x-image-meta-min_disk': '10', - 'x-glance-api-copy-from': 'http://example.com', - 'x-image-meta-property-a': 'b', - 'x-image-meta-property-c': 'd', - } - expect = [('POST', '/v1/images', expect_headers, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('1', image.id) - self.assertEqual('image-1', image.name) - self.assertEqual('ovf', image.container_format) - self.assertEqual('vhd', image.disk_format) - self.assertEqual('asdf', image.owner) - self.assertEqual(1024, image.size) - self.assertEqual(512, image.min_ram) - self.assertEqual(10, image.min_disk) - self.assertFalse(image.is_public) - self.assertFalse(image.protected) - self.assertFalse(image.deleted) - self.assertEqual({'a': 'b', 'c': 'd'}, image.properties) - - def test_create_with_data(self): - image_data = six.StringIO('XXX') - self.mgr.create(data=image_data) - expect_headers = {'x-image-meta-size': '3'} - expect = [('POST', '/v1/images', expect_headers, image_data)] - self.assertEqual(expect, self.api.calls) - - def test_create_req_id(self): - params = { - 'id': '4', - 'name': 'image-4', - 'container_format': 'ovf', - 'disk_format': 'vhd', - 'owner': 'asdf', - 'size': 1024, - 'min_ram': 512, - 'min_disk': 10, - 'copy_from': 'http://example.com', - 'properties': {'a': 'b', 'c': 'd'}, - 'return_req_id': [], - } - image = self.mgr.create(**params) - expect_headers = { - 'x-image-meta-id': '4', - 'x-image-meta-name': 'image-4', - 'x-image-meta-container_format': 'ovf', - 'x-image-meta-disk_format': 'vhd', - 'x-image-meta-owner': 'asdf', - 'x-image-meta-size': '1024', - 'x-image-meta-min_ram': '512', - 'x-image-meta-min_disk': '10', - 'x-glance-api-copy-from': 'http://example.com', - 'x-image-meta-property-a': 'b', - 'x-image-meta-property-c': 'd', - } - expect = [('POST', '/v1/images', expect_headers, None)] - self.assertEqual(self.api.calls, expect) - self.assertEqual(image.id, '1') - expect_req_id = ['req-1234'] - self.assertEqual(expect_req_id, params['return_req_id']) - - def test_update(self): - fields = { - 'name': 'image-2', - 'container_format': 'ovf', - 'disk_format': 'vhd', - 'owner': 'asdf', - 'size': 1024, - 'min_ram': 512, - 'min_disk': 10, - 'copy_from': 'http://example.com', - 'properties': {'a': 'b', 'c': 'd'}, - 'deleted': False, - } - image = self.mgr.update('1', **fields) - expect_hdrs = { - 'x-image-meta-name': 'image-2', - 'x-image-meta-container_format': 'ovf', - 'x-image-meta-disk_format': 'vhd', - 'x-image-meta-owner': 'asdf', - 'x-image-meta-size': '1024', - 'x-image-meta-min_ram': '512', - 'x-image-meta-min_disk': '10', - 'x-glance-api-copy-from': 'http://example.com', - 'x-image-meta-property-a': 'b', - 'x-image-meta-property-c': 'd', - 'x-image-meta-deleted': 'False', - 'x-glance-registry-purge-props': 'false', - } - expect = [('PUT', '/v1/images/1', expect_hdrs, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('1', image.id) - self.assertEqual('image-2', image.name) - self.assertEqual(1024, image.size) - self.assertEqual(512, image.min_ram) - self.assertEqual(10, image.min_disk) - - def test_update_with_data(self): - image_data = six.StringIO('XXX') - self.mgr.update('1', data=image_data) - expect_headers = {'x-image-meta-size': '3', - 'x-glance-registry-purge-props': 'false'} - expect = [('PUT', '/v1/images/1', expect_headers, image_data)] - self.assertEqual(expect, self.api.calls) - - def test_update_with_purge_props(self): - self.mgr.update('1', purge_props=True) - expect_headers = {'x-glance-registry-purge-props': 'true'} - expect = [('PUT', '/v1/images/1', expect_headers, None)] - self.assertEqual(expect, self.api.calls) - - def test_update_with_purge_props_false(self): - self.mgr.update('1', purge_props=False) - expect_headers = {'x-glance-registry-purge-props': 'false'} - expect = [('PUT', '/v1/images/1', expect_headers, None)] - self.assertEqual(expect, self.api.calls) - - def test_update_req_id(self): - fields = { - 'purge_props': True, - 'return_req_id': [], - } - self.mgr.update('4', **fields) - expect_headers = {'x-glance-registry-purge-props': 'true'} - expect = [('PUT', '/v1/images/4', expect_headers, None)] - self.assertEqual(self.api.calls, expect) - expect_req_id = ['req-1234'] - self.assertEqual(expect_req_id, fields['return_req_id']) - - def test_image_meta_from_headers_encoding(self): - value = u"ni\xf1o" - if six.PY2: - fields = {"x-image-meta-name": "ni\xc3\xb1o"} - else: - fields = {"x-image-meta-name": value} - headers = self.mgr._image_meta_from_headers(fields) - self.assertEqual(value, headers["name"]) - - def test_image_list_with_owner(self): - images = self.mgr.list(owner='A', page_size=20) - image_list = list(images) - self.assertEqual('A', image_list[0].owner) - self.assertEqual('a', image_list[0].id) - self.assertEqual(1, len(image_list)) - - def test_image_list_with_owner_req_id(self): - fields = { - 'owner': 'A', - 'return_req_id': [], - } - images = self.mgr.list(**fields) - next(images) - self.assertEqual(fields['return_req_id'], ['req-1234']) - - def test_image_list_with_notfound_owner(self): - images = self.mgr.list(owner='X', page_size=20) - self.assertEqual(0, len(list(images))) - - def test_image_list_with_empty_string_owner(self): - images = self.mgr.list(owner='', page_size=20) - image_list = list(images) - self.assertRaises(AttributeError, lambda: image_list[0].owner) - self.assertEqual('c', image_list[0].id) - self.assertEqual(1, len(image_list)) - - def test_image_list_with_unspecified_owner(self): - images = self.mgr.list(owner=None, page_size=5) - image_list = list(images) - self.assertEqual('A', image_list[0].owner) - self.assertEqual('a', image_list[0].id) - self.assertEqual('A', image_list[1].owner) - self.assertEqual('b', image_list[1].id) - self.assertEqual('B', image_list[2].owner) - self.assertEqual('b2', image_list[2].id) - self.assertRaises(AttributeError, lambda: image_list[3].owner) - self.assertEqual('c', image_list[3].id) - self.assertEqual(4, len(image_list)) - - def test_image_list_with_owner_and_limit(self): - images = self.mgr.list(owner='B', page_size=5, limit=1) - image_list = list(images) - self.assertEqual('B', image_list[0].owner) - self.assertEqual('b', image_list[0].id) - self.assertEqual(1, len(image_list)) - - def test_image_list_all_tenants(self): - images = self.mgr.list(is_public=None, page_size=5) - image_list = list(images) - self.assertEqual('A', image_list[0].owner) - self.assertEqual('a', image_list[0].id) - self.assertEqual('B', image_list[1].owner) - self.assertEqual('b', image_list[1].id) - self.assertEqual('B', image_list[2].owner) - self.assertEqual('b2', image_list[2].id) - self.assertRaises(AttributeError, lambda: image_list[3].owner) - self.assertEqual('c', image_list[3].id) - self.assertEqual(4, len(image_list)) - - def test_update_v2_created_image_using_v1(self): - fields_to_update = { - 'name': 'bar', - 'container_format': 'bare', - 'disk_format': 'qcow2', - } - image = self.mgr.update('v2_created_img', **fields_to_update) - expect_hdrs = { - 'x-image-meta-name': 'bar', - 'x-image-meta-container_format': 'bare', - 'x-image-meta-disk_format': 'qcow2', - 'x-glance-registry-purge-props': 'false', - } - expect = [('PUT', '/v1/images/v2_created_img', expect_hdrs, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('v2_created_img', image.id) - self.assertEqual('bar', image.name) - self.assertEqual(0, image.size) - self.assertEqual('bare', image.container_format) - self.assertEqual('qcow2', image.disk_format) - - -class ImageTest(testtools.TestCase): - def setUp(self): - super(ImageTest, self).setUp() - self.api = utils.FakeAPI(fixtures) - self.mgr = images.ImageManager(self.api) - - def test_delete(self): - image = self.mgr.get('1') - image.delete() - expect = [ - ('HEAD', '/v1/images/1', {}, None), - ('HEAD', '/v1/images/1', {}, None), - ('DELETE', '/v1/images/1', {}, None), - ] - self.assertEqual(expect, self.api.calls) - - def test_update(self): - image = self.mgr.get('1') - image.update(name='image-5') - expect = [ - ('HEAD', '/v1/images/1', {}, None), - ('HEAD', '/v1/images/1', {}, None), - ('PUT', '/v1/images/1', - {'x-image-meta-name': 'image-5', - 'x-glance-registry-purge-props': 'false'}, None), - ] - self.assertEqual(expect, self.api.calls) - - def test_data(self): - image = self.mgr.get('1') - data = ''.join([b for b in image.data()]) - expect = [ - ('HEAD', '/v1/images/1', {}, None), - ('HEAD', '/v1/images/1', {}, None), - ('GET', '/v1/images/1', {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual('XXX', data) - - data = ''.join([b for b in image.data(do_checksum=False)]) - expect += [('GET', '/v1/images/1', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('XXX', data) - - def test_data_with_wrong_checksum(self): - image = self.mgr.get('2') - data = ''.join([b for b in image.data(do_checksum=False)]) - expect = [ - ('HEAD', '/v1/images/2', {}, None), - ('HEAD', '/v1/images/2', {}, None), - ('GET', '/v1/images/2', {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual('YYY', data) - - data = image.data() - expect += [('GET', '/v1/images/2', {}, None)] - self.assertEqual(expect, self.api.calls) - try: - data = ''.join([b for b in image.data()]) - self.fail('data did not raise an error.') - except IOError as e: - self.assertEqual(errno.EPIPE, e.errno) - msg = 'was fd7c5c4fdaa97163ee4ba8842baa537a expected wrong' - self.assertIn(msg, str(e)) - - def test_data_with_checksum(self): - image = self.mgr.get('3') - data = ''.join([b for b in image.data(do_checksum=False)]) - expect = [ - ('HEAD', '/v1/images/3', {}, None), - ('HEAD', '/v1/images/3', {}, None), - ('GET', '/v1/images/3', {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual('ZZZ', data) - - data = ''.join([b for b in image.data()]) - expect += [('GET', '/v1/images/3', {}, None)] - self.assertEqual(expect, self.api.calls) - self.assertEqual('ZZZ', data) - - -class ParameterFakeAPI(utils.FakeAPI): - image_list = {'images': [ - { - 'id': 'a', - 'name': 'image-1', - 'properties': {'arch': 'x86_64'}, - }, - { - 'id': 'b', - 'name': 'image-2', - 'properties': {'arch': 'x86_64'}, - }, - ]} - - def get(self, url, **kwargs): - self.url = url - return utils.FakeResponse({}), ParameterFakeAPI.image_list - - -class FakeArg(object): - def __init__(self, arg_dict): - self.arg_dict = arg_dict - self.fields = arg_dict.keys() - - def __getattr__(self, name): - if name in self.arg_dict: - return self.arg_dict[name] - else: - return None - - -class UrlParameterTest(testtools.TestCase): - - def setUp(self): - super(UrlParameterTest, self).setUp() - self.api = ParameterFakeAPI({}) - self.gc = client.Client("http://fakeaddress.com") - self.gc.images = images.ImageManager(self.api) - - def test_is_public_list(self): - shell.do_image_list(self.gc, FakeArg({"is_public": "True"})) - parts = parse.urlparse(self.api.url) - qs_dict = parse.parse_qs(parts.query) - self.assertIn('is_public', qs_dict) - self.assertTrue(qs_dict['is_public'][0].lower() == "true") diff --git a/code/daisyclient/tests/v2/__init__.py b/code/daisyclient/tests/v2/__init__.py deleted file mode 100755 index e69de29b..00000000 diff --git a/code/daisyclient/tests/v2/test_images.py b/code/daisyclient/tests/v2/test_images.py deleted file mode 100755 index 409b5d4c..00000000 --- a/code/daisyclient/tests/v2/test_images.py +++ /dev/null @@ -1,1093 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import errno - -import testtools - -from glanceclient import exc -from glanceclient.v2 import images -from tests import utils - -_CHKSUM = '93264c3edf5972c9f1cb309543d38a5c' -_CHKSUM1 = '54264c3edf5972c9f1cb309453d38a46' - -_TAG1 = 'power' -_TAG2 = '64bit' - -_BOGUS_ID = '63e7f218-29de-4477-abdc-8db7c9533188' -_EVERYTHING_ID = '802cbbb7-0379-4c38-853f-37302b5e3d29' -_OWNED_IMAGE_ID = 'a4963502-acc7-42ba-ad60-5aa0962b7faf' -_OWNER_ID = '6bd473f0-79ae-40ad-a927-e07ec37b642f' -_PRIVATE_ID = 'e33560a7-3964-4de5-8339-5a24559f99ab' -_PUBLIC_ID = '857806e7-05b6-48e0-9d40-cb0e6fb727b9' -_SHARED_ID = '331ac905-2a38-44c5-a83d-653db8f08313' -_STATUS_REJECTED_ID = 'f3ea56ff-d7e4-4451-998c-1e3d33539c8e' - -data_fixtures = { - '/v2/schemas/image': { - 'GET': ( - {}, - { - 'name': 'image', - 'properties': { - 'id': {}, - 'name': {}, - 'locations': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'metadata': {'type': 'object'}, - 'url': {'type': 'string'}, - }, - 'required': ['url', 'metadata'], - }, - }, - 'color': {'type': 'string', 'is_base': False}, - }, - 'additionalProperties': {'type': 'string'}, - }, - ), - }, - '/v2/images?limit=%d' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - ]}, - ), - }, - '/v2/images?limit=2': { - 'GET': ( - {}, - { - 'images': [ - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - ], - 'next': ('/v2/images?limit=2&' - 'marker=6f99bf80-2ee6-47cf-acfe-1f1fabb7e810'), - }, - ), - }, - '/v2/images?limit=1': { - 'GET': ( - {}, - { - 'images': [ - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - ], - 'next': ('/v2/images?limit=1&' - 'marker=3a4560a1-e585-443e-9b39-553b46ec92d1'), - }, - ), - }, - ('/v2/images?limit=1&marker=3a4560a1-e585-443e-9b39-553b46ec92d1'): { - 'GET': ( - {}, - {'images': [ - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - ]}, - ), - }, - ('/v2/images?limit=1&marker=6f99bf80-2ee6-47cf-acfe-1f1fabb7e810'): { - 'GET': ( - {}, - {'images': [ - { - 'id': '3f99bf80-2ee6-47cf-acfe-1f1fabb7e811', - 'name': 'image-3', - }, - ]}, - ), - }, - '/v2/images/3a4560a1-e585-443e-9b39-553b46ec92d1': { - 'GET': ( - {}, - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - ), - 'PATCH': ( - {}, - '', - ), - }, - '/v2/images/e7e59ff6-fa2e-4075-87d3-1a1398a07dc3': { - 'GET': ( - {}, - { - 'id': 'e7e59ff6-fa2e-4075-87d3-1a1398a07dc3', - 'name': 'image-3', - 'barney': 'rubble', - 'george': 'jetson', - 'color': 'red', - }, - ), - 'PATCH': ( - {}, - '', - ), - }, - '/v2/images': { - 'POST': ( - {}, - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - ), - }, - '/v2/images/87b634c1-f893-33c9-28a9-e5673c99239a': { - 'DELETE': ( - {}, - { - 'id': '87b634c1-f893-33c9-28a9-e5673c99239a', - }, - ), - }, - '/v2/images/606b0e88-7c5a-4d54-b5bb-046105d4de6f/file': { - 'PUT': ( - {}, - '', - ), - }, - '/v2/images/5cc4bebc-db27-11e1-a1eb-080027cbe205/file': { - 'GET': ( - {}, - 'A', - ), - }, - '/v2/images/66fb18d6-db27-11e1-a1eb-080027cbe205/file': { - 'GET': ( - { - 'content-md5': 'wrong' - }, - 'BB', - ), - }, - '/v2/images/1b1c6366-dd57-11e1-af0f-02163e68b1d8/file': { - 'GET': ( - { - 'content-md5': 'defb99e69a9f1f6e06f15006b1f166ae' - }, - 'CCC', - ), - }, - '/v2/images?limit=%d&visibility=public' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': _PUBLIC_ID, - 'harvey': 'lipshitz', - }, - ]}, - ), - }, - '/v2/images?limit=%d&visibility=private' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': _PRIVATE_ID, - }, - ]}, - ), - }, - '/v2/images?limit=%d&visibility=shared' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': _SHARED_ID, - }, - ]}, - ), - }, - '/v2/images?limit=%d&member_status=rejected' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': _STATUS_REJECTED_ID, - }, - ]}, - ), - }, - '/v2/images?limit=%d&member_status=pending' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': []}, - ), - }, - '/v2/images?limit=%d&owner=%s' % (images.DEFAULT_PAGE_SIZE, _OWNER_ID): { - 'GET': ( - {}, - {'images': [ - { - 'id': _OWNED_IMAGE_ID, - }, - ]}, - ), - }, - '/v2/images?limit=%d&owner=%s' % (images.DEFAULT_PAGE_SIZE, _BOGUS_ID): { - 'GET': ( - {}, - {'images': []}, - ), - }, - '/v2/images?limit=%d&member_status=pending&owner=%s&visibility=shared' - % (images.DEFAULT_PAGE_SIZE, _BOGUS_ID): { - 'GET': ( - {}, - {'images': [ - { - 'id': _EVERYTHING_ID, - }, - ]}, - ), - }, - '/v2/images?checksum=%s&limit=%d' % (_CHKSUM, images.DEFAULT_PAGE_SIZE): { - 'GET': ( - {}, - {'images': [ - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - } - ]}, - ), - }, - '/v2/images?checksum=%s&limit=%d' % (_CHKSUM1, images.DEFAULT_PAGE_SIZE): { - 'GET': ( - {}, - {'images': [ - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - ]}, - ), - }, - '/v2/images?checksum=wrong&limit=%d' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': []}, - ), - }, - '/v2/images?limit=%d&tag=%s' % (images.DEFAULT_PAGE_SIZE, _TAG1): { - 'GET': ( - {}, - {'images': [ - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - } - ]}, - ), - }, - '/v2/images?limit=%d&tag=%s' % (images.DEFAULT_PAGE_SIZE, _TAG2): { - 'GET': ( - {}, - {'images': [ - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - ]}, - ), - }, - '/v2/images?limit=%d&tag=%s&tag=%s' % (images.DEFAULT_PAGE_SIZE, - _TAG1, _TAG2): - { - 'GET': ( - {}, - {'images': [ - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - } - ]}, - ), - }, - '/v2/images?limit=%d&tag=fake' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': []}, - ), - }, - '/v2/images/a2b83adc-888e-11e3-8872-78acc0b951d8': { - 'GET': ( - {}, - { - 'id': 'a2b83adc-888e-11e3-8872-78acc0b951d8', - 'name': 'image-location-tests', - 'locations': [{u'url': u'http://foo.com/', - u'metadata': {u'foo': u'foometa'}}, - {u'url': u'http://bar.com/', - u'metadata': {u'bar': u'barmeta'}}], - }, - ), - 'PATCH': ( - {}, - '', - ) - }, - '/v2/images?limit=%d&os_distro=NixOS' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': '8b052954-c76c-4e02-8e90-be89a70183a8', - 'name': 'image-5', - 'os_distro': 'NixOS', - }, - ]}, - ), - }, - '/v2/images?limit=%d&my_little_property=cant_be_this_cute' % - images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': []}, - ), - }, - '/v2/images?limit=%d&sort_key=name' % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - ]}, - ), - }, - '/v2/images?limit=%d&sort_key=name&sort_key=id' - % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image', - }, - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image', - }, - ]}, - ), - }, - '/v2/images?limit=%d&sort_dir=desc&sort_key=id' - % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - ]}, - ), - }, - '/v2/images?limit=%d&sort_dir=desc&sort_key=name&sort_key=id' - % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - ]}, - ), - }, - '/v2/images?limit=%d&sort_dir=desc&sort_dir=asc&sort_key=name&sort_key=id' - % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - ]}, - ), - }, - '/v2/images?limit=%d&sort=name%%3Adesc%%2Csize%%3Aasc' - % images.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'images': [ - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'name': 'image-2', - }, - { - 'id': '2a4560b2-e585-443e-9b39-553b46ec92d1', - 'name': 'image-1', - }, - ]}, - ), - }, -} - -schema_fixtures = { - 'image': { - 'GET': ( - {}, - { - 'name': 'image', - 'properties': { - 'id': {}, - 'name': {}, - 'locations': { - 'type': 'array', - 'items': { - 'type': 'object', - 'properties': { - 'metadata': {'type': 'object'}, - 'url': {'type': 'string'}, - }, - 'required': ['url', 'metadata'], - } - }, - 'color': {'type': 'string', 'is_base': False}, - 'tags': {'type': 'array'}, - }, - 'additionalProperties': {'type': 'string'}, - } - ) - } -} - - -class TestController(testtools.TestCase): - - def setUp(self): - super(TestController, self).setUp() - self.api = utils.FakeAPI(data_fixtures) - self.schema_api = utils.FakeSchemaAPI(schema_fixtures) - self.controller = images.Controller(self.api, self.schema_api) - - def test_list_images(self): - # NOTE(bcwaldon):cast to list since the controller returns a generator - images = list(self.controller.list()) - self.assertEqual('3a4560a1-e585-443e-9b39-553b46ec92d1', images[0].id) - self.assertEqual('image-1', images[0].name) - self.assertEqual('6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', images[1].id) - self.assertEqual('image-2', images[1].name) - - def test_list_images_paginated(self): - # NOTE(bcwaldon):cast to list since the controller returns a generator - images = list(self.controller.list(page_size=1)) - self.assertEqual('3a4560a1-e585-443e-9b39-553b46ec92d1', images[0].id) - self.assertEqual('image-1', images[0].name) - self.assertEqual('6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', images[1].id) - self.assertEqual('image-2', images[1].name) - - def test_list_images_paginated_with_limit(self): - # NOTE(bcwaldon):cast to list since the controller returns a generator - images = list(self.controller.list(limit=3, page_size=2)) - self.assertEqual('3a4560a1-e585-443e-9b39-553b46ec92d1', images[0].id) - self.assertEqual('image-1', images[0].name) - self.assertEqual('6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', images[1].id) - self.assertEqual('image-2', images[1].name) - self.assertEqual('3f99bf80-2ee6-47cf-acfe-1f1fabb7e811', images[2].id) - self.assertEqual('image-3', images[2].name) - self.assertEqual(3, len(images)) - - def test_list_images_visibility_public(self): - filters = {'filters': {'visibility': 'public'}} - images = list(self.controller.list(**filters)) - self.assertEqual(_PUBLIC_ID, images[0].id) - - def test_list_images_visibility_private(self): - filters = {'filters': {'visibility': 'private'}} - images = list(self.controller.list(**filters)) - self.assertEqual(_PRIVATE_ID, images[0].id) - - def test_list_images_visibility_shared(self): - filters = {'filters': {'visibility': 'shared'}} - images = list(self.controller.list(**filters)) - self.assertEqual(_SHARED_ID, images[0].id) - - def test_list_images_member_status_rejected(self): - filters = {'filters': {'member_status': 'rejected'}} - images = list(self.controller.list(**filters)) - self.assertEqual(_STATUS_REJECTED_ID, images[0].id) - - def test_list_images_for_owner(self): - filters = {'filters': {'owner': _OWNER_ID}} - images = list(self.controller.list(**filters)) - self.assertEqual(_OWNED_IMAGE_ID, images[0].id) - - def test_list_images_for_checksum_single_image(self): - fake_id = '3a4560a1-e585-443e-9b39-553b46ec92d1' - filters = {'filters': {'checksum': _CHKSUM}} - images = list(self.controller.list(**filters)) - self.assertEqual(1, len(images)) - self.assertEqual('%s' % fake_id, images[0].id) - - def test_list_images_for_checksum_multiple_images(self): - fake_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - fake_id2 = '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810' - filters = {'filters': {'checksum': _CHKSUM1}} - images = list(self.controller.list(**filters)) - self.assertEqual(2, len(images)) - self.assertEqual('%s' % fake_id1, images[0].id) - self.assertEqual('%s' % fake_id2, images[1].id) - - def test_list_images_for_wrong_checksum(self): - filters = {'filters': {'checksum': 'wrong'}} - images = list(self.controller.list(**filters)) - self.assertEqual(0, len(images)) - - def test_list_images_for_bogus_owner(self): - filters = {'filters': {'owner': _BOGUS_ID}} - images = list(self.controller.list(**filters)) - self.assertEqual([], images) - - def test_list_images_for_bunch_of_filters(self): - filters = {'filters': {'owner': _BOGUS_ID, - 'visibility': 'shared', - 'member_status': 'pending'}} - images = list(self.controller.list(**filters)) - self.assertEqual(_EVERYTHING_ID, images[0].id) - - def test_list_images_filters_encoding(self): - filters = {"owner": u"ni\xf1o"} - try: - list(self.controller.list(filters=filters)) - except KeyError: - # NOTE(flaper87): It raises KeyError because there's - # no fixture supporting this query: - # /v2/images?owner=ni%C3%B1o&limit=20 - # We just want to make sure filters are correctly encoded. - pass - self.assertEqual(b"ni\xc3\xb1o", filters["owner"]) - - def test_list_images_for_tag_single_image(self): - img_id = '3a4560a1-e585-443e-9b39-553b46ec92d1' - filters = {'filters': {'tag': [_TAG1]}} - images = list(self.controller.list(**filters)) - self.assertEqual(1, len(images)) - self.assertEqual('%s' % img_id, images[0].id) - pass - - def test_list_images_for_tag_multiple_images(self): - img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - img_id2 = '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810' - filters = {'filters': {'tag': [_TAG2]}} - images = list(self.controller.list(**filters)) - self.assertEqual(2, len(images)) - self.assertEqual('%s' % img_id1, images[0].id) - self.assertEqual('%s' % img_id2, images[1].id) - - def test_list_images_for_multi_tags(self): - img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - filters = {'filters': {'tag': [_TAG1, _TAG2]}} - images = list(self.controller.list(**filters)) - self.assertEqual(1, len(images)) - self.assertEqual('%s' % img_id1, images[0].id) - - def test_list_images_for_non_existent_tag(self): - filters = {'filters': {'tag': ['fake']}} - images = list(self.controller.list(**filters)) - self.assertEqual(0, len(images)) - - def test_list_images_with_single_sort_key(self): - img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - sort_key = 'name' - images = list(self.controller.list(sort_key=sort_key)) - self.assertEqual(2, len(images)) - self.assertEqual('%s' % img_id1, images[0].id) - - def test_list_with_multiple_sort_keys(self): - img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - sort_key = ['name', 'id'] - images = list(self.controller.list(sort_key=sort_key)) - self.assertEqual(2, len(images)) - self.assertEqual('%s' % img_id1, images[0].id) - - def test_list_images_with_desc_sort_dir(self): - img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - sort_key = 'id' - sort_dir = 'desc' - images = list(self.controller.list(sort_key=sort_key, - sort_dir=sort_dir)) - self.assertEqual(2, len(images)) - self.assertEqual('%s' % img_id1, images[1].id) - - def test_list_images_with_multiple_sort_keys_and_one_sort_dir(self): - img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - sort_key = ['name', 'id'] - sort_dir = 'desc' - images = list(self.controller.list(sort_key=sort_key, - sort_dir=sort_dir)) - self.assertEqual(2, len(images)) - self.assertEqual('%s' % img_id1, images[1].id) - - def test_list_images_with_multiple_sort_dirs(self): - img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - sort_key = ['name', 'id'] - sort_dir = ['desc', 'asc'] - images = list(self.controller.list(sort_key=sort_key, - sort_dir=sort_dir)) - self.assertEqual(2, len(images)) - self.assertEqual('%s' % img_id1, images[1].id) - - def test_list_images_with_new_sorting_syntax(self): - img_id1 = '2a4560b2-e585-443e-9b39-553b46ec92d1' - sort = 'name:desc,size:asc' - images = list(self.controller.list(sort=sort)) - self.assertEqual(2, len(images)) - self.assertEqual('%s' % img_id1, images[1].id) - - def test_list_images_sort_dirs_fewer_than_keys(self): - sort_key = ['name', 'id', 'created_at'] - sort_dir = ['desc', 'asc'] - self.assertRaises(exc.HTTPBadRequest, - list, - self.controller.list( - sort_key=sort_key, - sort_dir=sort_dir)) - - def test_list_images_combined_syntax(self): - sort_key = ['name', 'id'] - sort_dir = ['desc', 'asc'] - sort = 'name:asc' - self.assertRaises(exc.HTTPBadRequest, - list, - self.controller.list( - sort=sort, - sort_key=sort_key, - sort_dir=sort_dir)) - - def test_list_images_new_sorting_syntax_invalid_key(self): - sort = 'INVALID:asc' - self.assertRaises(exc.HTTPBadRequest, - list, - self.controller.list( - sort=sort)) - - def test_list_images_new_sorting_syntax_invalid_direction(self): - sort = 'name:INVALID' - self.assertRaises(exc.HTTPBadRequest, - list, - self.controller.list( - sort=sort)) - - def test_list_images_for_property(self): - filters = {'filters': dict([('os_distro', 'NixOS')])} - images = list(self.controller.list(**filters)) - self.assertEqual(1, len(images)) - - def test_list_images_for_non_existent_property(self): - filters = {'filters': dict([('my_little_property', - 'cant_be_this_cute')])} - images = list(self.controller.list(**filters)) - self.assertEqual(0, len(images)) - - def test_get_image(self): - image = self.controller.get('3a4560a1-e585-443e-9b39-553b46ec92d1') - self.assertEqual('3a4560a1-e585-443e-9b39-553b46ec92d1', image.id) - self.assertEqual('image-1', image.name) - - def test_create_image(self): - properties = { - 'name': 'image-1' - } - image = self.controller.create(**properties) - self.assertEqual('3a4560a1-e585-443e-9b39-553b46ec92d1', image.id) - self.assertEqual('image-1', image.name) - - def test_create_bad_additionalProperty_type(self): - properties = { - 'name': 'image-1', - 'bad_prop': True, - } - with testtools.ExpectedException(TypeError): - self.controller.create(**properties) - - def test_delete_image(self): - self.controller.delete('87b634c1-f893-33c9-28a9-e5673c99239a') - expect = [ - ('DELETE', - '/v2/images/87b634c1-f893-33c9-28a9-e5673c99239a', - {}, - None)] - self.assertEqual(expect, self.api.calls) - - def test_data_upload(self): - image_data = 'CCC' - image_id = '606b0e88-7c5a-4d54-b5bb-046105d4de6f' - self.controller.upload(image_id, image_data) - expect = [('PUT', '/v2/images/%s/file' % image_id, - {'Content-Type': 'application/octet-stream'}, - image_data)] - self.assertEqual(expect, self.api.calls) - - def test_data_upload_w_size(self): - image_data = 'CCC' - image_id = '606b0e88-7c5a-4d54-b5bb-046105d4de6f' - self.controller.upload(image_id, image_data, image_size=3) - body = {'image_data': image_data, - 'image_size': 3} - expect = [('PUT', '/v2/images/%s/file' % image_id, - {'Content-Type': 'application/octet-stream'}, - sorted(body.items()))] - self.assertEqual(expect, self.api.calls) - - def test_data_without_checksum(self): - body = self.controller.data('5cc4bebc-db27-11e1-a1eb-080027cbe205', - do_checksum=False) - body = ''.join([b for b in body]) - self.assertEqual('A', body) - - body = self.controller.data('5cc4bebc-db27-11e1-a1eb-080027cbe205') - body = ''.join([b for b in body]) - self.assertEqual('A', body) - - def test_data_with_wrong_checksum(self): - body = self.controller.data('66fb18d6-db27-11e1-a1eb-080027cbe205', - do_checksum=False) - body = ''.join([b for b in body]) - self.assertEqual('BB', body) - - body = self.controller.data('66fb18d6-db27-11e1-a1eb-080027cbe205') - try: - body = ''.join([b for b in body]) - self.fail('data did not raise an error.') - except IOError as e: - self.assertEqual(errno.EPIPE, e.errno) - msg = 'was 9d3d9048db16a7eee539e93e3618cbe7 expected wrong' - self.assertIn(msg, str(e)) - - def test_data_with_checksum(self): - body = self.controller.data('1b1c6366-dd57-11e1-af0f-02163e68b1d8', - do_checksum=False) - body = ''.join([b for b in body]) - self.assertEqual('CCC', body) - - body = self.controller.data('1b1c6366-dd57-11e1-af0f-02163e68b1d8') - body = ''.join([b for b in body]) - self.assertEqual('CCC', body) - - def test_update_replace_prop(self): - image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1' - params = {'name': 'pong'} - image = self.controller.update(image_id, **params) - expect_hdrs = { - 'Content-Type': 'application/openstack-images-v2.1-json-patch', - } - expect_body = [[('op', 'replace'), ('path', '/name'), - ('value', 'pong')]] - expect = [ - ('GET', '/v2/images/%s' % image_id, {}, None), - ('PATCH', '/v2/images/%s' % image_id, expect_hdrs, expect_body), - ('GET', '/v2/images/%s' % image_id, {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual(image_id, image.id) - # NOTE(bcwaldon):due to limitations of our fake api framework, the name - # will not actually change - yet in real life it will... - self.assertEqual('image-1', image.name) - - def test_update_add_prop(self): - image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1' - params = {'finn': 'human'} - image = self.controller.update(image_id, **params) - expect_hdrs = { - 'Content-Type': 'application/openstack-images-v2.1-json-patch', - } - expect_body = [[('op', 'add'), ('path', '/finn'), ('value', 'human')]] - expect = [ - ('GET', '/v2/images/%s' % image_id, {}, None), - ('PATCH', '/v2/images/%s' % image_id, expect_hdrs, expect_body), - ('GET', '/v2/images/%s' % image_id, {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual(image_id, image.id) - # NOTE(bcwaldon):due to limitations of our fake api framework, the name - # will not actually change - yet in real life it will... - self.assertEqual('image-1', image.name) - - def test_update_remove_prop(self): - image_id = 'e7e59ff6-fa2e-4075-87d3-1a1398a07dc3' - remove_props = ['barney'] - image = self.controller.update(image_id, remove_props) - expect_hdrs = { - 'Content-Type': 'application/openstack-images-v2.1-json-patch', - } - expect_body = [[('op', 'remove'), ('path', '/barney')]] - expect = [ - ('GET', '/v2/images/%s' % image_id, {}, None), - ('PATCH', '/v2/images/%s' % image_id, expect_hdrs, expect_body), - ('GET', '/v2/images/%s' % image_id, {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual(image_id, image.id) - # NOTE(bcwaldon):due to limitations of our fake api framework, the name - # will not actually change - yet in real life it will... - self.assertEqual('image-3', image.name) - - def test_update_replace_remove_same_prop(self): - image_id = 'e7e59ff6-fa2e-4075-87d3-1a1398a07dc3' - # Updating a property takes precedence over removing a property - params = {'barney': 'miller'} - remove_props = ['barney'] - image = self.controller.update(image_id, remove_props, **params) - expect_hdrs = { - 'Content-Type': 'application/openstack-images-v2.1-json-patch', - } - expect_body = ([[('op', 'replace'), ('path', '/barney'), - ('value', 'miller')]]) - expect = [ - ('GET', '/v2/images/%s' % image_id, {}, None), - ('PATCH', '/v2/images/%s' % image_id, expect_hdrs, expect_body), - ('GET', '/v2/images/%s' % image_id, {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual(image_id, image.id) - # NOTE(bcwaldon):due to limitations of our fake api framework, the name - # will not actually change - yet in real life it will... - self.assertEqual('image-3', image.name) - - def test_update_add_remove_same_prop(self): - image_id = 'e7e59ff6-fa2e-4075-87d3-1a1398a07dc3' - # Adding a property takes precedence over removing a property - params = {'finn': 'human'} - remove_props = ['finn'] - image = self.controller.update(image_id, remove_props, **params) - expect_hdrs = { - 'Content-Type': 'application/openstack-images-v2.1-json-patch', - } - expect_body = [[('op', 'add'), ('path', '/finn'), ('value', 'human')]] - expect = [ - ('GET', '/v2/images/%s' % image_id, {}, None), - ('PATCH', '/v2/images/%s' % image_id, expect_hdrs, expect_body), - ('GET', '/v2/images/%s' % image_id, {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual(image_id, image.id) - # NOTE(bcwaldon):due to limitations of our fake api framework, the name - # will not actually change - yet in real life it will... - self.assertEqual('image-3', image.name) - - def test_update_bad_additionalProperty_type(self): - image_id = 'e7e59ff6-fa2e-4075-87d3-1a1398a07dc3' - params = {'name': 'pong', 'bad_prop': False} - with testtools.ExpectedException(TypeError): - self.controller.update(image_id, **params) - - def test_update_add_custom_property(self): - image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1' - params = {'color': 'red'} - image = self.controller.update(image_id, **params) - expect_hdrs = { - 'Content-Type': 'application/openstack-images-v2.1-json-patch', - } - expect_body = [[('op', 'add'), ('path', '/color'), ('value', 'red')]] - expect = [ - ('GET', '/v2/images/%s' % image_id, {}, None), - ('PATCH', '/v2/images/%s' % image_id, expect_hdrs, expect_body), - ('GET', '/v2/images/%s' % image_id, {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual(image_id, image.id) - - def test_update_replace_custom_property(self): - image_id = 'e7e59ff6-fa2e-4075-87d3-1a1398a07dc3' - params = {'color': 'blue'} - image = self.controller.update(image_id, **params) - expect_hdrs = { - 'Content-Type': 'application/openstack-images-v2.1-json-patch', - } - expect_body = [[('op', 'replace'), ('path', '/color'), - ('value', 'blue')]] - expect = [ - ('GET', '/v2/images/%s' % image_id, {}, None), - ('PATCH', '/v2/images/%s' % image_id, expect_hdrs, expect_body), - ('GET', '/v2/images/%s' % image_id, {}, None), - ] - self.assertEqual(expect, self.api.calls) - self.assertEqual(image_id, image.id) - - def test_location_ops_when_server_disabled_location_ops(self): - # Location operations should not be allowed if server has not - # enabled location related operations - image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1' - estr = 'The administrator has disabled API access to image locations' - url = 'http://bar.com/' - meta = {'bar': 'barmeta'} - - e = self.assertRaises(exc.HTTPBadRequest, - self.controller.add_location, - image_id, url, meta) - self.assertIn(estr, str(e)) - - e = self.assertRaises(exc.HTTPBadRequest, - self.controller.delete_locations, - image_id, set([url])) - self.assertIn(estr, str(e)) - - e = self.assertRaises(exc.HTTPBadRequest, - self.controller.update_location, - image_id, url, meta) - self.assertIn(estr, str(e)) - - def _empty_get(self, image_id): - return ('GET', '/v2/images/%s' % image_id, {}, None) - - def _patch_req(self, image_id, patch_body): - c_type = 'application/openstack-images-v2.1-json-patch' - data = [sorted(d.items()) for d in patch_body] - return ('PATCH', - '/v2/images/%s' % image_id, - {'Content-Type': c_type}, - data) - - def test_add_location(self): - image_id = 'a2b83adc-888e-11e3-8872-78acc0b951d8' - new_loc = {'url': 'http://spam.com/', 'metadata': {'spam': 'ham'}} - add_patch = {'path': '/locations/-', 'value': new_loc, 'op': 'add'} - self.controller.add_location(image_id, **new_loc) - self.assertEqual(self.api.calls, [ - self._empty_get(image_id), - self._patch_req(image_id, [add_patch]), - self._empty_get(image_id) - ]) - - def test_add_duplicate_location(self): - image_id = 'a2b83adc-888e-11e3-8872-78acc0b951d8' - new_loc = {'url': 'http://foo.com/', 'metadata': {'foo': 'newfoo'}} - err_str = 'A location entry at %s already exists' % new_loc['url'] - - err = self.assertRaises(exc.HTTPConflict, - self.controller.add_location, - image_id, **new_loc) - self.assertIn(err_str, str(err)) - - def test_remove_location(self): - image_id = 'a2b83adc-888e-11e3-8872-78acc0b951d8' - url_set = set(['http://foo.com/', 'http://bar.com/']) - del_patches = [{'path': '/locations/1', 'op': 'remove'}, - {'path': '/locations/0', 'op': 'remove'}] - self.controller.delete_locations(image_id, url_set) - self.assertEqual(self.api.calls, [ - self._empty_get(image_id), - self._patch_req(image_id, del_patches) - ]) - - def test_remove_missing_location(self): - image_id = 'a2b83adc-888e-11e3-8872-78acc0b951d8' - url_set = set(['http://spam.ham/']) - err_str = 'Unknown URL(s): %s' % list(url_set) - - err = self.assertRaises(exc.HTTPNotFound, - self.controller.delete_locations, - image_id, url_set) - self.assertIn(err_str, str(err)) - - def test_update_location(self): - image_id = 'a2b83adc-888e-11e3-8872-78acc0b951d8' - new_loc = {'url': 'http://foo.com/', 'metadata': {'spam': 'ham'}} - fixture_idx = '/v2/images/%s' % (image_id) - orig_locations = data_fixtures[fixture_idx]['GET'][1]['locations'] - loc_map = dict([(l['url'], l) for l in orig_locations]) - loc_map[new_loc['url']] = new_loc - mod_patch = [{'path': '/locations', 'op': 'replace', - 'value': []}, - {'path': '/locations', 'op': 'replace', - 'value': list(loc_map.values())}] - self.controller.update_location(image_id, **new_loc) - self.assertEqual(self.api.calls, [ - self._empty_get(image_id), - self._patch_req(image_id, mod_patch), - self._empty_get(image_id) - ]) - - def test_update_tags(self): - image_id = 'a2b83adc-888e-11e3-8872-78acc0b951d8' - tag_map = {'tags': ['tag01', 'tag02', 'tag03']} - - image = self.controller.update(image_id, **tag_map) - - expected_body = [{'path': '/tags', 'op': 'replace', - 'value': tag_map['tags']}] - expected = [ - self._empty_get(image_id), - self._patch_req(image_id, expected_body), - self._empty_get(image_id) - ] - self.assertEqual(expected, self.api.calls) - self.assertEqual(image_id, image.id) - - def test_update_missing_location(self): - image_id = 'a2b83adc-888e-11e3-8872-78acc0b951d8' - new_loc = {'url': 'http://spam.com/', 'metadata': {'spam': 'ham'}} - err_str = 'Unknown URL: %s' % new_loc['url'] - err = self.assertRaises(exc.HTTPNotFound, - self.controller.update_location, - image_id, **new_loc) - self.assertIn(err_str, str(err)) diff --git a/code/daisyclient/tests/v2/test_members.py b/code/daisyclient/tests/v2/test_members.py deleted file mode 100755 index 7a3688fe..00000000 --- a/code/daisyclient/tests/v2/test_members.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import testtools - -from glanceclient.v2 import image_members -from tests import utils - - -IMAGE = '3a4560a1-e585-443e-9b39-553b46ec92d1' -MEMBER = '11223344-5566-7788-9911-223344556677' - - -data_fixtures = { - '/v2/images/{image}/members'.format(image=IMAGE): { - 'GET': ( - {}, - {'members': [ - { - 'image_id': IMAGE, - 'member_id': MEMBER, - }, - ]}, - ), - 'POST': ( - {}, - { - 'image_id': IMAGE, - 'member_id': MEMBER, - 'status': 'pending' - } - ) - }, - '/v2/images/{image}/members/{mem}'.format(image=IMAGE, mem=MEMBER): { - 'DELETE': ( - {}, - None, - ), - 'PUT': ( - {}, - { - 'image_id': IMAGE, - 'member_id': MEMBER, - 'status': 'accepted' - } - ), - } -} - -schema_fixtures = { - 'member': { - 'GET': ( - {}, - { - 'name': 'member', - 'properties': { - 'image_id': {}, - 'member_id': {} - } - }, - ) - } -} - - -class TestController(testtools.TestCase): - - def setUp(self): - super(TestController, self).setUp() - self.api = utils.FakeAPI(data_fixtures) - self.schema_api = utils.FakeSchemaAPI(schema_fixtures) - self.controller = image_members.Controller(self.api, self.schema_api) - - def test_list_image_members(self): - image_id = IMAGE - # NOTE(iccha): cast to list since the controller returns a generator - image_members = list(self.controller.list(image_id)) - self.assertEqual(IMAGE, image_members[0].image_id) - self.assertEqual(MEMBER, image_members[0].member_id) - - def test_delete_image_member(self): - image_id = IMAGE - member_id = MEMBER - self.controller.delete(image_id, member_id) - expect = [ - ('DELETE', - '/v2/images/{image}/members/{mem}'.format(image=IMAGE, - mem=MEMBER), - {}, - None)] - self.assertEqual(expect, self.api.calls) - - def test_update_image_members(self): - image_id = IMAGE - member_id = MEMBER - status = 'accepted' - image_member = self.controller.update(image_id, member_id, status) - self.assertEqual(IMAGE, image_member.image_id) - self.assertEqual(MEMBER, image_member.member_id) - self.assertEqual(status, image_member.status) - - def test_create_image_members(self): - image_id = IMAGE - member_id = MEMBER - status = 'pending' - image_member = self.controller.create(image_id, member_id) - self.assertEqual(IMAGE, image_member.image_id) - self.assertEqual(MEMBER, image_member.member_id) - self.assertEqual(status, image_member.status) diff --git a/code/daisyclient/tests/v2/test_metadefs_namespaces.py b/code/daisyclient/tests/v2/test_metadefs_namespaces.py deleted file mode 100755 index 41dcbb47..00000000 --- a/code/daisyclient/tests/v2/test_metadefs_namespaces.py +++ /dev/null @@ -1,675 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import testtools - -from glanceclient.v2 import metadefs -from tests import utils - -NAMESPACE1 = 'Namespace1' -NAMESPACE2 = 'Namespace2' -NAMESPACE3 = 'Namespace3' -NAMESPACE4 = 'Namespace4' -NAMESPACE5 = 'Namespace5' -NAMESPACE6 = 'Namespace6' -NAMESPACE7 = 'Namespace7' -NAMESPACE8 = 'Namespace8' -NAMESPACENEW = 'NamespaceNew' -RESOURCE_TYPE1 = 'ResourceType1' -RESOURCE_TYPE2 = 'ResourceType2' -OBJECT1 = 'Object1' -PROPERTY1 = 'Property1' -PROPERTY2 = 'Property2' - - -def _get_namespace_fixture(ns_name, rt_name=RESOURCE_TYPE1, **kwargs): - ns = { - "display_name": "Flavor Quota", - "description": "DESCRIPTION1", - "self": "/v2/metadefs/namespaces/%s" % ns_name, - "namespace": ns_name, - "visibility": "public", - "protected": True, - "owner": "admin", - "resource_types": [ - { - "name": rt_name - } - ], - "schema": "/v2/schemas/metadefs/namespace", - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - - ns.update(kwargs) - - return ns - -data_fixtures = { - "/v2/metadefs/namespaces?limit=20": { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=20", - "namespaces": [ - _get_namespace_fixture(NAMESPACE1), - _get_namespace_fixture(NAMESPACE2), - ], - "schema": "/v2/schemas/metadefs/namespaces" - } - ) - }, - "/v2/metadefs/namespaces?limit=1": { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=1", - "namespaces": [ - _get_namespace_fixture(NAMESPACE7), - ], - "schema": "/v2/schemas/metadefs/namespaces", - "next": "/v2/metadefs/namespaces?marker=%s&limit=1" - % NAMESPACE7, - } - ) - }, - "/v2/metadefs/namespaces?limit=1&marker=%s" % NAMESPACE7: { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=2", - "namespaces": [ - _get_namespace_fixture(NAMESPACE8), - ], - "schema": "/v2/schemas/metadefs/namespaces" - } - ) - }, - "/v2/metadefs/namespaces?limit=2&marker=%s" % NAMESPACE6: { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=2", - "namespaces": [ - _get_namespace_fixture(NAMESPACE7), - _get_namespace_fixture(NAMESPACE8), - ], - "schema": "/v2/schemas/metadefs/namespaces" - } - ) - }, - "/v2/metadefs/namespaces?limit=20&sort_dir=asc": { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=1", - "namespaces": [ - _get_namespace_fixture(NAMESPACE1), - ], - "schema": "/v2/schemas/metadefs/namespaces" - } - ) - }, - "/v2/metadefs/namespaces?limit=20&sort_key=created_at": { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=1", - "namespaces": [ - _get_namespace_fixture(NAMESPACE1), - ], - "schema": "/v2/schemas/metadefs/namespaces" - } - ) - }, - "/v2/metadefs/namespaces?limit=20&resource_types=%s" % RESOURCE_TYPE1: { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=20", - "namespaces": [ - _get_namespace_fixture(NAMESPACE3), - ], - "schema": "/v2/schemas/metadefs/namespaces" - } - ) - }, - "/v2/metadefs/namespaces?limit=20&resource_types=" - "%s%%2C%s" % (RESOURCE_TYPE1, RESOURCE_TYPE2): { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=20", - "namespaces": [ - _get_namespace_fixture(NAMESPACE4), - ], - "schema": "/v2/schemas/metadefs/namespaces" - } - ) - }, - "/v2/metadefs/namespaces?limit=20&visibility=private": { - "GET": ( - {}, - { - "first": "/v2/metadefs/namespaces?limit=20", - "namespaces": [ - _get_namespace_fixture(NAMESPACE5), - ], - "schema": "/v2/schemas/metadefs/namespaces" - } - ) - }, - "/v2/metadefs/namespaces": { - "POST": ( - {}, - { - "display_name": "Flavor Quota", - "description": "DESCRIPTION1", - "self": "/v2/metadefs/namespaces/%s" % 'NamespaceNew', - "namespace": 'NamespaceNew', - "visibility": "public", - "protected": True, - "owner": "admin", - "schema": "/v2/schemas/metadefs/namespace", - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - ) - }, - "/v2/metadefs/namespaces/%s" % NAMESPACE1: { - "GET": ( - {}, - { - "display_name": "Flavor Quota", - "description": "DESCRIPTION1", - "objects": [ - { - "description": "DESCRIPTION2", - "name": "OBJECT1", - "self": "/v2/metadefs/namespaces/%s/objects/" % - OBJECT1, - "required": [], - "properties": { - PROPERTY1: { - "type": "integer", - "description": "DESCRIPTION3", - "title": "Quota: CPU Shares" - }, - PROPERTY2: { - "minimum": 1000, - "type": "integer", - "description": "DESCRIPTION4", - "maximum": 1000000, - "title": "Quota: CPU Period" - }, - }, - "schema": "/v2/schemas/metadefs/object" - } - ], - "self": "/v2/metadefs/namespaces/%s" % NAMESPACE1, - "namespace": NAMESPACE1, - "visibility": "public", - "protected": True, - "owner": "admin", - "resource_types": [ - { - "name": RESOURCE_TYPE1 - } - ], - "schema": "/v2/schemas/metadefs/namespace", - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - ), - "PUT": ( - {}, - { - "display_name": "Flavor Quota", - "description": "DESCRIPTION1", - "objects": [ - { - "description": "DESCRIPTION2", - "name": "OBJECT1", - "self": "/v2/metadefs/namespaces/%s/objects/" % - OBJECT1, - "required": [], - "properties": { - PROPERTY1: { - "type": "integer", - "description": "DESCRIPTION3", - "title": "Quota: CPU Shares" - }, - PROPERTY2: { - "minimum": 1000, - "type": "integer", - "description": "DESCRIPTION4", - "maximum": 1000000, - "title": "Quota: CPU Period" - }, - }, - "schema": "/v2/schemas/metadefs/object" - } - ], - "self": "/v2/metadefs/namespaces/%s" % NAMESPACENEW, - "namespace": NAMESPACENEW, - "visibility": "public", - "protected": True, - "owner": "admin", - "resource_types": [ - { - "name": RESOURCE_TYPE1 - } - ], - "schema": "/v2/schemas/metadefs/namespace", - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - ), - "DELETE": ( - {}, - {} - ) - }, - "/v2/metadefs/namespaces/%s?resource_type=%s" % (NAMESPACE6, - RESOURCE_TYPE1): - { - "GET": ( - {}, - { - "display_name": "Flavor Quota", - "description": "DESCRIPTION1", - "objects": [], - "self": "/v2/metadefs/namespaces/%s" % NAMESPACE1, - "namespace": NAMESPACE6, - "visibility": "public", - "protected": True, - "owner": "admin", - "resource_types": [ - { - "name": RESOURCE_TYPE1 - } - ], - "schema": "/v2/schemas/metadefs/namespace", - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - ), - }, -} - -schema_fixtures = { - "metadefs/namespace": - { - "GET": ( - {}, - { - "additionalProperties": False, - "definitions": { - "property": { - "additionalProperties": { - "required": [ - "title", - "type" - ], - "type": "object", - "properties": { - "additionalItems": { - "type": "boolean" - }, - "enum": { - "type": "array" - }, - "description": { - "type": "string" - }, - "title": { - "type": "string" - }, - "default": {}, - "minLength": { - "$ref": "#/definitions/" - "positiveIntegerDefault0" - }, - "required": { - "$ref": "#/definitions/stringArray" - }, - "maximum": { - "type": "number" - }, - "minItems": { - "$ref": "#/definitions/" - "positiveIntegerDefault0" - }, - "readonly": { - "type": "boolean" - }, - "minimum": { - "type": "number" - }, - "maxItems": { - "$ref": "#/definitions/" - "positiveInteger" - }, - "maxLength": { - "$ref": "#/definitions/positiveInteger" - }, - "uniqueItems": { - "default": False, - "type": "boolean" - }, - "pattern": { - "type": "string", - "format": "regex" - }, - "items": { - "type": "object", - "properties": { - "enum": { - "type": "array" - }, - "type": { - "enum": [ - "array", - "boolean", - "integer", - "number", - "object", - "string", - "null" - ], - "type": "string" - } - } - }, - "type": { - "enum": [ - "array", - "boolean", - "integer", - "number", - "object", - "string", - "null" - ], - "type": "string" - } - } - }, - "type": "object" - }, - "positiveIntegerDefault0": { - "allOf": [ - { - "$ref": "#/definitions/positiveInteger" - }, - { - "default": 0 - } - ] - }, - "stringArray": { - "uniqueItems": True, - "items": { - "type": "string" - }, - "type": "array" - }, - "positiveInteger": { - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "namespace" - ], - "name": "namespace", - "properties": { - "description": { - "type": "string", - "description": "Provides a user friendly description " - "of the namespace.", - "maxLength": 500 - }, - "updated_at": { - "type": "string", - "description": "Date and time of the last namespace " - "modification (READ-ONLY)", - "format": "date-time" - }, - "visibility": { - "enum": [ - "public", - "private" - ], - "type": "string", - "description": "Scope of namespace accessibility." - }, - "self": { - "type": "string" - }, - "objects": { - "items": { - "type": "object", - "properties": { - "properties": { - "$ref": "#/definitions/property" - }, - "required": { - "$ref": "#/definitions/stringArray" - }, - "name": { - "type": "string" - }, - "description": { - "type": "string" - } - } - }, - "type": "array" - }, - "owner": { - "type": "string", - "description": "Owner of the namespace.", - "maxLength": 255 - }, - "resource_types": { - "items": { - "type": "object", - "properties": { - "prefix": { - "type": "string" - }, - "name": { - "type": "string" - }, - "metadata_type": { - "type": "string" - } - } - }, - "type": "array" - }, - "properties": { - "$ref": "#/definitions/property" - }, - "display_name": { - "type": "string", - "description": "The user friendly name for the " - "namespace. Used by UI if available.", - "maxLength": 80 - }, - "created_at": { - "type": "string", - "description": "Date and time of namespace creation " - "(READ-ONLY)", - "format": "date-time" - }, - "namespace": { - "type": "string", - "description": "The unique namespace text.", - "maxLength": 80 - }, - "protected": { - "type": "boolean", - "description": "If true, namespace will not be " - "deletable." - }, - "schema": { - "type": "string" - } - } - } - ), - } -} - - -class TestNamespaceController(testtools.TestCase): - - def setUp(self): - super(TestNamespaceController, self).setUp() - self.api = utils.FakeAPI(data_fixtures) - self.schema_api = utils.FakeSchemaAPI(schema_fixtures) - self.controller = metadefs.NamespaceController(self.api, - self.schema_api) - - def test_list_namespaces(self): - namespaces = list(self.controller.list()) - - self.assertEqual(2, len(namespaces)) - self.assertEqual(NAMESPACE1, namespaces[0]['namespace']) - self.assertEqual(NAMESPACE2, namespaces[1]['namespace']) - - def test_list_namespaces_paginate(self): - namespaces = list(self.controller.list(page_size=1)) - - self.assertEqual(2, len(namespaces)) - self.assertEqual(NAMESPACE7, namespaces[0]['namespace']) - self.assertEqual(NAMESPACE8, namespaces[1]['namespace']) - - def test_list_with_limit_greater_than_page_size(self): - namespaces = list(self.controller.list(page_size=1, limit=2)) - self.assertEqual(2, len(namespaces)) - self.assertEqual(NAMESPACE7, namespaces[0]['namespace']) - self.assertEqual(NAMESPACE8, namespaces[1]['namespace']) - - def test_list_with_marker(self): - namespaces = list(self.controller.list(marker=NAMESPACE6, page_size=2)) - self.assertEqual(2, len(namespaces)) - self.assertEqual(NAMESPACE7, namespaces[0]['namespace']) - self.assertEqual(NAMESPACE8, namespaces[1]['namespace']) - - def test_list_with_sort_dir(self): - namespaces = list(self.controller.list(sort_dir='asc', limit=1)) - self.assertEqual(1, len(namespaces)) - self.assertEqual(NAMESPACE1, namespaces[0]['namespace']) - - def test_list_with_sort_dir_invalid(self): - # NOTE(TravT): The clients work by returning an iterator. - # Invoking the iterator is what actually executes the logic. - ns_iterator = self.controller.list(sort_dir='foo') - self.assertRaises(ValueError, next, ns_iterator) - - def test_list_with_sort_key(self): - namespaces = list(self.controller.list(sort_key='created_at', limit=1)) - self.assertEqual(1, len(namespaces)) - self.assertEqual(NAMESPACE1, namespaces[0]['namespace']) - - def test_list_with_sort_key_invalid(self): - # NOTE(TravT): The clients work by returning an iterator. - # Invoking the iterator is what actually executes the logic. - ns_iterator = self.controller.list(sort_key='foo') - self.assertRaises(ValueError, next, ns_iterator) - - def test_list_namespaces_with_one_resource_type_filter(self): - namespaces = list(self.controller.list( - filters={ - 'resource_types': [RESOURCE_TYPE1] - } - )) - - self.assertEqual(1, len(namespaces)) - self.assertEqual(NAMESPACE3, namespaces[0]['namespace']) - - def test_list_namespaces_with_multiple_resource_types_filter(self): - namespaces = list(self.controller.list( - filters={ - 'resource_types': [RESOURCE_TYPE1, RESOURCE_TYPE2] - } - )) - - self.assertEqual(1, len(namespaces)) - self.assertEqual(NAMESPACE4, namespaces[0]['namespace']) - - def test_list_namespaces_with_visibility_filter(self): - namespaces = list(self.controller.list( - filters={ - 'visibility': 'private' - } - )) - - self.assertEqual(1, len(namespaces)) - self.assertEqual(NAMESPACE5, namespaces[0]['namespace']) - - def test_get_namespace(self): - namespace = self.controller.get(NAMESPACE1) - self.assertEqual(NAMESPACE1, namespace.namespace) - self.assertTrue(namespace.protected) - - def test_get_namespace_with_resource_type(self): - namespace = self.controller.get(NAMESPACE6, - resource_type=RESOURCE_TYPE1) - self.assertEqual(NAMESPACE6, namespace.namespace) - self.assertTrue(namespace.protected) - - def test_create_namespace(self): - properties = { - 'namespace': NAMESPACENEW - } - namespace = self.controller.create(**properties) - - self.assertEqual(NAMESPACENEW, namespace.namespace) - self.assertTrue(namespace.protected) - - def test_create_namespace_invalid_data(self): - properties = {} - - self.assertRaises(TypeError, self.controller.create, **properties) - - def test_create_namespace_invalid_property(self): - properties = {'namespace': 'NewNamespace', 'protected': '123'} - - self.assertRaises(TypeError, self.controller.create, **properties) - - def test_update_namespace(self): - properties = {'display_name': 'My Updated Name'} - namespace = self.controller.update(NAMESPACE1, **properties) - - self.assertEqual(NAMESPACE1, namespace.namespace) - - def test_update_namespace_invalid_property(self): - properties = {'protected': '123'} - - self.assertRaises(TypeError, self.controller.update, NAMESPACE1, - **properties) - - def test_delete_namespace(self): - self.controller.delete(NAMESPACE1) - expect = [ - ('DELETE', - '/v2/metadefs/namespaces/%s' % NAMESPACE1, - {}, - None)] - self.assertEqual(expect, self.api.calls) diff --git a/code/daisyclient/tests/v2/test_metadefs_objects.py b/code/daisyclient/tests/v2/test_metadefs_objects.py deleted file mode 100755 index 7efb9655..00000000 --- a/code/daisyclient/tests/v2/test_metadefs_objects.py +++ /dev/null @@ -1,324 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import six -import testtools - -from glanceclient.v2 import metadefs -from tests import utils - -NAMESPACE1 = 'Namespace1' -OBJECT1 = 'Object1' -OBJECT2 = 'Object2' -OBJECTNEW = 'ObjectNew' -PROPERTY1 = 'Property1' -PROPERTY2 = 'Property2' -PROPERTY3 = 'Property3' -PROPERTY4 = 'Property4' - - -def _get_object_fixture(ns_name, obj_name, **kwargs): - obj = { - "description": "DESCRIPTION", - "name": obj_name, - "self": "/v2/metadefs/namespaces/%s/objects/%s" % - (ns_name, obj_name), - "required": [], - "properties": { - PROPERTY1: { - "type": "integer", - "description": "DESCRIPTION", - "title": "Quota: CPU Shares" - }, - PROPERTY2: { - "minimum": 1000, - "type": "integer", - "description": "DESCRIPTION", - "maximum": 1000000, - "title": "Quota: CPU Period" - }}, - "schema": "/v2/schemas/metadefs/object", - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - - obj.update(kwargs) - - return obj - -data_fixtures = { - "/v2/metadefs/namespaces/%s/objects" % NAMESPACE1: { - "GET": ( - {}, - { - "objects": [ - _get_object_fixture(NAMESPACE1, OBJECT1), - _get_object_fixture(NAMESPACE1, OBJECT2) - ], - "schema": "v2/schemas/metadefs/objects" - } - ), - "POST": ( - {}, - _get_object_fixture(NAMESPACE1, OBJECTNEW) - ), - "DELETE": ( - {}, - {} - ) - }, - "/v2/metadefs/namespaces/%s/objects/%s" % (NAMESPACE1, OBJECT1): { - "GET": ( - {}, - _get_object_fixture(NAMESPACE1, OBJECT1) - ), - "PUT": ( - {}, - _get_object_fixture(NAMESPACE1, OBJECT1) - ), - "DELETE": ( - {}, - {} - ) - } -} - -schema_fixtures = { - "metadefs/object": { - "GET": ( - {}, - { - "additionalProperties": False, - "definitions": { - "property": { - "additionalProperties": { - "required": [ - "title", - "type" - ], - "type": "object", - "properties": { - "additionalItems": { - "type": "boolean" - }, - "enum": { - "type": "array" - }, - "description": { - "type": "string" - }, - "title": { - "type": "string" - }, - "default": {}, - "minLength": { - "$ref": "#/definitions/positiveInteger" - "Default0" - }, - "required": { - "$ref": "#/definitions/stringArray" - }, - "maximum": { - "type": "number" - }, - "minItems": { - "$ref": "#/definitions/positiveInteger" - "Default0" - }, - "readonly": { - "type": "boolean" - }, - "minimum": { - "type": "number" - }, - "maxItems": { - "$ref": "#/definitions/positiveInteger" - }, - "maxLength": { - "$ref": "#/definitions/positiveInteger" - }, - "uniqueItems": { - "default": False, - "type": "boolean" - }, - "pattern": { - "type": "string", - "format": "regex" - }, - "items": { - "type": "object", - "properties": { - "enum": { - "type": "array" - }, - "type": { - "enum": [ - "array", - "boolean", - "integer", - "number", - "object", - "string", - "null" - ], - "type": "string" - } - } - }, - "type": { - "enum": [ - "array", - "boolean", - "integer", - "number", - "object", - "string", - "null" - ], - "type": "string" - } - } - }, - "type": "object" - }, - "positiveIntegerDefault0": { - "allOf": [ - { - "$ref": "#/definitions/positiveInteger" - }, - { - "default": 0 - } - ] - }, - "stringArray": { - "uniqueItems": True, - "items": { - "type": "string" - }, - "type": "array" - }, - "positiveInteger": { - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "name" - ], - "name": "object", - "properties": { - "created_at": { - "type": "string", - "description": "Date and time of object creation " - "(READ-ONLY)", - "format": "date-time" - }, - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "self": { - "type": "string" - }, - "required": { - "$ref": "#/definitions/stringArray" - }, - "properties": { - "$ref": "#/definitions/property" - }, - "schema": { - "type": "string" - }, - "updated_at": { - "type": "string", - "description": "Date and time of the last object " - "modification (READ-ONLY)", - "format": "date-time" - }, - } - } - ) - } -} - - -class TestObjectController(testtools.TestCase): - - def setUp(self): - super(TestObjectController, self).setUp() - self.api = utils.FakeAPI(data_fixtures) - self.schema_api = utils.FakeSchemaAPI(schema_fixtures) - self.controller = metadefs.ObjectController(self.api, self.schema_api) - - def test_list_object(self): - objects = list(self.controller.list(NAMESPACE1)) - - actual = [obj.name for obj in objects] - self.assertEqual([OBJECT1, OBJECT2], actual) - - def test_get_object(self): - obj = self.controller.get(NAMESPACE1, OBJECT1) - self.assertEqual(OBJECT1, obj.name) - self.assertEqual(sorted([PROPERTY1, PROPERTY2]), - sorted(list(six.iterkeys(obj.properties)))) - - def test_create_object(self): - properties = { - 'name': OBJECTNEW, - 'description': 'DESCRIPTION' - } - obj = self.controller.create(NAMESPACE1, **properties) - self.assertEqual(OBJECTNEW, obj.name) - - def test_create_object_invalid_property(self): - properties = { - 'namespace': NAMESPACE1 - } - self.assertRaises(TypeError, self.controller.create, **properties) - - def test_update_object(self): - properties = { - 'description': 'UPDATED_DESCRIPTION' - } - obj = self.controller.update(NAMESPACE1, OBJECT1, **properties) - self.assertEqual(OBJECT1, obj.name) - - def test_update_object_invalid_property(self): - properties = { - 'required': 'INVALID' - } - self.assertRaises(TypeError, self.controller.update, NAMESPACE1, - OBJECT1, **properties) - - def test_delete_object(self): - self.controller.delete(NAMESPACE1, OBJECT1) - expect = [ - ('DELETE', - '/v2/metadefs/namespaces/%s/objects/%s' % (NAMESPACE1, OBJECT1), - {}, - None)] - self.assertEqual(expect, self.api.calls) - - def test_delete_all_objects(self): - self.controller.delete_all(NAMESPACE1) - expect = [ - ('DELETE', - '/v2/metadefs/namespaces/%s/objects' % NAMESPACE1, - {}, - None)] - self.assertEqual(expect, self.api.calls) diff --git a/code/daisyclient/tests/v2/test_metadefs_properties.py b/code/daisyclient/tests/v2/test_metadefs_properties.py deleted file mode 100755 index 9a3ce650..00000000 --- a/code/daisyclient/tests/v2/test_metadefs_properties.py +++ /dev/null @@ -1,301 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import testtools - -from glanceclient.v2 import metadefs -from tests import utils - -NAMESPACE1 = 'Namespace1' -PROPERTY1 = 'Property1' -PROPERTY2 = 'Property2' -PROPERTYNEW = 'PropertyNew' - -data_fixtures = { - "/v2/metadefs/namespaces/%s/properties" % NAMESPACE1: { - "GET": ( - {}, - { - "properties": { - PROPERTY1: { - "default": "1", - "type": "integer", - "description": "Number of cores.", - "title": "cores" - }, - PROPERTY2: { - "items": { - "enum": [ - "Intel", - "AMD" - ], - "type": "string" - }, - "type": "array", - "description": "Specifies the CPU manufacturer.", - "title": "Vendor" - }, - } - } - ), - "POST": ( - {}, - { - "items": { - "enum": [ - "Intel", - "AMD" - ], - "type": "string" - }, - "type": "array", - "description": "UPDATED_DESCRIPTION", - "title": "Vendor", - "name": PROPERTYNEW - } - ), - "DELETE": ( - {}, - {} - ) - }, - "/v2/metadefs/namespaces/%s/properties/%s" % (NAMESPACE1, PROPERTY1): { - "GET": ( - {}, - { - "items": { - "enum": [ - "Intel", - "AMD" - ], - "type": "string" - }, - "type": "array", - "description": "Specifies the CPU manufacturer.", - "title": "Vendor" - } - ), - "PUT": ( - {}, - { - "items": { - "enum": [ - "Intel", - "AMD" - ], - "type": "string" - }, - "type": "array", - "description": "UPDATED_DESCRIPTION", - "title": "Vendor" - } - ), - "DELETE": ( - {}, - {} - ) - } -} - -schema_fixtures = { - "metadefs/property": { - "GET": ( - {}, - { - "additionalProperties": False, - "definitions": { - "positiveIntegerDefault0": { - "allOf": [ - { - "$ref": "#/definitions/positiveInteger" - }, - { - "default": 0 - } - ] - }, - "stringArray": { - "minItems": 1, - "items": { - "type": "string" - }, - "uniqueItems": True, - "type": "array" - }, - "positiveInteger": { - "minimum": 0, - "type": "integer" - } - }, - "required": [ - "name", - "title", - "type" - ], - "name": "property", - "properties": { - "description": { - "type": "string" - }, - "minLength": { - "$ref": "#/definitions/positiveIntegerDefault0" - }, - "enum": { - "type": "array" - }, - "minimum": { - "type": "number" - }, - "maxItems": { - "$ref": "#/definitions/positiveInteger" - }, - "maxLength": { - "$ref": "#/definitions/positiveInteger" - }, - "uniqueItems": { - "default": False, - "type": "boolean" - }, - "additionalItems": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "title": { - "type": "string" - }, - "default": {}, - "pattern": { - "type": "string", - "format": "regex" - }, - "required": { - "$ref": "#/definitions/stringArray" - }, - "maximum": { - "type": "number" - }, - "minItems": { - "$ref": "#/definitions/positiveIntegerDefault0" - }, - "readonly": { - "type": "boolean" - }, - "items": { - "type": "object", - "properties": { - "enum": { - "type": "array" - }, - "type": { - "enum": [ - "array", - "boolean", - "integer", - "number", - "object", - "string", - "null" - ], - "type": "string" - } - } - }, - "type": { - "enum": [ - "array", - "boolean", - "integer", - "number", - "object", - "string", - "null" - ], - "type": "string" - } - } - } - ) - } -} - - -class TestPropertyController(testtools.TestCase): - - def setUp(self): - super(TestPropertyController, self).setUp() - self.api = utils.FakeAPI(data_fixtures) - self.schema_api = utils.FakeSchemaAPI(schema_fixtures) - self.controller = metadefs.PropertyController(self.api, - self.schema_api) - - def test_list_property(self): - properties = list(self.controller.list(NAMESPACE1)) - - actual = [prop.name for prop in properties] - self.assertEqual(sorted([PROPERTY1, PROPERTY2]), sorted(actual)) - - def test_get_property(self): - prop = self.controller.get(NAMESPACE1, PROPERTY1) - self.assertEqual(PROPERTY1, prop.name) - - def test_create_property(self): - properties = { - 'name': PROPERTYNEW, - 'title': 'TITLE', - 'type': 'string' - } - obj = self.controller.create(NAMESPACE1, **properties) - self.assertEqual(PROPERTYNEW, obj.name) - - def test_create_property_invalid_property(self): - properties = { - 'namespace': NAMESPACE1 - } - self.assertRaises(TypeError, self.controller.create, **properties) - - def test_update_property(self): - properties = { - 'description': 'UPDATED_DESCRIPTION' - } - prop = self.controller.update(NAMESPACE1, PROPERTY1, **properties) - self.assertEqual(PROPERTY1, prop.name) - - def test_update_property_invalid_property(self): - properties = { - 'type': 'INVALID' - } - self.assertRaises(TypeError, self.controller.update, NAMESPACE1, - PROPERTY1, **properties) - - def test_delete_property(self): - self.controller.delete(NAMESPACE1, PROPERTY1) - expect = [ - ('DELETE', - '/v2/metadefs/namespaces/%s/properties/%s' % (NAMESPACE1, - PROPERTY1), - {}, - None)] - self.assertEqual(expect, self.api.calls) - - def test_delete_all_properties(self): - self.controller.delete_all(NAMESPACE1) - expect = [ - ('DELETE', - '/v2/metadefs/namespaces/%s/properties' % NAMESPACE1, - {}, - None)] - self.assertEqual(expect, self.api.calls) diff --git a/code/daisyclient/tests/v2/test_metadefs_resource_types.py b/code/daisyclient/tests/v2/test_metadefs_resource_types.py deleted file mode 100755 index b10614d9..00000000 --- a/code/daisyclient/tests/v2/test_metadefs_resource_types.py +++ /dev/null @@ -1,187 +0,0 @@ -# Copyright 2012 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import testtools - -from glanceclient.v2 import metadefs -from tests import utils - -NAMESPACE1 = 'Namespace1' -RESOURCE_TYPE1 = 'ResourceType1' -RESOURCE_TYPE2 = 'ResourceType2' -RESOURCE_TYPE3 = 'ResourceType3' -RESOURCE_TYPE4 = 'ResourceType4' -RESOURCE_TYPENEW = 'ResourceTypeNew' - - -data_fixtures = { - "/v2/metadefs/namespaces/%s/resource_types" % NAMESPACE1: { - "GET": ( - {}, - { - "resource_type_associations": [ - { - "name": RESOURCE_TYPE3, - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - }, - { - "name": RESOURCE_TYPE4, - "prefix": "PREFIX:", - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - ] - } - ), - "POST": ( - {}, - { - "name": RESOURCE_TYPENEW, - "prefix": "PREFIX:", - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - ), - }, - "/v2/metadefs/namespaces/%s/resource_types/%s" % (NAMESPACE1, - RESOURCE_TYPE1): - { - "DELETE": ( - {}, - {} - ), - }, - "/v2/metadefs/resource_types": { - "GET": ( - {}, - { - "resource_types": [ - { - "name": RESOURCE_TYPE1, - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - }, - { - "name": RESOURCE_TYPE2, - "created_at": "2014-08-14T09:07:06Z", - "updated_at": "2014-08-14T09:07:06Z", - } - ] - } - ) - } -} - -schema_fixtures = { - "metadefs/resource_type": { - "GET": ( - {}, - { - "name": "resource_type", - "properties": { - "prefix": { - "type": "string", - "description": "Specifies the prefix to use for the " - "given resource type. Any properties " - "in the namespace should be prefixed " - "with this prefix when being applied " - "to the specified resource type. Must " - "include prefix separator (e.g. a " - "colon :).", - "maxLength": 80 - }, - "properties_target": { - "type": "string", - "description": "Some resource types allow more than " - "one key / value pair per instance. " - "For example, Cinder allows user and " - "image metadata on volumes. Only the " - "image properties metadata is " - "evaluated by Nova (scheduling or " - "drivers). This property allows a " - "namespace target to remove the " - "ambiguity.", - "maxLength": 80 - }, - "name": { - "type": "string", - "description": "Resource type names should be " - "aligned with Heat resource types " - "whenever possible: http://docs." - "openstack.org/developer/heat/" - "template_guide/openstack.html", - "maxLength": 80 - }, - "created_at": { - "type": "string", - "description": "Date and time of resource type " - "association (READ-ONLY)", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "description": "Date and time of the last resource " - "type association modification " - "(READ-ONLY)", - "format": "date-time" - }, - } - } - ) - } -} - - -class TestResoureTypeController(testtools.TestCase): - - def setUp(self): - super(TestResoureTypeController, self).setUp() - self.api = utils.FakeAPI(data_fixtures) - self.schema_api = utils.FakeSchemaAPI(schema_fixtures) - self.controller = metadefs.ResourceTypeController(self.api, - self.schema_api) - - def test_list_resource_types(self): - resource_types = list(self.controller.list()) - names = [rt.name for rt in resource_types] - self.assertEqual([RESOURCE_TYPE1, RESOURCE_TYPE2], names) - - def test_get_resource_types(self): - resource_types = list(self.controller.get(NAMESPACE1)) - names = [rt.name for rt in resource_types] - self.assertEqual([RESOURCE_TYPE3, RESOURCE_TYPE4], names) - - def test_associate_resource_types(self): - resource_types = self.controller.associate(NAMESPACE1, - name=RESOURCE_TYPENEW) - - self.assertEqual(RESOURCE_TYPENEW, resource_types['name']) - - def test_associate_resource_types_invalid_property(self): - longer = '1234' * 50 - properties = {'name': RESOURCE_TYPENEW, 'prefix': longer} - self.assertRaises(TypeError, self.controller.associate, NAMESPACE1, - **properties) - - def test_deassociate_resource_types(self): - self.controller.deassociate(NAMESPACE1, RESOURCE_TYPE1) - expect = [ - ('DELETE', - '/v2/metadefs/namespaces/%s/resource_types/%s' % (NAMESPACE1, - RESOURCE_TYPE1), - {}, - None)] - self.assertEqual(expect, self.api.calls) diff --git a/code/daisyclient/tests/v2/test_schemas.py b/code/daisyclient/tests/v2/test_schemas.py deleted file mode 100755 index d274e93f..00000000 --- a/code/daisyclient/tests/v2/test_schemas.py +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright 2012 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from jsonpatch import JsonPatch -import testtools -import warlock - -from glanceclient.v2 import schemas -from tests import utils - - -fixtures = { - '/v2/schemas': { - 'GET': ( - {}, - { - 'image': '/v2/schemas/image', - 'access': '/v2/schemas/image/access', - }, - ), - }, - '/v2/schemas/image': { - 'GET': ( - {}, - { - 'name': 'image', - 'properties': { - 'name': {'type': 'string', - 'description': 'Name of image'}, - 'tags': {'type': 'array'} - }, - - }, - ), - }, -} - - -_SCHEMA = schemas.Schema({ - 'name': 'image', - 'properties': { - 'name': {'type': 'string'}, - 'color': {'type': 'string'}, - 'shape': {'type': 'string', 'is_base': False}, - 'tags': {'type': 'array'} - }, -}) - - -def compare_json_patches(a, b): - """Return 0 if a and b describe the same JSON patch.""" - return JsonPatch.from_string(a) == JsonPatch.from_string(b) - - -class TestSchemaProperty(testtools.TestCase): - - def test_property_minimum(self): - prop = schemas.SchemaProperty('size') - self.assertEqual('size', prop.name) - - def test_property_description(self): - prop = schemas.SchemaProperty('size', description='some quantity') - self.assertEqual('size', prop.name) - self.assertEqual('some quantity', prop.description) - - -class TestSchema(testtools.TestCase): - - def test_schema_minimum(self): - raw_schema = {'name': 'Country', 'properties': {}} - schema = schemas.Schema(raw_schema) - self.assertEqual('Country', schema.name) - self.assertEqual([], schema.properties) - - def test_schema_with_property(self): - raw_schema = {'name': 'Country', 'properties': {'size': {}}} - schema = schemas.Schema(raw_schema) - self.assertEqual('Country', schema.name) - self.assertEqual(['size'], [p.name for p in schema.properties]) - - def test_raw(self): - raw_schema = {'name': 'Country', 'properties': {}} - schema = schemas.Schema(raw_schema) - self.assertEqual(raw_schema, schema.raw()) - - -class TestController(testtools.TestCase): - - def setUp(self): - super(TestController, self).setUp() - self.api = utils.FakeAPI(fixtures) - self.controller = schemas.Controller(self.api) - - def test_get_schema(self): - schema = self.controller.get('image') - self.assertEqual('image', schema.name) - self.assertEqual(['name', 'tags'], - [p.name for p in schema.properties]) - - -class TestSchemaBasedModel(testtools.TestCase): - - def setUp(self): - super(TestSchemaBasedModel, self).setUp() - self.model = warlock.model_factory(_SCHEMA.raw(), - schemas.SchemaBasedModel) - - def test_patch_should_replace_missing_core_properties(self): - obj = { - 'name': 'fred' - } - - original = self.model(obj) - original['color'] = 'red' - - patch = original.patch - expected = '[{"path": "/color", "value": "red", "op": "replace"}]' - self.assertTrue(compare_json_patches(patch, expected)) - - def test_patch_should_add_extra_properties(self): - obj = { - 'name': 'fred', - } - - original = self.model(obj) - original['weight'] = '10' - - patch = original.patch - expected = '[{"path": "/weight", "value": "10", "op": "add"}]' - self.assertTrue(compare_json_patches(patch, expected)) - - def test_patch_should_replace_extra_properties(self): - obj = { - 'name': 'fred', - 'weight': '10' - } - - original = self.model(obj) - original['weight'] = '22' - - patch = original.patch - expected = '[{"path": "/weight", "value": "22", "op": "replace"}]' - self.assertTrue(compare_json_patches(patch, expected)) - - def test_patch_should_remove_extra_properties(self): - obj = { - 'name': 'fred', - 'weight': '10' - } - - original = self.model(obj) - del original['weight'] - - patch = original.patch - expected = '[{"path": "/weight", "op": "remove"}]' - self.assertTrue(compare_json_patches(patch, expected)) - - def test_patch_should_remove_core_properties(self): - obj = { - 'name': 'fred', - 'color': 'red' - } - - original = self.model(obj) - del original['color'] - - patch = original.patch - expected = '[{"path": "/color", "op": "remove"}]' - self.assertTrue(compare_json_patches(patch, expected)) - - def test_patch_should_add_missing_custom_properties(self): - obj = { - 'name': 'fred' - } - - original = self.model(obj) - original['shape'] = 'circle' - - patch = original.patch - expected = '[{"path": "/shape", "value": "circle", "op": "add"}]' - self.assertTrue(compare_json_patches(patch, expected)) - - def test_patch_should_replace_custom_properties(self): - obj = { - 'name': 'fred', - 'shape': 'circle' - } - - original = self.model(obj) - original['shape'] = 'square' - - patch = original.patch - expected = '[{"path": "/shape", "value": "square", "op": "replace"}]' - self.assertTrue(compare_json_patches(patch, expected)) - - def test_patch_should_replace_tags(self): - obj = {'name': 'fred', } - - original = self.model(obj) - original['tags'] = ['tag1', 'tag2'] - - patch = original.patch - expected = '[{"path": "/tags", "value": ["tag1", "tag2"], ' \ - '"op": "replace"}]' - self.assertTrue(compare_json_patches(patch, expected)) diff --git a/code/daisyclient/tests/v2/test_shell_v2.py b/code/daisyclient/tests/v2/test_shell_v2.py deleted file mode 100755 index 798ac420..00000000 --- a/code/daisyclient/tests/v2/test_shell_v2.py +++ /dev/null @@ -1,1075 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# Copyright (C) 2013 Yahoo! Inc. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -import json -import mock -import os -import tempfile -import testtools - -from glanceclient.common import utils -from glanceclient.v2 import shell as test_shell - - -class ShellV2Test(testtools.TestCase): - - def setUp(self): - super(ShellV2Test, self).setUp() - self._mock_utils() - self.gc = self._mock_glance_client() - - def _make_args(self, args): - # NOTE(venkatesh): this conversion from a dict to an object - # is required because the test_shell.do_xxx(gc, args) methods - # expects the args to be attributes of an object. If passed as - # dict directly, it throws an AttributeError. - class Args(): - - def __init__(self, entries): - self.__dict__.update(entries) - - return Args(args) - - def _mock_glance_client(self): - my_mocked_gc = mock.Mock() - my_mocked_gc.schemas.return_value = 'test' - my_mocked_gc.get.return_value = {} - return my_mocked_gc - - def _mock_utils(self): - utils.print_list = mock.Mock() - utils.print_dict = mock.Mock() - utils.save_image = mock.Mock() - - def assert_exits_with_msg(self, func, func_args, err_msg): - with mock.patch.object(utils, 'exit') as mocked_utils_exit: - mocked_utils_exit.return_value = '%s' % err_msg - - func(self.gc, func_args) - - mocked_utils_exit.assert_called_once_with(err_msg) - - def test_do_image_list(self): - input = { - 'limit': None, - 'page_size': 18, - 'visibility': True, - 'member_status': 'Fake', - 'owner': 'test', - 'checksum': 'fake_checksum', - 'tag': 'fake tag', - 'properties': [], - 'sort_key': ['name', 'id'], - 'sort_dir': ['desc', 'asc'], - 'sort': None - } - args = self._make_args(input) - with mock.patch.object(self.gc.images, 'list') as mocked_list: - mocked_list.return_value = {} - - test_shell.do_image_list(self.gc, args) - - exp_img_filters = { - 'owner': 'test', - 'member_status': 'Fake', - 'visibility': True, - 'checksum': 'fake_checksum', - 'tag': 'fake tag' - } - mocked_list.assert_called_once_with(page_size=18, - sort_key=['name', 'id'], - sort_dir=['desc', 'asc'], - filters=exp_img_filters) - utils.print_list.assert_called_once_with({}, ['ID', 'Name']) - - def test_do_image_list_with_single_sort_key(self): - input = { - 'limit': None, - 'page_size': 18, - 'visibility': True, - 'member_status': 'Fake', - 'owner': 'test', - 'checksum': 'fake_checksum', - 'tag': 'fake tag', - 'properties': [], - 'sort_key': ['name'], - 'sort_dir': ['desc'], - 'sort': None - } - args = self._make_args(input) - with mock.patch.object(self.gc.images, 'list') as mocked_list: - mocked_list.return_value = {} - - test_shell.do_image_list(self.gc, args) - - exp_img_filters = { - 'owner': 'test', - 'member_status': 'Fake', - 'visibility': True, - 'checksum': 'fake_checksum', - 'tag': 'fake tag' - } - mocked_list.assert_called_once_with(page_size=18, - sort_key=['name'], - sort_dir=['desc'], - filters=exp_img_filters) - utils.print_list.assert_called_once_with({}, ['ID', 'Name']) - - def test_do_image_list_new_sorting_syntax(self): - input = { - 'limit': None, - 'page_size': 18, - 'visibility': True, - 'member_status': 'Fake', - 'owner': 'test', - 'checksum': 'fake_checksum', - 'tag': 'fake tag', - 'properties': [], - 'sort': 'name:desc,size:asc', - 'sort_key': [], - 'sort_dir': [] - } - args = self._make_args(input) - with mock.patch.object(self.gc.images, 'list') as mocked_list: - mocked_list.return_value = {} - - test_shell.do_image_list(self.gc, args) - - exp_img_filters = { - 'owner': 'test', - 'member_status': 'Fake', - 'visibility': True, - 'checksum': 'fake_checksum', - 'tag': 'fake tag' - } - mocked_list.assert_called_once_with( - page_size=18, - sort='name:desc,size:asc', - filters=exp_img_filters) - utils.print_list.assert_called_once_with({}, ['ID', 'Name']) - - def test_do_image_list_with_property_filter(self): - input = { - 'limit': None, - 'page_size': 1, - 'visibility': True, - 'member_status': 'Fake', - 'owner': 'test', - 'checksum': 'fake_checksum', - 'tag': 'fake tag', - 'properties': ['os_distro=NixOS', 'architecture=x86_64'], - 'sort_key': ['name'], - 'sort_dir': ['desc'], - 'sort': None - } - args = self._make_args(input) - with mock.patch.object(self.gc.images, 'list') as mocked_list: - mocked_list.return_value = {} - - test_shell.do_image_list(self.gc, args) - - exp_img_filters = { - 'owner': 'test', - 'member_status': 'Fake', - 'visibility': True, - 'checksum': 'fake_checksum', - 'tag': 'fake tag', - 'os_distro': 'NixOS', - 'architecture': 'x86_64' - } - - mocked_list.assert_called_once_with(page_size=1, - sort_key=['name'], - sort_dir=['desc'], - filters=exp_img_filters) - utils.print_list.assert_called_once_with({}, ['ID', 'Name']) - - def test_do_image_show(self): - args = self._make_args({'id': 'pass', 'page_size': 18, - 'max_column_width': 120}) - with mock.patch.object(self.gc.images, 'get') as mocked_list: - ignore_fields = ['self', 'access', 'file', 'schema'] - expect_image = dict([(field, field) for field in ignore_fields]) - expect_image['id'] = 'pass' - mocked_list.return_value = expect_image - - test_shell.do_image_show(self.gc, args) - - mocked_list.assert_called_once_with('pass') - utils.print_dict.assert_called_once_with({'id': 'pass'}, - max_column_width=120) - - @mock.patch('sys.stdin', autospec=True) - def test_do_image_create_no_user_props(self, mock_stdin): - args = self._make_args({'name': 'IMG-01', 'disk_format': 'vhd', - 'container_format': 'bare', - 'file': None}) - with mock.patch.object(self.gc.images, 'create') as mocked_create: - ignore_fields = ['self', 'access', 'file', 'schema'] - expect_image = dict([(field, field) for field in ignore_fields]) - expect_image['id'] = 'pass' - expect_image['name'] = 'IMG-01' - expect_image['disk_format'] = 'vhd' - expect_image['container_format'] = 'bare' - mocked_create.return_value = expect_image - - # Ensure that the test stdin is not considered - # to be supplying image data - mock_stdin.isatty = lambda: True - test_shell.do_image_create(self.gc, args) - - mocked_create.assert_called_once_with(name='IMG-01', - disk_format='vhd', - container_format='bare') - utils.print_dict.assert_called_once_with({ - 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', - 'container_format': 'bare'}) - - def test_do_image_create_with_file(self): - try: - file_name = None - with open(tempfile.mktemp(), 'w+') as f: - f.write('Some data here') - f.flush() - f.seek(0) - file_name = f.name - temp_args = {'name': 'IMG-01', - 'disk_format': 'vhd', - 'container_format': 'bare', - 'file': file_name, - 'progress': False} - args = self._make_args(temp_args) - with mock.patch.object(self.gc.images, 'create') as mocked_create: - with mock.patch.object(self.gc.images, 'get') as mocked_get: - - ignore_fields = ['self', 'access', 'schema'] - expect_image = dict([(field, field) for field in - ignore_fields]) - expect_image['id'] = 'pass' - expect_image['name'] = 'IMG-01' - expect_image['disk_format'] = 'vhd' - expect_image['container_format'] = 'bare' - mocked_create.return_value = expect_image - mocked_get.return_value = expect_image - - test_shell.do_image_create(self.gc, args) - - temp_args.pop('file', None) - mocked_create.assert_called_once_with(**temp_args) - mocked_get.assert_called_once_with('pass') - utils.print_dict.assert_called_once_with({ - 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', - 'container_format': 'bare'}) - finally: - try: - os.remove(f.name) - except Exception: - pass - - @mock.patch('sys.stdin', autospec=True) - def test_do_image_create_with_user_props(self, mock_stdin): - args = self._make_args({'name': 'IMG-01', - 'property': ['myprop=myval'], - 'file': None}) - with mock.patch.object(self.gc.images, 'create') as mocked_create: - ignore_fields = ['self', 'access', 'file', 'schema'] - expect_image = dict([(field, field) for field in ignore_fields]) - expect_image['id'] = 'pass' - expect_image['name'] = 'IMG-01' - expect_image['myprop'] = 'myval' - mocked_create.return_value = expect_image - - # Ensure that the test stdin is not considered - # to be supplying image data - mock_stdin.isatty = lambda: True - test_shell.do_image_create(self.gc, args) - - mocked_create.assert_called_once_with(name='IMG-01', - myprop='myval') - utils.print_dict.assert_called_once_with({ - 'id': 'pass', 'name': 'IMG-01', 'myprop': 'myval'}) - - def test_do_image_update_no_user_props(self): - args = self._make_args({'id': 'pass', 'name': 'IMG-01', - 'disk_format': 'vhd', - 'container_format': 'bare'}) - with mock.patch.object(self.gc.images, 'update') as mocked_update: - ignore_fields = ['self', 'access', 'file', 'schema'] - expect_image = dict([(field, field) for field in ignore_fields]) - expect_image['id'] = 'pass' - expect_image['name'] = 'IMG-01' - expect_image['disk_format'] = 'vhd' - expect_image['container_format'] = 'bare' - mocked_update.return_value = expect_image - - test_shell.do_image_update(self.gc, args) - - mocked_update.assert_called_once_with('pass', - None, - name='IMG-01', - disk_format='vhd', - container_format='bare') - utils.print_dict.assert_called_once_with({ - 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd', - 'container_format': 'bare'}) - - def test_do_image_update_with_user_props(self): - args = self._make_args({'id': 'pass', 'name': 'IMG-01', - 'property': ['myprop=myval']}) - with mock.patch.object(self.gc.images, 'update') as mocked_update: - ignore_fields = ['self', 'access', 'file', 'schema'] - expect_image = dict([(field, field) for field in ignore_fields]) - expect_image['id'] = 'pass' - expect_image['name'] = 'IMG-01' - expect_image['myprop'] = 'myval' - mocked_update.return_value = expect_image - - test_shell.do_image_update(self.gc, args) - - mocked_update.assert_called_once_with('pass', - None, - name='IMG-01', - myprop='myval') - utils.print_dict.assert_called_once_with({ - 'id': 'pass', 'name': 'IMG-01', 'myprop': 'myval'}) - - def test_do_image_update_with_remove_props(self): - args = self._make_args({'id': 'pass', 'name': 'IMG-01', - 'disk_format': 'vhd', - 'remove-property': ['container_format']}) - with mock.patch.object(self.gc.images, 'update') as mocked_update: - ignore_fields = ['self', 'access', 'file', 'schema'] - expect_image = dict([(field, field) for field in ignore_fields]) - expect_image['id'] = 'pass' - expect_image['name'] = 'IMG-01' - expect_image['disk_format'] = 'vhd' - - mocked_update.return_value = expect_image - - test_shell.do_image_update(self.gc, args) - - mocked_update.assert_called_once_with('pass', - ['container_format'], - name='IMG-01', - disk_format='vhd') - utils.print_dict.assert_called_once_with({ - 'id': 'pass', 'name': 'IMG-01', 'disk_format': 'vhd'}) - - def test_do_explain(self): - input = { - 'page_size': 18, - 'id': 'pass', - 'schemas': 'test', - 'model': 'test', - } - args = self._make_args(input) - with mock.patch.object(utils, 'print_list'): - test_shell.do_explain(self.gc, args) - - self.gc.schemas.get.assert_called_once_with('test') - - def test_do_location_add(self): - gc = self.gc - loc = {'url': 'http://foo.com/', 'metadata': {'foo': 'bar'}} - args = self._make_args({'id': 'pass', - 'url': loc['url'], - 'metadata': json.dumps(loc['metadata'])}) - with mock.patch.object(gc.images, 'add_location') as mocked_addloc: - expect_image = {'id': 'pass', 'locations': [loc]} - mocked_addloc.return_value = expect_image - - test_shell.do_location_add(self.gc, args) - mocked_addloc.assert_called_once_with('pass', - loc['url'], - loc['metadata']) - utils.print_dict.assert_called_once_with(expect_image) - - def test_do_location_delete(self): - gc = self.gc - loc_set = set(['http://foo/bar', 'http://spam/ham']) - args = self._make_args({'id': 'pass', 'url': loc_set}) - - with mock.patch.object(gc.images, 'delete_locations') as mocked_rmloc: - test_shell.do_location_delete(self.gc, args) - mocked_rmloc.assert_called_once_with('pass', loc_set) - - def test_do_location_update(self): - gc = self.gc - loc = {'url': 'http://foo.com/', 'metadata': {'foo': 'bar'}} - args = self._make_args({'id': 'pass', - 'url': loc['url'], - 'metadata': json.dumps(loc['metadata'])}) - with mock.patch.object(gc.images, 'update_location') as mocked_modloc: - expect_image = {'id': 'pass', 'locations': [loc]} - mocked_modloc.return_value = expect_image - - test_shell.do_location_update(self.gc, args) - mocked_modloc.assert_called_once_with('pass', - loc['url'], - loc['metadata']) - utils.print_dict.assert_called_once_with(expect_image) - - def test_image_upload(self): - args = self._make_args( - {'id': 'IMG-01', 'file': 'test', 'size': 1024, 'progress': False}) - - with mock.patch.object(self.gc.images, 'upload') as mocked_upload: - utils.get_data_file = mock.Mock(return_value='testfile') - mocked_upload.return_value = None - test_shell.do_image_upload(self.gc, args) - mocked_upload.assert_called_once_with('IMG-01', 'testfile', 1024) - - def test_image_download(self): - args = self._make_args( - {'id': 'IMG-01', 'file': 'test', 'progress': True}) - - with mock.patch.object(self.gc.images, 'data') as mocked_data: - def _data(): - for c in 'abcedf': - yield c - mocked_data.return_value = utils.IterableWithLength(_data(), 5) - - test_shell.do_image_download(self.gc, args) - mocked_data.assert_called_once_with('IMG-01') - - def test_do_image_delete(self): - args = self._make_args({'id': 'pass', 'file': 'test'}) - with mock.patch.object(self.gc.images, 'delete') as mocked_delete: - mocked_delete.return_value = 0 - - test_shell.do_image_delete(self.gc, args) - - mocked_delete.assert_called_once_with('pass') - - def test_do_image_delete_deleted(self): - image_id = 'deleted-img' - args = self._make_args({'id': image_id}) - with mock.patch.object(self.gc.images, 'get') as mocked_get: - mocked_get.return_value = self._make_args({'id': image_id, - 'status': 'deleted'}) - - msg = "No image with an ID of '%s' exists." % image_id - self.assert_exits_with_msg(func=test_shell.do_image_delete, - func_args=args, - err_msg=msg) - - def test_do_member_list(self): - args = self._make_args({'image_id': 'IMG-01'}) - with mock.patch.object(self.gc.image_members, 'list') as mocked_list: - mocked_list.return_value = {} - - test_shell.do_member_list(self.gc, args) - - mocked_list.assert_called_once_with('IMG-01') - columns = ['Image ID', 'Member ID', 'Status'] - utils.print_list.assert_called_once_with({}, columns) - - def test_do_member_create(self): - args = self._make_args({'image_id': 'IMG-01', 'member_id': 'MEM-01'}) - with mock.patch.object(self.gc.image_members, 'create') as mock_create: - mock_create.return_value = {} - - test_shell.do_member_create(self.gc, args) - - mock_create.assert_called_once_with('IMG-01', 'MEM-01') - columns = ['Image ID', 'Member ID', 'Status'] - utils.print_list.assert_called_once_with([{}], columns) - - def test_do_member_create_with_few_arguments(self): - args = self._make_args({'image_id': None, 'member_id': 'MEM-01'}) - msg = 'Unable to create member. Specify image_id and member_id' - - self.assert_exits_with_msg(func=test_shell.do_member_create, - func_args=args, - err_msg=msg) - - def test_do_member_update(self): - input = { - 'image_id': 'IMG-01', - 'member_id': 'MEM-01', - 'member_status': 'status', - } - args = self._make_args(input) - with mock.patch.object(self.gc.image_members, 'update') as mock_update: - mock_update.return_value = {} - - test_shell.do_member_update(self.gc, args) - - mock_update.assert_called_once_with('IMG-01', 'MEM-01', 'status') - columns = ['Image ID', 'Member ID', 'Status'] - utils.print_list.assert_called_once_with([{}], columns) - - def test_do_member_update_with_few_arguments(self): - input = { - 'image_id': 'IMG-01', - 'member_id': 'MEM-01', - 'member_status': None, - } - args = self._make_args(input) - msg = 'Unable to update member. Specify image_id, member_id' \ - ' and member_status' - - self.assert_exits_with_msg(func=test_shell.do_member_update, - func_args=args, - err_msg=msg) - - def test_do_member_delete(self): - args = self._make_args({'image_id': 'IMG-01', 'member_id': 'MEM-01'}) - with mock.patch.object(self.gc.image_members, 'delete') as mock_delete: - test_shell.do_member_delete(self.gc, args) - - mock_delete.assert_called_once_with('IMG-01', 'MEM-01') - - def test_do_member_delete_with_few_arguments(self): - args = self._make_args({'image_id': None, 'member_id': 'MEM-01'}) - msg = 'Unable to delete member. Specify image_id and member_id' - - self.assert_exits_with_msg(func=test_shell.do_member_delete, - func_args=args, - err_msg=msg) - - def test_image_tag_update(self): - args = self._make_args({'image_id': 'IMG-01', 'tag_value': 'tag01'}) - with mock.patch.object(self.gc.image_tags, 'update') as mocked_update: - self.gc.images.get = mock.Mock(return_value={}) - mocked_update.return_value = None - - test_shell.do_image_tag_update(self.gc, args) - - mocked_update.assert_called_once_with('IMG-01', 'tag01') - - def test_image_tag_update_with_few_arguments(self): - args = self._make_args({'image_id': None, 'tag_value': 'tag01'}) - msg = 'Unable to update tag. Specify image_id and tag_value' - - self.assert_exits_with_msg(func=test_shell.do_image_tag_update, - func_args=args, - err_msg=msg) - - def test_image_tag_delete(self): - args = self._make_args({'image_id': 'IMG-01', 'tag_value': 'tag01'}) - with mock.patch.object(self.gc.image_tags, 'delete') as mocked_delete: - mocked_delete.return_value = None - - test_shell.do_image_tag_delete(self.gc, args) - - mocked_delete.assert_called_once_with('IMG-01', 'tag01') - - def test_image_tag_delete_with_few_arguments(self): - args = self._make_args({'image_id': 'IMG-01', 'tag_value': None}) - msg = 'Unable to delete tag. Specify image_id and tag_value' - - self.assert_exits_with_msg(func=test_shell.do_image_tag_delete, - func_args=args, - err_msg=msg) - - def test_do_md_namespace_create(self): - args = self._make_args({'namespace': 'MyNamespace', - 'protected': True}) - with mock.patch.object(self.gc.metadefs_namespace, - 'create') as mocked_create: - expect_namespace = {} - expect_namespace['namespace'] = 'MyNamespace' - expect_namespace['protected'] = True - - mocked_create.return_value = expect_namespace - - test_shell.do_md_namespace_create(self.gc, args) - - mocked_create.assert_called_once_with(namespace='MyNamespace', - protected=True) - utils.print_dict.assert_called_once_with(expect_namespace) - - def test_do_md_namespace_import(self): - args = self._make_args({'file': 'test'}) - - expect_namespace = {} - expect_namespace['namespace'] = 'MyNamespace' - expect_namespace['protected'] = True - - with mock.patch.object(self.gc.metadefs_namespace, - 'create') as mocked_create: - mock_read = mock.Mock(return_value=json.dumps(expect_namespace)) - mock_file = mock.Mock(read=mock_read) - utils.get_data_file = mock.Mock(return_value=mock_file) - mocked_create.return_value = expect_namespace - - test_shell.do_md_namespace_import(self.gc, args) - - mocked_create.assert_called_once_with(**expect_namespace) - utils.print_dict.assert_called_once_with(expect_namespace) - - def test_do_md_namespace_import_invalid_json(self): - args = self._make_args({'file': 'test'}) - mock_read = mock.Mock(return_value='Invalid') - mock_file = mock.Mock(read=mock_read) - utils.get_data_file = mock.Mock(return_value=mock_file) - - self.assertRaises(SystemExit, test_shell.do_md_namespace_import, - self.gc, args) - - def test_do_md_namespace_import_no_input(self): - args = self._make_args({'file': None}) - utils.get_data_file = mock.Mock(return_value=None) - - self.assertRaises(SystemExit, test_shell.do_md_namespace_import, - self.gc, args) - - def test_do_md_namespace_update(self): - args = self._make_args({'id': 'MyNamespace', - 'protected': True}) - with mock.patch.object(self.gc.metadefs_namespace, - 'update') as mocked_update: - expect_namespace = {} - expect_namespace['namespace'] = 'MyNamespace' - expect_namespace['protected'] = True - - mocked_update.return_value = expect_namespace - - test_shell.do_md_namespace_update(self.gc, args) - - mocked_update.assert_called_once_with('MyNamespace', - id='MyNamespace', - protected=True) - utils.print_dict.assert_called_once_with(expect_namespace) - - def test_do_md_namespace_show(self): - args = self._make_args({'namespace': 'MyNamespace', - 'max_column_width': 80, - 'resource_type': None}) - with mock.patch.object(self.gc.metadefs_namespace, - 'get') as mocked_get: - expect_namespace = {} - expect_namespace['namespace'] = 'MyNamespace' - - mocked_get.return_value = expect_namespace - - test_shell.do_md_namespace_show(self.gc, args) - - mocked_get.assert_called_once_with('MyNamespace') - utils.print_dict.assert_called_once_with(expect_namespace, 80) - - def test_do_md_namespace_show_resource_type(self): - args = self._make_args({'namespace': 'MyNamespace', - 'max_column_width': 80, - 'resource_type': 'RESOURCE'}) - with mock.patch.object(self.gc.metadefs_namespace, - 'get') as mocked_get: - expect_namespace = {} - expect_namespace['namespace'] = 'MyNamespace' - - mocked_get.return_value = expect_namespace - - test_shell.do_md_namespace_show(self.gc, args) - - mocked_get.assert_called_once_with('MyNamespace', - resource_type='RESOURCE') - utils.print_dict.assert_called_once_with(expect_namespace, 80) - - def test_do_md_namespace_list(self): - args = self._make_args({'resource_type': None, - 'visibility': None, - 'page_size': None}) - with mock.patch.object(self.gc.metadefs_namespace, - 'list') as mocked_list: - expect_namespaces = [{'namespace': 'MyNamespace'}] - - mocked_list.return_value = expect_namespaces - - test_shell.do_md_namespace_list(self.gc, args) - - mocked_list.assert_called_once_with(filters={}) - utils.print_list.assert_called_once_with(expect_namespaces, - ['namespace']) - - def test_do_md_namespace_list_page_size(self): - args = self._make_args({'resource_type': None, - 'visibility': None, - 'page_size': 2}) - with mock.patch.object(self.gc.metadefs_namespace, - 'list') as mocked_list: - expect_namespaces = [{'namespace': 'MyNamespace'}] - - mocked_list.return_value = expect_namespaces - - test_shell.do_md_namespace_list(self.gc, args) - - mocked_list.assert_called_once_with(filters={}, page_size=2) - utils.print_list.assert_called_once_with(expect_namespaces, - ['namespace']) - - def test_do_md_namespace_list_one_filter(self): - args = self._make_args({'resource_types': ['OS::Compute::Aggregate'], - 'visibility': None, - 'page_size': None}) - with mock.patch.object(self.gc.metadefs_namespace, 'list') as \ - mocked_list: - expect_namespaces = [{'namespace': 'MyNamespace'}] - - mocked_list.return_value = expect_namespaces - - test_shell.do_md_namespace_list(self.gc, args) - - mocked_list.assert_called_once_with(filters={ - 'resource_types': ['OS::Compute::Aggregate']}) - utils.print_list.assert_called_once_with(expect_namespaces, - ['namespace']) - - def test_do_md_namespace_list_all_filters(self): - args = self._make_args({'resource_types': ['OS::Compute::Aggregate'], - 'visibility': 'public', - 'page_size': None}) - with mock.patch.object(self.gc.metadefs_namespace, - 'list') as mocked_list: - expect_namespaces = [{'namespace': 'MyNamespace'}] - - mocked_list.return_value = expect_namespaces - - test_shell.do_md_namespace_list(self.gc, args) - - mocked_list.assert_called_once_with(filters={ - 'resource_types': ['OS::Compute::Aggregate'], - 'visibility': 'public'}) - utils.print_list.assert_called_once_with(expect_namespaces, - ['namespace']) - - def test_do_md_namespace_list_unknown_filter(self): - args = self._make_args({'resource_type': None, - 'visibility': None, - 'some_arg': 'some_value', - 'page_size': None}) - with mock.patch.object(self.gc.metadefs_namespace, - 'list') as mocked_list: - expect_namespaces = [{'namespace': 'MyNamespace'}] - - mocked_list.return_value = expect_namespaces - - test_shell.do_md_namespace_list(self.gc, args) - - mocked_list.assert_called_once_with(filters={}) - utils.print_list.assert_called_once_with(expect_namespaces, - ['namespace']) - - def test_do_md_namespace_delete(self): - args = self._make_args({'namespace': 'MyNamespace', - 'content': False}) - with mock.patch.object(self.gc.metadefs_namespace, 'delete') as \ - mocked_delete: - test_shell.do_md_namespace_delete(self.gc, args) - - mocked_delete.assert_called_once_with('MyNamespace') - - def test_do_md_resource_type_associate(self): - args = self._make_args({'namespace': 'MyNamespace', - 'name': 'MyResourceType', - 'prefix': 'PREFIX:'}) - with mock.patch.object(self.gc.metadefs_resource_type, - 'associate') as mocked_associate: - expect_rt = {} - expect_rt['namespace'] = 'MyNamespace' - expect_rt['name'] = 'MyResourceType' - expect_rt['prefix'] = 'PREFIX:' - - mocked_associate.return_value = expect_rt - - test_shell.do_md_resource_type_associate(self.gc, args) - - mocked_associate.assert_called_once_with('MyNamespace', - **expect_rt) - utils.print_dict.assert_called_once_with(expect_rt) - - def test_do_md_resource_type_deassociate(self): - args = self._make_args({'namespace': 'MyNamespace', - 'resource_type': 'MyResourceType'}) - with mock.patch.object(self.gc.metadefs_resource_type, - 'deassociate') as mocked_deassociate: - test_shell.do_md_resource_type_deassociate(self.gc, args) - - mocked_deassociate.assert_called_once_with('MyNamespace', - 'MyResourceType') - - def test_do_md_resource_type_list(self): - args = self._make_args({}) - with mock.patch.object(self.gc.metadefs_resource_type, - 'list') as mocked_list: - expect_objects = ['MyResourceType1', 'MyResourceType2'] - - mocked_list.return_value = expect_objects - - test_shell.do_md_resource_type_list(self.gc, args) - - mocked_list.assert_called_once() - - def test_do_md_namespace_resource_type_list(self): - args = self._make_args({'namespace': 'MyNamespace'}) - with mock.patch.object(self.gc.metadefs_resource_type, - 'get') as mocked_get: - expect_objects = [{'namespace': 'MyNamespace', - 'object': 'MyObject'}] - - mocked_get.return_value = expect_objects - - test_shell.do_md_namespace_resource_type_list(self.gc, args) - - mocked_get.assert_called_once_with('MyNamespace') - utils.print_list.assert_called_once_with(expect_objects, - ['name', 'prefix', - 'properties_target']) - - def test_do_md_property_create(self): - args = self._make_args({'namespace': 'MyNamespace', - 'name': "MyProperty", - 'title': "Title", - 'schema': '{}'}) - with mock.patch.object(self.gc.metadefs_property, - 'create') as mocked_create: - expect_property = {} - expect_property['namespace'] = 'MyNamespace' - expect_property['name'] = 'MyProperty' - expect_property['title'] = 'Title' - - mocked_create.return_value = expect_property - - test_shell.do_md_property_create(self.gc, args) - - mocked_create.assert_called_once_with('MyNamespace', - name='MyProperty', - title='Title') - utils.print_dict.assert_called_once_with(expect_property) - - def test_do_md_property_create_invalid_schema(self): - args = self._make_args({'namespace': 'MyNamespace', - 'name': "MyProperty", - 'title': "Title", - 'schema': 'Invalid'}) - self.assertRaises(SystemExit, test_shell.do_md_property_create, - self.gc, args) - - def test_do_md_property_update(self): - args = self._make_args({'namespace': 'MyNamespace', - 'property': 'MyProperty', - 'name': 'NewName', - 'title': "Title", - 'schema': '{}'}) - with mock.patch.object(self.gc.metadefs_property, - 'update') as mocked_update: - expect_property = {} - expect_property['namespace'] = 'MyNamespace' - expect_property['name'] = 'MyProperty' - expect_property['title'] = 'Title' - - mocked_update.return_value = expect_property - - test_shell.do_md_property_update(self.gc, args) - - mocked_update.assert_called_once_with('MyNamespace', 'MyProperty', - name='NewName', - title='Title') - utils.print_dict.assert_called_once_with(expect_property) - - def test_do_md_property_update_invalid_schema(self): - args = self._make_args({'namespace': 'MyNamespace', - 'property': 'MyProperty', - 'name': "MyObject", - 'title': "Title", - 'schema': 'Invalid'}) - self.assertRaises(SystemExit, test_shell.do_md_property_update, - self.gc, args) - - def test_do_md_property_show(self): - args = self._make_args({'namespace': 'MyNamespace', - 'property': 'MyProperty', - 'max_column_width': 80}) - with mock.patch.object(self.gc.metadefs_property, 'get') as mocked_get: - expect_property = {} - expect_property['namespace'] = 'MyNamespace' - expect_property['property'] = 'MyProperty' - expect_property['title'] = 'Title' - - mocked_get.return_value = expect_property - - test_shell.do_md_property_show(self.gc, args) - - mocked_get.assert_called_once_with('MyNamespace', 'MyProperty') - utils.print_dict.assert_called_once_with(expect_property, 80) - - def test_do_md_property_delete(self): - args = self._make_args({'namespace': 'MyNamespace', - 'property': 'MyProperty'}) - with mock.patch.object(self.gc.metadefs_property, - 'delete') as mocked_delete: - test_shell.do_md_property_delete(self.gc, args) - - mocked_delete.assert_called_once_with('MyNamespace', 'MyProperty') - - def test_do_md_namespace_property_delete(self): - args = self._make_args({'namespace': 'MyNamespace'}) - with mock.patch.object(self.gc.metadefs_property, - 'delete_all') as mocked_delete_all: - test_shell.do_md_namespace_properties_delete(self.gc, args) - - mocked_delete_all.assert_called_once_with('MyNamespace') - - def test_do_md_property_list(self): - args = self._make_args({'namespace': 'MyNamespace'}) - with mock.patch.object(self.gc.metadefs_property, - 'list') as mocked_list: - expect_objects = [{'namespace': 'MyNamespace', - 'property': 'MyProperty', - 'title': 'MyTitle'}] - - mocked_list.return_value = expect_objects - - test_shell.do_md_property_list(self.gc, args) - - mocked_list.assert_called_once_with('MyNamespace') - utils.print_list.assert_called_once_with(expect_objects, - ['name', 'title', 'type']) - - def test_do_md_object_create(self): - args = self._make_args({'namespace': 'MyNamespace', - 'name': "MyObject", - 'schema': '{}'}) - with mock.patch.object(self.gc.metadefs_object, - 'create') as mocked_create: - expect_object = {} - expect_object['namespace'] = 'MyNamespace' - expect_object['name'] = 'MyObject' - - mocked_create.return_value = expect_object - - test_shell.do_md_object_create(self.gc, args) - - mocked_create.assert_called_once_with('MyNamespace', - name='MyObject') - utils.print_dict.assert_called_once_with(expect_object) - - def test_do_md_object_create_invalid_schema(self): - args = self._make_args({'namespace': 'MyNamespace', - 'name': "MyObject", - 'schema': 'Invalid'}) - self.assertRaises(SystemExit, test_shell.do_md_object_create, - self.gc, args) - - def test_do_md_object_update(self): - args = self._make_args({'namespace': 'MyNamespace', - 'object': 'MyObject', - 'name': 'NewName', - 'schema': '{}'}) - with mock.patch.object(self.gc.metadefs_object, - 'update') as mocked_update: - expect_object = {} - expect_object['namespace'] = 'MyNamespace' - expect_object['name'] = 'MyObject' - - mocked_update.return_value = expect_object - - test_shell.do_md_object_update(self.gc, args) - - mocked_update.assert_called_once_with('MyNamespace', 'MyObject', - name='NewName') - utils.print_dict.assert_called_once_with(expect_object) - - def test_do_md_object_update_invalid_schema(self): - args = self._make_args({'namespace': 'MyNamespace', - 'object': 'MyObject', - 'name': "MyObject", - 'schema': 'Invalid'}) - self.assertRaises(SystemExit, test_shell.do_md_object_update, - self.gc, args) - - def test_do_md_object_show(self): - args = self._make_args({'namespace': 'MyNamespace', - 'object': 'MyObject', - 'max_column_width': 80}) - with mock.patch.object(self.gc.metadefs_object, 'get') as mocked_get: - expect_object = {} - expect_object['namespace'] = 'MyNamespace' - expect_object['object'] = 'MyObject' - - mocked_get.return_value = expect_object - - test_shell.do_md_object_show(self.gc, args) - - mocked_get.assert_called_once_with('MyNamespace', 'MyObject') - utils.print_dict.assert_called_once_with(expect_object, 80) - - def test_do_md_object_property_show(self): - args = self._make_args({'namespace': 'MyNamespace', - 'object': 'MyObject', - 'property': 'MyProperty', - 'max_column_width': 80}) - with mock.patch.object(self.gc.metadefs_object, 'get') as mocked_get: - expect_object = {'name': 'MyObject', - 'properties': { - 'MyProperty': {'type': 'string'} - }} - - mocked_get.return_value = expect_object - - test_shell.do_md_object_property_show(self.gc, args) - - mocked_get.assert_called_once_with('MyNamespace', 'MyObject') - utils.print_dict.assert_called_once_with({'type': 'string', - 'name': 'MyProperty'}, - 80) - - def test_do_md_object_property_show_non_existing(self): - args = self._make_args({'namespace': 'MyNamespace', - 'object': 'MyObject', - 'property': 'MyProperty', - 'max_column_width': 80}) - with mock.patch.object(self.gc.metadefs_object, 'get') as mocked_get: - expect_object = {'name': 'MyObject', 'properties': {}} - mocked_get.return_value = expect_object - - self.assertRaises(SystemExit, - test_shell.do_md_object_property_show, - self.gc, args) - mocked_get.assert_called_once_with('MyNamespace', 'MyObject') - - def test_do_md_object_delete(self): - args = self._make_args({'namespace': 'MyNamespace', - 'object': 'MyObject'}) - with mock.patch.object(self.gc.metadefs_object, - 'delete') as mocked_delete: - test_shell.do_md_object_delete(self.gc, args) - - mocked_delete.assert_called_once_with('MyNamespace', 'MyObject') - - def test_do_md_namespace_objects_delete(self): - args = self._make_args({'namespace': 'MyNamespace'}) - with mock.patch.object(self.gc.metadefs_object, - 'delete_all') as mocked_delete_all: - test_shell.do_md_namespace_objects_delete(self.gc, args) - - mocked_delete_all.assert_called_once_with('MyNamespace') - - def test_do_md_object_list(self): - args = self._make_args({'namespace': 'MyNamespace'}) - with mock.patch.object(self.gc.metadefs_object, 'list') as mocked_list: - expect_objects = [{'namespace': 'MyNamespace', - 'object': 'MyObject'}] - - mocked_list.return_value = expect_objects - - test_shell.do_md_object_list(self.gc, args) - - mocked_list.assert_called_once_with('MyNamespace') - utils.print_list.assert_called_once_with( - expect_objects, - ['name', 'description'], - field_settings={ - 'description': {'align': 'l', 'max_width': 50}}) diff --git a/code/daisyclient/tests/v2/test_tags.py b/code/daisyclient/tests/v2/test_tags.py deleted file mode 100755 index 69adf0d7..00000000 --- a/code/daisyclient/tests/v2/test_tags.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import testtools - -from glanceclient.v2 import image_tags -from tests import utils - - -IMAGE = '3a4560a1-e585-443e-9b39-553b46ec92d1' -TAG = 'tag01' - - -data_fixtures = { - '/v2/images/{image}/tags/{tag_value}'.format(image=IMAGE, tag_value=TAG): { - 'DELETE': ( - {}, - None, - ), - 'PUT': ( - {}, - { - 'image_id': IMAGE, - 'tag_value': TAG - } - ), - } -} - -schema_fixtures = { - 'tag': { - 'GET': ( - {}, - {'name': 'image', 'properties': {'image_id': {}, 'tags': {}}} - ) - } -} - - -class TestController(testtools.TestCase): - - def setUp(self): - super(TestController, self).setUp() - self.api = utils.FakeAPI(data_fixtures) - self.schema_api = utils.FakeSchemaAPI(schema_fixtures) - self.controller = image_tags.Controller(self.api, self.schema_api) - - def test_update_image_tag(self): - image_id = IMAGE - tag_value = TAG - self.controller.update(image_id, tag_value) - expect = [ - ('PUT', - '/v2/images/{image}/tags/{tag_value}'.format(image=IMAGE, - tag_value=TAG), - {}, - None)] - self.assertEqual(expect, self.api.calls) - - def test_delete_image_tag(self): - image_id = IMAGE - tag_value = TAG - self.controller.delete(image_id, tag_value) - expect = [ - ('DELETE', - '/v2/images/{image}/tags/{tag_value}'.format(image=IMAGE, - tag_value=TAG), - {}, - None)] - self.assertEqual(expect, self.api.calls) diff --git a/code/daisyclient/tests/v2/test_tasks.py b/code/daisyclient/tests/v2/test_tasks.py deleted file mode 100755 index 5d48b2d1..00000000 --- a/code/daisyclient/tests/v2/test_tasks.py +++ /dev/null @@ -1,278 +0,0 @@ -# Copyright 2013 OpenStack Foundation. -# Copyright 2013 IBM Corp. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import testtools - -from glanceclient.v2 import tasks -from tests import utils - - -_OWNED_TASK_ID = 'a4963502-acc7-42ba-ad60-5aa0962b7faf' -_OWNER_ID = '6bd473f0-79ae-40ad-a927-e07ec37b642f' -_FAKE_OWNER_ID = '63e7f218-29de-4477-abdc-8db7c9533188' - - -fixtures = { - '/v2/tasks?limit=%d' % tasks.DEFAULT_PAGE_SIZE: { - 'GET': ( - {}, - {'tasks': [ - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'type': 'import', - 'status': 'pending', - }, - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'type': 'import', - 'status': 'processing', - }, - ]}, - ), - }, - '/v2/tasks?limit=1': { - 'GET': ( - {}, - { - 'tasks': [ - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'type': 'import', - 'status': 'pending', - }, - ], - 'next': ('/v2/tasks?limit=1&' - 'marker=3a4560a1-e585-443e-9b39-553b46ec92d1'), - }, - ), - }, - ('/v2/tasks?limit=1&marker=3a4560a1-e585-443e-9b39-553b46ec92d1'): { - 'GET': ( - {}, - {'tasks': [ - { - 'id': '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810', - 'type': 'import', - 'status': 'pending', - }, - ]}, - ), - }, - '/v2/tasks/3a4560a1-e585-443e-9b39-553b46ec92d1': { - 'GET': ( - {}, - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'type': 'import', - 'status': 'pending', - }, - ), - 'PATCH': ( - {}, - '', - ), - }, - '/v2/tasks/e7e59ff6-fa2e-4075-87d3-1a1398a07dc3': { - 'GET': ( - {}, - { - 'id': 'e7e59ff6-fa2e-4075-87d3-1a1398a07dc3', - 'type': 'import', - 'status': 'pending', - }, - ), - 'PATCH': ( - {}, - '', - ), - }, - '/v2/tasks': { - 'POST': ( - {}, - { - 'id': '3a4560a1-e585-443e-9b39-553b46ec92d1', - 'type': 'import', - 'status': 'pending', - 'input': '{"import_from": "file:///", ' - '"import_from_format": "qcow2"}' - }, - ), - }, - '/v2/tasks?limit=%d&owner=%s' % (tasks.DEFAULT_PAGE_SIZE, _OWNER_ID): { - 'GET': ( - {}, - {'tasks': [ - { - 'id': _OWNED_TASK_ID, - }, - ]}, - ), - }, - '/v2/tasks?limit=%d&status=processing' % (tasks.DEFAULT_PAGE_SIZE): { - 'GET': ( - {}, - {'tasks': [ - { - 'id': _OWNED_TASK_ID, - }, - ]}, - ), - }, - '/v2/tasks?limit=%d&type=import' % (tasks.DEFAULT_PAGE_SIZE): { - 'GET': ( - {}, - {'tasks': [ - { - 'id': _OWNED_TASK_ID, - }, - ]}, - ), - }, - '/v2/tasks?limit=%d&type=fake' % (tasks.DEFAULT_PAGE_SIZE): { - 'GET': ( - {}, - {'tasks': [ - ]}, - ), - }, - '/v2/tasks?limit=%d&status=fake' % (tasks.DEFAULT_PAGE_SIZE): { - 'GET': ( - {}, - {'tasks': [ - ]}, - ), - }, - '/v2/tasks?limit=%d&type=import' % (tasks.DEFAULT_PAGE_SIZE): { - 'GET': ( - {}, - {'tasks': [ - { - 'id': _OWNED_TASK_ID, - }, - ]}, - ), - }, - '/v2/tasks?limit=%d&owner=%s' % (tasks.DEFAULT_PAGE_SIZE, _FAKE_OWNER_ID): - { - 'GET': ({}, - {'tasks': []}, - ), - } -} - -schema_fixtures = { - 'task': { - 'GET': ( - {}, - { - 'name': 'task', - 'properties': { - 'id': {}, - 'type': {}, - 'status': {}, - 'input': {}, - 'result': {}, - 'message': {}, - }, - } - ) - } -} - - -class TestController(testtools.TestCase): - - def setUp(self): - super(TestController, self).setUp() - self.api = utils.FakeAPI(fixtures) - self.schema_api = utils.FakeSchemaAPI(schema_fixtures) - self.controller = tasks.Controller(self.api, self.schema_api) - - def test_list_tasks(self): - # NOTE(flwang): cast to list since the controller returns a generator - tasks = list(self.controller.list()) - self.assertEqual(tasks[0].id, '3a4560a1-e585-443e-9b39-553b46ec92d1') - self.assertEqual(tasks[0].type, 'import') - self.assertEqual(tasks[0].status, 'pending') - self.assertEqual(tasks[1].id, '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810') - self.assertEqual(tasks[1].type, 'import') - self.assertEqual(tasks[1].status, 'processing') - - def test_list_tasks_paginated(self): - # NOTE(flwang): cast to list since the controller returns a generator - tasks = list(self.controller.list(page_size=1)) - self.assertEqual(tasks[0].id, '3a4560a1-e585-443e-9b39-553b46ec92d1') - self.assertEqual(tasks[0].type, 'import') - self.assertEqual(tasks[1].id, '6f99bf80-2ee6-47cf-acfe-1f1fabb7e810') - self.assertEqual(tasks[1].type, 'import') - - def test_list_tasks_with_status(self): - filters = {'filters': {'status': 'processing'}} - tasks = list(self.controller.list(**filters)) - self.assertEqual(tasks[0].id, _OWNED_TASK_ID) - - def test_list_tasks_with_wrong_status(self): - filters = {'filters': {'status': 'fake'}} - tasks = list(self.controller.list(**filters)) - self.assertEqual(len(tasks), 0) - - def test_list_tasks_with_type(self): - filters = {'filters': {'type': 'import'}} - tasks = list(self.controller.list(**filters)) - self.assertEqual(tasks[0].id, _OWNED_TASK_ID) - - def test_list_tasks_with_wrong_type(self): - filters = {'filters': {'type': 'fake'}} - tasks = list(self.controller.list(**filters)) - self.assertEqual(len(tasks), 0) - - def test_list_tasks_for_owner(self): - filters = {'filters': {'owner': _OWNER_ID}} - tasks = list(self.controller.list(**filters)) - self.assertEqual(tasks[0].id, _OWNED_TASK_ID) - - def test_list_tasks_for_fake_owner(self): - filters = {'filters': {'owner': _FAKE_OWNER_ID}} - tasks = list(self.controller.list(**filters)) - self.assertEqual(tasks, []) - - def test_list_tasks_filters_encoding(self): - filters = {"owner": u"ni\xf1o"} - try: - list(self.controller.list(filters=filters)) - except KeyError: - # NOTE(flaper87): It raises KeyError because there's - # no fixture supporting this query: - # /v2/tasks?owner=ni%C3%B1o&limit=20 - # We just want to make sure filters are correctly encoded. - pass - - self.assertEqual(b"ni\xc3\xb1o", filters["owner"]) - - def test_get_task(self): - task = self.controller.get('3a4560a1-e585-443e-9b39-553b46ec92d1') - self.assertEqual(task.id, '3a4560a1-e585-443e-9b39-553b46ec92d1') - self.assertEqual(task.type, 'import') - - def test_create_task(self): - properties = { - 'type': 'import', - 'input': {'import_from_format': 'ovf', 'import_from': - 'swift://cloud.foo/myaccount/mycontainer/path'}, - } - task = self.controller.create(**properties) - self.assertEqual(task.id, '3a4560a1-e585-443e-9b39-553b46ec92d1') - self.assertEqual(task.type, 'import') diff --git a/code/daisyclient/tools/glance.bash_completion b/code/daisyclient/tools/glance.bash_completion deleted file mode 100755 index 1e7f72cd..00000000 --- a/code/daisyclient/tools/glance.bash_completion +++ /dev/null @@ -1,25 +0,0 @@ -_glance_opts="" # lazy init -_glance_flags="" # lazy init -_glance_opts_exp="" # lazy init -_glance() -{ - local cur prev nbc cflags - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - if [ "x$_glance_opts" == "x" ] ; then - nbc="`glance bash-completion | sed -e "s/ *-h */ /" -e "s/ *-i */ /"`" - _glance_opts="`echo "$nbc" | sed -e "s/--[a-z0-9_-]*//g" -e "s/ */ /g"`" - _glance_flags="`echo " $nbc" | sed -e "s/ [^-][^-][a-z0-9_-]*//g" -e "s/ */ /g"`" - _glance_opts_exp="`echo "$_glance_opts" | sed 's/^ *//' | tr ' ' '|'`" - fi - - if [[ " ${COMP_WORDS[@]} " =~ " "($_glance_opts_exp)" " && "$prev" != "help" ]] ; then - COMPREPLY=($(compgen -W "${_glance_flags}" -- ${cur})) - else - COMPREPLY=($(compgen -W "${_glance_opts}" -- ${cur})) - fi - return 0 -} -complete -F _glance glance \ No newline at end of file diff --git a/code/daisyclient/tools/with_venv.sh b/code/daisyclient/tools/with_venv.sh deleted file mode 100755 index e6e44f59..00000000 --- a/code/daisyclient/tools/with_venv.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -command -v tox > /dev/null 2>&1 -if [ $? -ne 0 ]; then - echo 'This script requires "tox" to run.' - echo 'You can install it with "pip install tox".' - exit 1; -fi - -tox -evenv -- $@