Remove all references to images v1
*Images v1 will soon be deprecated so the test code needs to be removed *Removed images v1 behaviors *Removed images v1 client *Removed images v1 models *Removed images v1 metatests Change-Id: If7279be4096c525780877d84a465930d0000341c
This commit is contained in:
parent
535be48912
commit
96b46e789c
@ -1,15 +0,0 @@
|
||||
"""
|
||||
Copyright 2013 Rackspace
|
||||
|
||||
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.
|
||||
"""
|
@ -1,121 +0,0 @@
|
||||
"""
|
||||
Copyright 2013 Rackspace
|
||||
|
||||
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 time
|
||||
import cStringIO as StringIO
|
||||
|
||||
from cafe.engine.behaviors import BaseBehavior
|
||||
|
||||
from cloudcafe.auth.config import UserAuthConfig
|
||||
from cloudcafe.auth.provider import AuthProvider
|
||||
from cloudcafe.identity.v2_0.tenants_api.client import \
|
||||
TenantsAPI_Client
|
||||
|
||||
from cloudcafe.images.common.types import ImageStatus
|
||||
from cloudcafe.common.exceptions import \
|
||||
TimeoutException, BuildErrorException
|
||||
|
||||
|
||||
class ImagesV1Behaviors(BaseBehavior):
|
||||
"""
|
||||
@summary: Base Behaviors class for Images V1 API tests
|
||||
"""
|
||||
|
||||
def __init__(self, images_client, images_config):
|
||||
super(ImagesV1Behaviors, self).__init__()
|
||||
self.config = images_config
|
||||
self.client = images_client
|
||||
|
||||
access_data = AuthProvider().get_access_data()
|
||||
self.tenants_client = TenantsAPI_Client(
|
||||
UserAuthConfig().auth_endpoint,
|
||||
access_data.token.id_,
|
||||
'json', 'json')
|
||||
|
||||
def wait_for_image_status(self, image_id, desired_status,
|
||||
interval_time=None, timeout=None):
|
||||
"""Waits for a image to reach a desired status
|
||||
@param image_id: The uuid of the image
|
||||
@type image_id: String
|
||||
@param desired_status: The desired final status of the image
|
||||
@type desired_status: String
|
||||
@param interval_time: The amount of time in seconds to wait
|
||||
between polling
|
||||
@type interval_time: Integer
|
||||
@param interval_time: The amount of time in seconds to wait
|
||||
before aborting
|
||||
@type interval_time: Integer
|
||||
@return: Response object containing response and the image
|
||||
domain object
|
||||
@rtype: requests.Response
|
||||
"""
|
||||
|
||||
interval_time = interval_time or self.config.image_status_interval
|
||||
timeout = timeout or self.config.snapshot_timeout
|
||||
end_time = time.time() + timeout
|
||||
|
||||
while time.time() < end_time:
|
||||
resp = self.client.retrieve_metadata(image_id)
|
||||
image_id = resp.headers['x-image-meta-id']
|
||||
image_status = resp.headers['x-image-meta-status']
|
||||
|
||||
if image_status == ImageStatus.ERROR:
|
||||
raise BuildErrorException(
|
||||
'Build failed. Image with uuid {0} entered ERROR status.'
|
||||
.format(image_id))
|
||||
|
||||
if image_status == desired_status:
|
||||
break
|
||||
time.sleep(interval_time)
|
||||
else:
|
||||
raise TimeoutException(
|
||||
"wait_for_image_status ran for {0} seconds and did not "
|
||||
"observe the image achieving the {1} status.".format(
|
||||
timeout, desired_status))
|
||||
|
||||
return resp
|
||||
|
||||
def create_remote_image(self, name, container_format, disk_format):
|
||||
"""Create new remote image.
|
||||
@return ID of the newly registered image
|
||||
"""
|
||||
name = 'New Remote Image {0}'.format(name)
|
||||
|
||||
response = self.client.add_image(
|
||||
name,
|
||||
None,
|
||||
image_meta_container_format=container_format,
|
||||
image_meta_disk_format=disk_format,
|
||||
image_meta_is_public=True,
|
||||
image_meta_location=self.config.remote_image)
|
||||
|
||||
return response.entity.id_
|
||||
|
||||
def create_standard_image(cls, name, container_format, disk_format, size):
|
||||
"""Create new standard image.
|
||||
@return ID of the newly registered image
|
||||
"""
|
||||
image_data = StringIO.StringIO('*' * size)
|
||||
name = 'New Standard Image {0}'.format(name)
|
||||
|
||||
response = cls.client.add_image(
|
||||
name,
|
||||
image_data,
|
||||
image_meta_container_format=container_format,
|
||||
image_meta_disk_format=disk_format,
|
||||
image_meta_is_public=True)
|
||||
|
||||
return response.entity.id_
|
@ -1,183 +0,0 @@
|
||||
"""
|
||||
Copyright 2013 Rackspace
|
||||
|
||||
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 cafe.engine.http.client import AutoMarshallingHTTPClient
|
||||
from cloudcafe.images.v1.models.image import Image, ImageMinList
|
||||
from cloudcafe.images.v1.models.member import MemberList
|
||||
|
||||
|
||||
class ImagesClient(AutoMarshallingHTTPClient):
|
||||
"""Client for Image API."""
|
||||
|
||||
def __init__(self, url, auth_token, serialize_format, deserialize_format):
|
||||
"""
|
||||
@param url: Base URL for the compute service
|
||||
@type url: String
|
||||
@param auth_token: Auth token to be used for all requests
|
||||
@type auth_token: String
|
||||
@param serialize_format: Format for serializing requests
|
||||
@type serialize_format: String
|
||||
@param deserialize_format: Format for de-serializing responses
|
||||
@type deserialize_format: String
|
||||
"""
|
||||
super(ImagesClient, self).__init__(
|
||||
serialize_format,
|
||||
deserialize_format)
|
||||
self.auth_token = auth_token
|
||||
self.default_headers['X-Auth-Token'] = auth_token
|
||||
self.default_headers['Content-Type'] = 'application/{0}'.format(
|
||||
self.serialize_format)
|
||||
self.default_headers['Accept'] = 'application/{0}'.format(
|
||||
self.deserialize_format)
|
||||
self.url = url
|
||||
|
||||
def list_images(self, requestslib_kwargs=None):
|
||||
url = '{0}/images'.format(self.url)
|
||||
return self.request('GET', url, response_entity_type=Image,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def list_images_detail(self, parameters_list, requestslib_kwargs=None):
|
||||
url = '{0}/images/detail'.format(self.url)
|
||||
return self.request('GET', url, params=parameters_list,
|
||||
response_entity_type=Image,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def get_image(self, image_id, requestslib_kwargs=None):
|
||||
url = '{0}/images/{1}'.format(self.url, image_id)
|
||||
return self.request('GET', url, response_entity_type=Image,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def delete_image(self, image_id, requestslib_kwargs=None):
|
||||
url = '{0}/images/{1}'.format(self.url, image_id)
|
||||
return self.request('DELETE', url, response_entity_type=Image,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def filter_images_list(self, parameters_list, requestslib_kwargs=None):
|
||||
url = '{0}/images'.format(self.url)
|
||||
return self.request('GET', url, params=parameters_list,
|
||||
response_entity_type=Image,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def retrieve_metadata(self, image_id, requestslib_kwargs=None):
|
||||
url = '{0}/images/{1}'.format(self.url, image_id)
|
||||
return self.request('HEAD',
|
||||
url,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def retrieve_raw_image_data(self, image_id, requestslib_kwargs=None):
|
||||
url = '{0}/images/{1}'.format(self.url, image_id)
|
||||
return self.request('GET', url, requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def add_image(self, image_name, image_data=None, headers=None,
|
||||
image_meta_id=None, image_meta_store=None,
|
||||
image_meta_disk_format=None,
|
||||
image_meta_container_format=None, image_meta_size=None,
|
||||
image_meta_checksum=None, image_meta_is_public=None,
|
||||
image_meta_min_ram=None, image_meta_min_disk=None,
|
||||
image_meta_owner=None, image_meta_property=None,
|
||||
image_meta_location=None,
|
||||
requestslib_kwargs=None):
|
||||
|
||||
headers = headers if headers else {}
|
||||
|
||||
if image_data:
|
||||
headers['Content-Type'] = 'application/octet-stream'
|
||||
|
||||
headers['x-image-meta-name'] = image_name
|
||||
headers['x-image-meta-id'] = image_meta_id
|
||||
headers['x-image-meta-store'] = image_meta_store
|
||||
headers['x-image-meta-disk-format'] = image_meta_disk_format
|
||||
headers['x-image-meta-container-format'] = image_meta_container_format
|
||||
headers['x-image-meta-size'] = image_meta_size
|
||||
headers['x-image-meta-checksum'] = image_meta_checksum
|
||||
headers['x-image-meta-is-public'] = image_meta_is_public
|
||||
headers['x-image-meta-min-ram'] = image_meta_min_ram
|
||||
headers['x-image-meta-min-disk'] = image_meta_min_disk
|
||||
headers['x-image-meta-owner'] = image_meta_owner
|
||||
headers['x-image-meta-location'] = image_meta_location
|
||||
if image_meta_property:
|
||||
for key, val in image_meta_property.items():
|
||||
headers['x-image-meta-property-{0}'.format(key)] = val
|
||||
|
||||
url = '{0}/images'.format(self.url)
|
||||
return self.request('POST', url, headers=headers, data=image_data,
|
||||
response_entity_type=Image,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def list_image_membership(self, image_id, requestslib_kwargs=None):
|
||||
url = '{0}/images/{1}/members'.format(self.url, image_id)
|
||||
|
||||
return self.request('GET', url,
|
||||
response_entity_type=MemberList,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def update_image(self, image_id, image_data=None, headers=None,
|
||||
image_meta_name=None, image_meta_store=None,
|
||||
image_meta_disk_format=None,
|
||||
image_meta_container_format=None, image_meta_size=None,
|
||||
image_meta_checksum=None, image_meta_is_public=None,
|
||||
image_meta_min_ram=None, image_meta_min_disk=None,
|
||||
image_meta_owner=None, image_meta_property=None,
|
||||
image_meta_location=None,
|
||||
requestslib_kwargs=None):
|
||||
|
||||
headers = headers if headers else {}
|
||||
|
||||
if image_data:
|
||||
headers['Content-Type'] = 'application/octet-stream'
|
||||
|
||||
headers['x-image-meta-name'] = image_meta_name
|
||||
headers['x-image-meta-store'] = image_meta_store
|
||||
headers['x-image-meta-disk-format'] = image_meta_disk_format
|
||||
headers['x-image-meta-container-format'] = image_meta_container_format
|
||||
headers['x-image-meta-size'] = image_meta_size
|
||||
headers['x-image-meta-checksum'] = image_meta_checksum
|
||||
headers['x-image-meta-is-public'] = image_meta_is_public
|
||||
headers['x-image-meta-min-ram'] = image_meta_min_ram
|
||||
headers['x-image-meta-min-disk'] = image_meta_min_disk
|
||||
headers['x-image-meta-owner'] = image_meta_owner
|
||||
headers['x-image-meta-location'] = image_meta_location
|
||||
if image_meta_property:
|
||||
for key, val in image_meta_property.items():
|
||||
headers['x-image-meta-property-{0}'.format(key)] = val
|
||||
|
||||
url = '{0}/images/{1}'.format(self.url, image_id)
|
||||
return self.request('PUT', url, headers=headers,
|
||||
data=image_data,
|
||||
response_entity_type=Image,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def list_shared_images(self, member_id, requestslib_kwargs=None):
|
||||
url = '{0}/shared-images/{1}'.format(self.url, member_id)
|
||||
|
||||
return self.request('GET', url, response_entity_type=ImageMinList,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def add_member_to_image(self, image_id, member_id,
|
||||
requestslib_kwargs=None):
|
||||
url = '{0}/images/{1}/members/{2}'.format(self.url, image_id,
|
||||
member_id)
|
||||
return self.request('PUT', url, requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def delete_member_from_image(self, image_id, member_id,
|
||||
requestslib_kwargs=None):
|
||||
url = '{0}/images/{1}/members/{2}'.format(self.url, image_id,
|
||||
member_id)
|
||||
return self.request('DELETE', url,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def replace_members_list(self, image_id, requestslib_kwargs=None):
|
||||
url = '{0}/images/{1}/members'.format(self.url, image_id)
|
||||
return self.request('PUT', url, requestslib_kwargs=requestslib_kwargs)
|
@ -1,15 +0,0 @@
|
||||
"""
|
||||
Copyright 2013 Rackspace
|
||||
|
||||
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.
|
||||
"""
|
@ -1,172 +0,0 @@
|
||||
"""
|
||||
Copyright 2013 Rackspace
|
||||
|
||||
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 copy import deepcopy
|
||||
|
||||
from cafe.engine.models.base import AutoMarshallingModel
|
||||
from cloudcafe.compute.common.equality_tools import EqualityTools
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
|
||||
class Image(AutoMarshallingModel):
|
||||
"""@Summary Complete model of an image"""
|
||||
|
||||
def __init__(self, id_=None, status=None, name=None, container_format=None,
|
||||
disk_format=None, owner=None, checksum=None, min_ram=None,
|
||||
min_disk=None, size=None, deleted=None, protected=None,
|
||||
is_public=None, properties=None, created_at=None,
|
||||
updated_at=None, deleted_at=None, members_list=None):
|
||||
super(Image, self).__init__()
|
||||
|
||||
self.id_ = id_
|
||||
self.status = status
|
||||
self.name = name
|
||||
self.deleted = deleted
|
||||
self.container_format = container_format
|
||||
self.created_at = created_at
|
||||
self.disk_format = disk_format
|
||||
self.updated_at = updated_at
|
||||
self.owner = owner
|
||||
self.protected = protected
|
||||
self.min_ram = min_ram
|
||||
self.checksum = checksum
|
||||
self.min_disk = min_disk
|
||||
self.is_public = is_public
|
||||
self.deleted_at = deleted_at
|
||||
self.properties = properties
|
||||
self.size = size
|
||||
self.members_list = members_list
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
@summary: Overrides the default equals
|
||||
@param other: Image object to compare with
|
||||
@type other: Image
|
||||
@return: True if Image objects are equal, False otherwise
|
||||
@rtype: bool
|
||||
"""
|
||||
return EqualityTools.are_objects_equal(self, other, '_log')
|
||||
|
||||
def __ne__(self, other):
|
||||
"""
|
||||
@summary: Overrides the default not-equals
|
||||
@param other: Image object to compare with
|
||||
@type other: Image
|
||||
@return: True if Image objects are not equal, False otherwise
|
||||
@rtype: bool
|
||||
"""
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __repr__(self):
|
||||
values = []
|
||||
for prop in self.__dict__:
|
||||
if prop in ['created_at', 'updated_at', 'deleted_at']:
|
||||
date_property = self.__dict__[prop]
|
||||
date_string = 'None' or \
|
||||
date_property.strftime('%Y-%m-%dT%H:%M:%S')
|
||||
values.append("{0}: {1}".format(prop, date_string))
|
||||
else:
|
||||
values.append("{0}: {1}".format(prop, self.__dict__[prop]))
|
||||
return '[ {0} ]'.format(', '.join(values))
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
serialized_str.replace('false', 'False')
|
||||
serialized_str.replace('true', 'True')
|
||||
json_dict = json.loads(serialized_str)
|
||||
|
||||
if 'images' in json_dict.keys():
|
||||
images = []
|
||||
for image in json_dict['images']:
|
||||
images.append(cls._dict_to_obj(image))
|
||||
return images
|
||||
else:
|
||||
image_str = json_dict['image']
|
||||
return cls._dict_to_obj(image_str)
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, json_dict):
|
||||
"""@summary: Processing dates in converting string to date objects"""
|
||||
json_dict = deepcopy(json_dict)
|
||||
json_dict['id_'] = json_dict.pop('id')
|
||||
|
||||
for date_key in ['created_at', 'updated_at', 'deleted_at']:
|
||||
if json_dict.get(date_key):
|
||||
json_dict[date_key] = None or datetime.strptime(
|
||||
json_dict[date_key],
|
||||
'%Y-%m-%dT%H:%M:%S')
|
||||
return Image(**json_dict)
|
||||
|
||||
@classmethod
|
||||
def _xml_to_obj(cls, serialized_str):
|
||||
"""Returns an instance of a Image based on the xml serialized_str
|
||||
passed in.
|
||||
"""
|
||||
raise NotImplementedError("Glance does not serve XML-formatted \
|
||||
resources.")
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
raise NotImplementedError("Glance does not serve XML-formatted \
|
||||
resources.")
|
||||
|
||||
def add_member(self, member):
|
||||
members_list = self.members_list if self.members_list else []
|
||||
members_list.append(member)
|
||||
self.members_list = members_list
|
||||
|
||||
def delete_member(self, member_to_delete):
|
||||
members_list = self.members_list if self.members_list else []
|
||||
for member in self.members_list:
|
||||
if member.member_id == member_to_delete.member_id:
|
||||
members_list.remove(member_to_delete)
|
||||
|
||||
self.members_list = members_list
|
||||
|
||||
def replace_members_list(self, members_list):
|
||||
self.members_list = members_list
|
||||
|
||||
|
||||
class ImageMin(AutoMarshallingModel):
|
||||
"""A mini Image model returned on certain calls to Images API """
|
||||
|
||||
def __init__(self, id_=None, can_share=None):
|
||||
super(ImageMin, self).__init__()
|
||||
|
||||
self.id_ = id_
|
||||
self.can_share = can_share
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
return cls._dict_to_obj(json_dict)
|
||||
|
||||
@classmethod
|
||||
def _dict_to_obj(cls, json_dict):
|
||||
return ImageMin(
|
||||
id_=json_dict['image_id'],
|
||||
can_share=json_dict['can_share'])
|
||||
|
||||
|
||||
class ImageMinList(AutoMarshallingModel):
|
||||
"""Model class that allows automarshalling of list of MinImage."""
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_list = json.loads(serialized_str)
|
||||
|
||||
return [ImageMin._dict_to_obj(image_json) for image_json in
|
||||
json_list['shared_images']]
|
@ -1,31 +0,0 @@
|
||||
import json
|
||||
from cafe.engine.models.base import \
|
||||
AutoMarshallingModel, AutoMarshallingListModel
|
||||
|
||||
|
||||
class Member(AutoMarshallingModel):
|
||||
def __init__(self, member_id=None, shared_images=None, can_share=None):
|
||||
self.member_id = member_id
|
||||
self.shared_images = shared_images
|
||||
self.can_share = can_share
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
|
||||
if 'members' in json_dict.keys():
|
||||
members = []
|
||||
members.extend([Member(**m) for m in json_dict['members']])
|
||||
return members
|
||||
else:
|
||||
return Member(**json_dict)
|
||||
|
||||
|
||||
class MemberList(AutoMarshallingListModel):
|
||||
"""Represent a list of Members"""
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
|
||||
return [Member(**m) for m in json_dict.get('members')]
|
@ -1,15 +0,0 @@
|
||||
"""
|
||||
Copyright 2013 Rackspace
|
||||
|
||||
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.
|
||||
"""
|
@ -1,297 +0,0 @@
|
||||
import re
|
||||
from cloudcafe.images.v1.client import ImagesClient
|
||||
from httpretty import HTTPretty
|
||||
|
||||
GLANCE_API_SERVER_ENDPOINT = 'http://localhost:9292/v1'
|
||||
|
||||
|
||||
class TestClient(object):
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
HTTPretty.enable()
|
||||
|
||||
cls.images_client = ImagesClient(
|
||||
url=GLANCE_API_SERVER_ENDPOINT,
|
||||
auth_token="36a04b4e71484ab9aacb1d0ac95733fc",
|
||||
serialize_format="json",
|
||||
deserialize_format="json"
|
||||
)
|
||||
|
||||
cls.image_id = '1c675abd94f49cda114e12490b328d9'
|
||||
cls.images_uri = '{0}/images'.format(GLANCE_API_SERVER_ENDPOINT)
|
||||
cls.image_uri = '{0}/{1}'.format(cls.images_uri, cls.image_id)
|
||||
|
||||
@classmethod
|
||||
def teardown_class(cls):
|
||||
HTTPretty.disable()
|
||||
|
||||
def setup_method(self, method):
|
||||
if method.__name__ is 'test_retrieve_image_metadata' or \
|
||||
method.__name__ is 'test_retrieve_raw_image_data' or \
|
||||
method.__name__ is 'test_update_image':
|
||||
self.expected_headers = {
|
||||
'x-image-meta-uri':
|
||||
'{0}/images/1c675abd94f49cda114e12490b328d9'
|
||||
.format(GLANCE_API_SERVER_ENDPOINT),
|
||||
'x-image-meta-name': 'Ubuntu 10.04 Plain 5GB',
|
||||
'x-image-meta-disk_format': 'vhd',
|
||||
'x-image-meta-container_format': 'ovf',
|
||||
'x-image-meta-size': '5368709120',
|
||||
'x-image-meta-checksum': 'c2e5db72bd7fd153f53ede5da5a06de3',
|
||||
'x-image-meta-created_at': '2010-02-03 09:34:01',
|
||||
'x-image-meta-updated_at': '2010-02-03 09:34:01',
|
||||
'x-image-meta-deleted_at': '',
|
||||
'x-image-meta-status': 'available',
|
||||
'x-image-meta-is_public': 'true',
|
||||
'x-image-meta-min-ram': '256',
|
||||
'x-image-meta-min-disk': '0',
|
||||
'x-image-meta-owner': 'null',
|
||||
'x-image-meta-property-distro': 'Ubuntu 10.04 LTS'
|
||||
}
|
||||
|
||||
def test_list_available_images(self):
|
||||
HTTPretty.register_uri(HTTPretty.GET, self.images_uri,
|
||||
body=self._build_response_body(),
|
||||
content_type="application/json")
|
||||
|
||||
actual_response = self.images_client.list_images()
|
||||
|
||||
assert HTTPretty.last_request.headers['X-Auth-Token'] == \
|
||||
'36a04b4e71484ab9aacb1d0ac95733fc'
|
||||
assert HTTPretty.last_request.headers['Content-Type'] == \
|
||||
'application/json'
|
||||
assert HTTPretty.last_request.headers['Accept'] == 'application/json'
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert self._build_response_body() == actual_response.content
|
||||
assert self.images_uri == actual_response.url
|
||||
|
||||
def test_get_image(self):
|
||||
HTTPretty.register_uri(HTTPretty.GET, self.image_uri,
|
||||
body=self._build_response_body(),
|
||||
content_type="application/json")
|
||||
|
||||
actual_response = self.images_client.get_image(image_id=self.image_id)
|
||||
|
||||
assert HTTPretty.last_request.headers['X-Auth-Token'] == \
|
||||
'36a04b4e71484ab9aacb1d0ac95733fc'
|
||||
assert HTTPretty.last_request.headers['Content-Type'] == \
|
||||
'application/json'
|
||||
assert HTTPretty.last_request.headers['Accept'] == 'application/json'
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert self._build_response_body() == actual_response.content
|
||||
assert self.image_uri == actual_response.url
|
||||
|
||||
def test_filter_images_list(self):
|
||||
filtering_parameters = {'name': 'precise', 'status': 'active',
|
||||
'container_format': 'bare',
|
||||
'disk_format': 'qcow2',
|
||||
'min_disk': '0', 'size': '252116992'}
|
||||
HTTPretty.register_uri(
|
||||
HTTPretty.GET,
|
||||
"{0}/images?status=active&name=precise&container_format=bare&\
|
||||
disk_format=qcow2&min_disk=0&size=252116992"
|
||||
.format(GLANCE_API_SERVER_ENDPOINT),
|
||||
body=self._build_response_body(),
|
||||
content_type="application/json")
|
||||
|
||||
actual_response = self.images_client.filter_images_list(
|
||||
filtering_parameters)
|
||||
request_querystring = HTTPretty.last_request.querystring
|
||||
expected_request_querystring = self._get_querystring_data(
|
||||
request_querystring)
|
||||
|
||||
assert expected_request_querystring == filtering_parameters
|
||||
assert HTTPretty.last_request.headers['X-Auth-Token'] == \
|
||||
'36a04b4e71484ab9aacb1d0ac95733fc'
|
||||
assert HTTPretty.last_request.headers['Content-Type'] == \
|
||||
'application/json'
|
||||
assert HTTPretty.last_request.headers['Accept'] == 'application/json'
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert self._build_response_body() == actual_response.content
|
||||
|
||||
def test_add_image(self):
|
||||
HTTPretty.register_uri(HTTPretty.POST, self.images_uri,
|
||||
body='Adding New Image',
|
||||
adding_headers={'x-image-meta-property-distro':
|
||||
'Ubuntu 10.04 LTS'})
|
||||
|
||||
image_name = 'Ubuntu 10.04 Plain 5GB'
|
||||
actual_response = self.images_client.add_image(image_name=image_name,
|
||||
image_data=None)
|
||||
|
||||
assert 'x-image-meta-name' in \
|
||||
HTTPretty.last_request.headers.keys()
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert self.images_uri == actual_response.url
|
||||
assert 'Adding New Image' == actual_response.content
|
||||
assert 'x-image-meta-property-distro' in \
|
||||
actual_response.__dict__['headers'].keys()
|
||||
|
||||
def test_retrieve_image_metadata(self):
|
||||
url = '{0}/71c675ab-d94f-49cd-a114-e12490b328d9'.format(
|
||||
self.images_uri)
|
||||
HTTPretty.register_uri(HTTPretty.HEAD, url, body='Raw Image Data',
|
||||
headers=self.expected_headers)
|
||||
|
||||
actual_response = self.images_client.retrieve_metadata(
|
||||
'71c675ab-d94f-49cd-a114-e12490b328d9'
|
||||
)
|
||||
uri_regex = re.compile(
|
||||
'{0}/[\w\d]{{8}}-[\w\d]{{4}}-[\w\d]{{4}}-[\w\d]{{4}}-[\w\d]{{12}}'
|
||||
.format(self.images_uri)
|
||||
)
|
||||
assert re.match(uri_regex, actual_response.url) is not None
|
||||
assert HTTPretty.last_request.headers['X-Auth-Token'] == \
|
||||
'36a04b4e71484ab9aacb1d0ac95733fc'
|
||||
assert HTTPretty.last_request.headers['Content-Type'] == \
|
||||
'application/json'
|
||||
assert HTTPretty.last_request.headers['Accept'] == 'application/json'
|
||||
assert 200 == actual_response.status_code
|
||||
|
||||
def test_retrieve_raw_image_data(self):
|
||||
url = '{0}/71c675ab-d94f-49cd-a114-e12490b328d9'.format(
|
||||
self.images_uri)
|
||||
HTTPretty.register_uri(HTTPretty.GET, url, body='Raw Image Data',
|
||||
headers=self.expected_headers)
|
||||
|
||||
actual_response = self.images_client.retrieve_raw_image_data(
|
||||
'71c675ab-d94f-49cd-a114-e12490b328d9'
|
||||
)
|
||||
uri_regex = re.compile(
|
||||
'{0}/[\w\d]{{8}}-[\w\d]{{4}}-[\w\d]{{4}}-[\w\d]{{4}}-[\w\d]{{12}}'
|
||||
.format(self.images_uri)
|
||||
)
|
||||
assert re.match(uri_regex, actual_response.url) is not None
|
||||
assert HTTPretty.last_request.headers['X-Auth-Token'] == \
|
||||
'36a04b4e71484ab9aacb1d0ac95733fc'
|
||||
assert HTTPretty.last_request.headers['Content-Type'] == \
|
||||
'application/json'
|
||||
assert HTTPretty.last_request.headers['Accept'] == 'application/json'
|
||||
assert 200 == actual_response.status_code
|
||||
|
||||
def test_list_image_memberships(self):
|
||||
url = '{0}/members'.format(self.image_uri)
|
||||
HTTPretty.register_uri(HTTPretty.GET,
|
||||
url, body=self._build_list_image_membership())
|
||||
|
||||
actual_response = \
|
||||
self.images_client.list_image_membership(self.image_id)
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert url == actual_response.url
|
||||
|
||||
def _build_response_body(self):
|
||||
return '{"images": '\
|
||||
'[{"status": "active", '\
|
||||
'"name": "precise", '\
|
||||
'"deleted": false, '\
|
||||
'"container_format": "bare", '\
|
||||
'"created_at": "2013-04-29T19:32:56", '\
|
||||
'"disk_format": "qcow2", '\
|
||||
'"updated_at": "2013-04-29T19:32:56", '\
|
||||
'"properties": {}, '\
|
||||
'"min_disk": 0, '\
|
||||
'"protected": false, '\
|
||||
'"id": "46fd5b5c-b925-4316-a878-63cbbe7f0030", '\
|
||||
'"checksum": null, '\
|
||||
'"owner": "bd7531a57d3a47538fae1b89c169b293", '\
|
||||
'"is_public": true, '\
|
||||
'"deleted_at": null, '\
|
||||
'"min_ram": "0", '\
|
||||
'"size": "252116992"}]}'
|
||||
|
||||
def _get_querystring_data(self, querystring):
|
||||
querystring_data = {}
|
||||
for key in querystring:
|
||||
querystring_data[key] = querystring[key][0] or \
|
||||
int(querystring[key][0])
|
||||
|
||||
return querystring_data
|
||||
|
||||
def _build_list_image_membership(self):
|
||||
"""@summary: Get the members_list attribute (image.members_list)
|
||||
for an image with id=image_id"""
|
||||
|
||||
return {'members': 'members_list'}
|
||||
|
||||
def test_update_image(self):
|
||||
url = '{0}/71c675ab-d94f-49cd-a114-e12490b328d9'.format(
|
||||
self.images_uri)
|
||||
HTTPretty.register_uri(HTTPretty.PUT, url, body='Updated Image',
|
||||
headers=self.expected_headers)
|
||||
|
||||
actual_response = self.images_client.update_image(
|
||||
'71c675ab-d94f-49cd-a114-e12490b328d9',
|
||||
image_meta_is_public=False)
|
||||
|
||||
uri_regex = re.compile(
|
||||
'{0}/[\w\d]{{8}}-[\w\d]{{4}}-[\w\d]{{4}}-[\w\d]{{4}}-[\w\d]{{12}}'
|
||||
.format(self.images_uri)
|
||||
)
|
||||
assert re.match(uri_regex, actual_response.url) is not None
|
||||
assert HTTPretty.last_request.headers['X-Auth-Token'] == \
|
||||
'36a04b4e71484ab9aacb1d0ac95733fc'
|
||||
assert 200 == actual_response.status_code
|
||||
|
||||
def test_list_shared_images(self):
|
||||
member_id = ''
|
||||
shared_images_url = '{0}/shared-images/{1}'.format(
|
||||
GLANCE_API_SERVER_ENDPOINT,
|
||||
member_id
|
||||
)
|
||||
|
||||
HTTPretty.register_uri(HTTPretty.GET, shared_images_url,
|
||||
body=self._build_list_shared_images())
|
||||
|
||||
actual_response = self.images_client.list_shared_images(member_id)
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert shared_images_url == actual_response.url
|
||||
|
||||
def _build_list_shared_images(self):
|
||||
"""@summary: Get the shared_images attribute (member.shared_attributes)
|
||||
for a member with id=member_id"""
|
||||
|
||||
return {'shared_images': 'shared_images'}
|
||||
|
||||
def test_add_member_to_an_image(self):
|
||||
member_id = ''
|
||||
url = '{0}/members/{1}'.format(self.image_uri, member_id)
|
||||
HTTPretty.register_uri(HTTPretty.PUT, url, body={})
|
||||
|
||||
actual_response = self.images_client.add_member_to_image(self.image_id,
|
||||
member_id)
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert url == actual_response.url
|
||||
|
||||
def test_remove_member_from_an_image(self):
|
||||
member_id = ''
|
||||
url = '{0}/members/{1}'.format(self.image_uri, member_id)
|
||||
HTTPretty.register_uri(HTTPretty.DELETE, url, body={})
|
||||
|
||||
actual_response = \
|
||||
self.images_client.delete_member_from_image(self.image_id,
|
||||
member_id)
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert url == actual_response.url
|
||||
|
||||
def test_replace_members_list_for_an_image(self):
|
||||
url = '{0}/members'.format(self.image_uri)
|
||||
HTTPretty.register_uri(HTTPretty.PUT, url,
|
||||
body=self._build_members_list())
|
||||
|
||||
actual_response = \
|
||||
self.images_client.replace_members_list(self.image_id)
|
||||
|
||||
assert 200 == actual_response.status_code
|
||||
assert url == actual_response.url
|
||||
|
||||
def _build_members_list(self):
|
||||
return {'memberships': 'new_members_list'}
|
@ -1,8 +0,0 @@
|
||||
{"image": {
|
||||
"name": "cirros-0.3.1-x86_64-uec",
|
||||
"container_format": "ami",
|
||||
"disk_format": "ami",
|
||||
"checksum": "f8a2eeee2dc65b3d9b6e63678955bd83",
|
||||
"id": "48dd25eb-ada4-47e7-8b8c-9547c9263b52",
|
||||
"size": 25165824
|
||||
}}
|
@ -1,26 +0,0 @@
|
||||
{"images": [
|
||||
{
|
||||
"name": "cirros-0.3.1-x86_64-uec",
|
||||
"container_format": "ami",
|
||||
"disk_format": "ami",
|
||||
"checksum": "f8a2eeee2dc65b3d9b6e63678955bd83",
|
||||
"id": "48dd25eb-ada4-47e7-8b8c-9547c9263b52",
|
||||
"size": 25165824
|
||||
},
|
||||
{
|
||||
"name": "cirros-0.3.1-x86_64-uec-ramdisk",
|
||||
"container_format": "ari",
|
||||
"disk_format": "ari",
|
||||
"checksum": "69c33642f44ca552ba4bb8b66ad97e85",
|
||||
"id": "ba740a49-2b27-45a2-9fc6-d142b44698a9",
|
||||
"size": 3714968
|
||||
},
|
||||
{
|
||||
"name": "cirros-0.3.1-x86_64-uec-kernel",
|
||||
"container_format": "aki",
|
||||
"disk_format": "aki",
|
||||
"checksum": "c352f4e7121c6eae958bc1570324f17e",
|
||||
"id": "4057ba81-47da-406f-aa92-09bcb5b6ddcc",
|
||||
"size": 4955792
|
||||
}
|
||||
]}
|
@ -1,15 +0,0 @@
|
||||
"""
|
||||
Copyright 2013 Rackspace
|
||||
|
||||
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.
|
||||
"""
|
@ -1,98 +0,0 @@
|
||||
import json
|
||||
import os
|
||||
from copy import deepcopy
|
||||
from datetime import datetime
|
||||
|
||||
from cloudcafe.images.v1.models.image import Image
|
||||
from cloudcafe.images.v1.models.member import Member
|
||||
|
||||
|
||||
class TestImage(object):
|
||||
"""@summary: Testing the behaviors of the image model..."""
|
||||
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
cls.raw_image_str = open(os.path.join(
|
||||
os.path.dirname(__file__), '../data/image.json')).read()
|
||||
cls.raw_images_str = open(os.path.join(
|
||||
os.path.dirname(__file__), '../data/images.json')).read()
|
||||
|
||||
cls._dict = json.loads(cls.raw_image_str).get('image')
|
||||
|
||||
cls.image_one = Image(
|
||||
id_=cls._dict.get('id'),
|
||||
name=cls._dict.get('name'),
|
||||
container_format=cls._dict.get('container_format'),
|
||||
checksum=cls._dict.get('checksum'),
|
||||
size=cls._dict.get('size'),
|
||||
disk_format=cls._dict.get('disk_format'))
|
||||
|
||||
cls.image_two = Image(
|
||||
id_="c7dd539e-5077-49e8-bc4d-0359ba051122",
|
||||
status="active",
|
||||
name="precise",
|
||||
deleted="False",
|
||||
container_format="cirros",
|
||||
created_at=datetime.today(),
|
||||
disk_format="qcow2",
|
||||
updated_at=datetime.today(),
|
||||
owner="bd7531a57d3a47538fae1b89c169b293",
|
||||
protected="False",
|
||||
min_ram=0,
|
||||
checksum="",
|
||||
min_disk=0,
|
||||
is_public="True",
|
||||
deleted_at=datetime.today(),
|
||||
properties={},
|
||||
size=252116992)
|
||||
|
||||
def test_positive_equality_of_images(self):
|
||||
assert self.image_one == deepcopy(self.image_one)
|
||||
|
||||
def test_negative_equality_of_images(self):
|
||||
assert self.image_one != deepcopy(self.image_two)
|
||||
|
||||
def test_dict_to_obj(self):
|
||||
assert Image._dict_to_obj(self._dict) == self.image_one
|
||||
|
||||
def test_json_to_obj(self):
|
||||
assert Image._json_to_obj(self.raw_image_str) == self.image_one
|
||||
|
||||
def test_add_member_to_an_image(self):
|
||||
member1 = Member(member_id='1')
|
||||
|
||||
self.image_one.add_member(member1)
|
||||
|
||||
assert len(self.image_one.members_list) == 1
|
||||
assert member1 in self.image_one.members_list
|
||||
|
||||
def test_remove_member_from_an_image(self):
|
||||
image = Image._dict_to_obj(self._dict)
|
||||
member1 = Member(member_id='1')
|
||||
member2 = Member(member_id='2')
|
||||
|
||||
image.add_member(member1)
|
||||
image.add_member(member2)
|
||||
assert len(image.members_list) == 2
|
||||
|
||||
image.delete_member(member1)
|
||||
|
||||
assert len(image.members_list) == 1
|
||||
assert member1 not in image.members_list
|
||||
assert member2 in image.members_list
|
||||
|
||||
def test_replace_members_list_for_an_image(self):
|
||||
member1 = Member(member_id='1')
|
||||
member2 = Member(member_id='2')
|
||||
member3 = Member(member_id='3')
|
||||
|
||||
members_list = [member1, member2]
|
||||
image = self.image_one
|
||||
image.members_list = members_list
|
||||
|
||||
image.replace_members_list([member3])
|
||||
|
||||
assert len(image.members_list) == 1
|
||||
assert member1 not in image.members_list
|
||||
assert member2 not in image.members_list
|
||||
assert member3 in image.members_list
|
Loading…
x
Reference in New Issue
Block a user