Reorganizes blockstorage v1 testing infrastructure

* Removes empty integration package
* Fixes and expands blockstorage rehaviors, client and config.
* Renames volumes_api models to move them into the models namespace.

Change-Id: Ieba9f0b62c3abb15e2375b871e4f598ef4852f50
This commit is contained in:
Jose Idar
2013-12-04 11:10:33 -06:00
parent 6f76946fde
commit 211739be32
10 changed files with 235 additions and 197 deletions

View File

@@ -1,26 +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.behaviors import BaseBehavior, behavior
from cloudcafe.compute.servers_api.client import ServersClient
class ComputeBlockStorageBehaviors(BaseBehavior):
@behavior(ServersClient)
def attach_volume_to_server(*args, **kwargs):
pass

View File

@@ -19,8 +19,7 @@ from time import time, sleep
from cafe.engine.behaviors import BaseBehavior, behavior
from cloudcafe.blockstorage.v1.volumes_api.client import VolumesClient
from cloudcafe.blockstorage.v1.volumes_api.config import VolumesAPIConfig
from cloudcafe.blockstorage.v1.volumes_api.models.statuses import \
VolumeStatuses, SnapshotStatuses
from cloudcafe.blockstorage.v1.volumes_api.models import statuses
class VolumesAPIBehaviorException(Exception):
@@ -39,25 +38,25 @@ class VolumesAPI_Behaviors(BaseBehavior):
size=None, timeout=None, min_timeout=None, max_timeout=None,
wait_per_gb=None):
if wait_per_gb is not None:
if size is not None:
if wait_per_gb is not None and size is not None:
timeout = timeout or size * wait_per_gb
if min_timeout is not None:
timeout = timeout if timeout > min_timeout else min_timeout
timeout = max(timeout, min_timeout)
if max_timeout is not None:
timeout = timeout if timeout < max_timeout else max_timeout
timeout = min(timeout, max_timeout)
return timeout
@behavior(VolumesClient)
def wait_for_volume_status(
self, volume_id, expected_status, timeout, wait_period=None):
''' Waits for a specific status and returns a BehaviorResponse object
when that status is observed.
Note: Shouldn't be used for transient statuses like 'deleting'.
'''
""" Waits for a specific status and returns None when that status is
observed.
Note: Unreliable for transient statuses like 'deleting'.
"""
wait_period = float(
wait_period or self.config.volume_status_poll_frequency)
end_time = time() + int(timeout)
@@ -67,21 +66,24 @@ class VolumesAPI_Behaviors(BaseBehavior):
if not resp.ok:
msg = (
"get_volume_info() call failed with status_code {0} "
"while waiting for volume to reach the {1} status".format(
"wait_for_volume_status() failure: "
"get_volume_info() call failed with status_code {0} while "
"waiting for volume to reach the {1} status".format(
resp.status_code, expected_status))
self._log.error(msg)
raise VolumesAPIBehaviorException(msg)
if resp.entity is None:
msg = (
"wait_for_volume_status() failure: "
"get_volume_info() response body did not deserialize.")
self._log.error(msg)
raise VolumesAPIBehaviorException(msg)
if resp.entity.status == expected_status:
self._log.info(
'Volume status "{0}" observed as expected'.format(
"wait_for_volume_status() failure: "
'Expected Volume status "{0}" observed as expected'.format(
expected_status))
break
@@ -98,10 +100,11 @@ class VolumesAPI_Behaviors(BaseBehavior):
@behavior(VolumesClient)
def wait_for_snapshot_status(
self, snapshot_id, expected_status, timeout, wait_period=None):
''' Waits for a specific status and returns a BehaviorResponse object
when that status is observed.
Note: Shouldn't be used for transient statuses like 'deleting'.
'''
""" Waits for a specific status and returns None when that status is
observed.
Note: Unreliable for transient statuses like 'deleting'.
"""
wait_period = float(
wait_period or self.config.snapshot_status_poll_frequency)
end_time = time() + int(timeout)
@@ -111,6 +114,7 @@ class VolumesAPI_Behaviors(BaseBehavior):
if not resp.ok:
msg = (
"wait_for_snapshot_status() failure: "
"get_snapshot_info() call failed with status_code {0} "
"while waiting for snapshot status".format(
resp.status_code))
@@ -119,14 +123,15 @@ class VolumesAPI_Behaviors(BaseBehavior):
if resp.entity is None:
msg = (
"wait_for_snapshot_status() failure: "
"get_snapshot_info() response body did not deserialize as "
"expected")
self._log.error(msg)
raise VolumesAPIBehaviorException(msg)
if resp.entity.status == expected_status:
self._log.info('Snapshot status "{0}" observed'.format(
expected_status))
self._log.info('Expected Snapshot status "{0}" observed'
.format(expected_status))
break
sleep(wait_period)
@@ -141,19 +146,18 @@ class VolumesAPI_Behaviors(BaseBehavior):
@behavior(VolumesClient)
def create_available_volume(
self, display_name, size, volume_type, display_description=None,
metadata=None, availability_zone=None, timeout=None):
self, size, volume_type, display_name=None,
display_description=None, availability_zone=None, metadata=None,
timeout=None):
expected_status = VolumeStatuses.AVAILABLE
expected_status = statuses.Volume.AVAILABLE
metadata = metadata or {}
timeout = self._calculate_timeout(
size=size, timeout=timeout,
max_timeout=self.config.volume_create_max_timeout)
timeout = timeout or self.config.volume_create_timeout
self._log.info("create_available_volume() is creating a volume")
resp = self.client.create_volume(
display_name=display_name, size=size, volume_type=volume_type,
size, volume_type, display_name=display_name,
display_description=display_description, metadata=metadata,
availability_zone=availability_zone)
@@ -179,9 +183,9 @@ class VolumesAPI_Behaviors(BaseBehavior):
@behavior(VolumesClient)
def create_available_snapshot(
self, volume_id, display_name=None, display_description=None,
force_create='False', name=None, timeout=None):
force_create=True, timeout=None):
expected_status = SnapshotStatuses.AVAILABLE
expected_status = statuses.Snapshot.AVAILABLE
#Try and get volume size
vol_size = None
@@ -201,11 +205,10 @@ class VolumesAPI_Behaviors(BaseBehavior):
self._log.debug(
"create_available_snapshot() timeout set to {0}".format(timeout))
self._log.info("create_available_snapshot() is creating a snapshot")
self._log.info("create_available_snapshot is creating a snapshot")
resp = self.client.create_snapshot(
volume_id, display_name=display_name,
display_description=display_description,
force_create=force_create, name=name)
volume_id, force=force_create, display_name=display_name,
display_description=display_description)
if not resp.ok:
msg = (
@@ -253,7 +256,7 @@ class VolumesAPI_Behaviors(BaseBehavior):
@behavior(VolumesClient)
def delete_volume_confirmed(
self, volume_id, size=None, timeout=None, wait_period=None):
'''Returns True if volume deleted, False otherwise'''
"""Returns True if volume deleted, False otherwise"""
timeout = self._calculate_timeout(
size=size, timeout=timeout,
@@ -272,7 +275,7 @@ class VolumesAPI_Behaviors(BaseBehavior):
while time() < end:
#issue DELETE request on volume
resp = self.client.delete_volume(volume_id)
if not resp.ok:
if not resp.ok and resp.status_code != 404:
msg = (
"delete_volume_confirmed() call to delete_volume() failed "
"with a '{0}'".format(resp.status_code))
@@ -309,7 +312,7 @@ class VolumesAPI_Behaviors(BaseBehavior):
@behavior(VolumesClient)
def delete_snapshot_confirmed(
self, snapshot_id, vol_size=None, timeout=None, wait_period=None):
'''Returns True if snapshot deleted, False otherwise'''
"""Returns True if snapshot deleted, False otherwise"""
timeout = self._calculate_timeout(
size=vol_size, timeout=timeout,
@@ -365,7 +368,7 @@ class VolumesAPI_Behaviors(BaseBehavior):
@behavior(VolumesClient)
def delete_volume_with_snapshots_confirmed(self, volume_id):
'''Returns True if volume deleted, False otherwise'''
"""Returns True if volume deleted, False otherwise"""
#Attempt to delete all snapshots associated with provided volume_id
snapshots = self.list_volume_snapshots(volume_id)

View File

@@ -15,14 +15,13 @@ limitations under the License.
"""
from cafe.engine.clients.rest import AutoMarshallingRestClient
from cloudcafe.blockstorage.v1.volumes_api.models.requests.volumes_api import (
Volume as VolumeRequest,
VolumeSnapshot as VolumeSnapshotRequest)
from cloudcafe.blockstorage.v1.volumes_api.models.requests import (
VolumeRequest,
VolumeSnapshotRequest)
from cloudcafe.blockstorage.v1.volumes_api.models.responses.volumes_api import(
Volume as VolumeResponse,
VolumeSnapshot as VolumeSnapshotResponse,
VolumeType, VolumeList, VolumeTypeList, VolumeSnapshotList)
from cloudcafe.blockstorage.v1.volumes_api.models.responses import(
VolumeResponse, VolumeSnapshotResponse, VolumeTypeResponse,
VolumeListResponse, VolumeTypeListResponse, VolumeSnapshotListResponse)
class VolumesClient(AutoMarshallingRestClient):
@@ -36,14 +35,14 @@ class VolumesClient(AutoMarshallingRestClient):
self.url = url
self.auth_token = auth_token
self.default_headers['X-Auth-Token'] = auth_token
self.default_headers['Content-Type'] = 'application/%s' % \
self.serialize_format
self.default_headers['Accept'] = 'application/%s' % \
self.deserialize_format
self.default_headers['Content-Type'] = 'application/{0}'.format(
self.serialize_format)
self.default_headers['Accept'] = 'application/{0}'.format(
self.deserialize_format)
def create_volume(
self, display_name, size, volume_type, availability_zone=None,
metadata={}, display_description=None, snapshot_id=None,
self, size, volume_type, display_name=None,
display_description=None, availability_zone=None, metadata=None,
requestslib_kwargs=None):
'''POST v1/{tenant_id}/volumes'''
@@ -51,22 +50,22 @@ class VolumesClient(AutoMarshallingRestClient):
url = '{0}/volumes'.format(self.url)
volume_request_entity = VolumeRequest(
display_name=display_name,
size=size,
volume_type=volume_type,
display_name=display_name,
display_description=display_description,
metadata=metadata,
availability_zone=availability_zone,
snapshot_id=snapshot_id)
metadata=metadata)
return self.request(
'POST', url, response_entity_type=VolumeResponse,
'POST', url,
response_entity_type=VolumeResponse,
request_entity=volume_request_entity,
requestslib_kwargs=requestslib_kwargs)
def create_volume_from_snapshot(
self, snapshot_id, size, display_name='', volume_type=None,
availability_zone=None, display_description='', metadata={},
self, snapshot_id, size, volume_type, display_name=None,
display_description=None, availability_zone=None, metadata=None,
requestslib_kwargs=None):
'''POST v1/{tenant_id}/volumes'''
@@ -74,16 +73,17 @@ class VolumesClient(AutoMarshallingRestClient):
url = '{0}/volumes'.format(self.url)
volume_request_entity = VolumeRequest(
display_name=display_name,
size=size,
volume_type=volume_type,
display_name=display_name,
display_description=display_description,
metadata=metadata,
availability_zone=availability_zone,
metadata=metadata,
snapshot_id=snapshot_id)
return self.request(
'POST', url, response_entity_type=VolumeResponse,
'POST', url,
response_entity_type=VolumeResponse,
request_entity=volume_request_entity,
requestslib_kwargs=requestslib_kwargs)
@@ -93,7 +93,7 @@ class VolumesClient(AutoMarshallingRestClient):
url = '{0}/volumes'.format(self.url)
return self.request(
'GET', url, response_entity_type=VolumeList,
'GET', url, response_entity_type=VolumeListResponse,
requestslib_kwargs=requestslib_kwargs)
def list_all_volumes_info(self, requestslib_kwargs=None):
@@ -102,7 +102,7 @@ class VolumesClient(AutoMarshallingRestClient):
url = '{0}/volumes/detail'.format(self.url)
return self.request(
'GET', url, response_entity_type=VolumeList,
'GET', url, response_entity_type=VolumeListResponse,
requestslib_kwargs=requestslib_kwargs)
def get_volume_info(self, volume_id, requestslib_kwargs=None):
@@ -130,7 +130,7 @@ class VolumesClient(AutoMarshallingRestClient):
url = '{0}/types'.format(self.url)
return self.request(
'GET', url, response_entity_type=VolumeTypeList,
'GET', url, response_entity_type=VolumeTypeListResponse,
requestslib_kwargs=requestslib_kwargs)
def get_volume_type_info(self, volume_type_id, requestslib_kwargs=None):
@@ -139,13 +139,13 @@ class VolumesClient(AutoMarshallingRestClient):
url = '{0}/types/{1}'.format(self.url, volume_type_id)
return self.request(
'GET', url, response_entity_type=VolumeType,
'GET', url, response_entity_type=VolumeTypeResponse,
requestslib_kwargs=requestslib_kwargs)
#Volume Snapshot API
def create_snapshot(
self, volume_id, display_name=None, display_description=None,
force_create=False, name=None, requestslib_kwargs=None):
force_create=False, requestslib_kwargs=None):
'''POST v1/{tenant_id}/snapshots'''
@@ -155,11 +155,11 @@ class VolumesClient(AutoMarshallingRestClient):
volume_id,
force=force_create,
display_name=display_name,
name=name,
display_description=display_description)
return self.request(
'POST', url, response_entity_type=VolumeSnapshotResponse,
'POST', url,
response_entity_type=VolumeSnapshotResponse,
request_entity=volume_snapshot_request_entity,
requestslib_kwargs=requestslib_kwargs)
@@ -170,7 +170,7 @@ class VolumesClient(AutoMarshallingRestClient):
url = '{0}/snapshots'.format(self.url)
return self.request(
'GET', url, response_entity_type=VolumeSnapshotList,
'GET', url, response_entity_type=VolumeSnapshotListResponse,
requestslib_kwargs=requestslib_kwargs)
def list_all_snapshots_info(self, requestslib_kwargs=None):
@@ -179,7 +179,7 @@ class VolumesClient(AutoMarshallingRestClient):
url = '{0}/snapshots/detail'.format(self.url)
return self.request(
'GET', url, response_entity_type=VolumeSnapshotList,
'GET', url, response_entity_type=VolumeSnapshotListResponse,
requestslib_kwargs=requestslib_kwargs)
def get_snapshot_info(self, snapshot_id, requestslib_kwargs=None):

View File

@@ -46,7 +46,7 @@ class VolumesAPIConfig(ConfigSectionInterface):
return self.get("volume_status_poll_frequency", default=None)
@property
def volume_create_max_timeout(self):
def volume_create_timeout(self):
return self.get("volume_create_timeout", default=None)
@property
@@ -79,7 +79,7 @@ class VolumesAPIConfig(ConfigSectionInterface):
def snapshot_create_base_timeout(self):
"""Amount of time added by default to calculated snapshot create
timeouts"""
return self.get("snapshot_create_min_timeout", default=0)
return self.get("snapshot_create_base_timeout", default=0)
@property
def snapshot_create_wait_per_gigabyte(self):

View File

@@ -20,48 +20,43 @@ from xml.etree import ElementTree
from cafe.engine.models.base import AutoMarshallingModel
class Volume(AutoMarshallingModel):
obj_model_key = "volume"
class VolumeRequest(AutoMarshallingModel):
def __init__(
self, display_name=None, size=None, volume_type=None,
self, size=None, volume_type=None, display_name=None,
display_description=None, metadata=None, availability_zone=None,
snapshot_id=None, attachments=None):
self.display_name = display_name
self.display_description = display_description
self.size = size
self.volume_type = volume_type
self.metadata = metadata or {}
self.display_name = display_name
self.display_description = display_description
self.metadata = metadata or dict()
self.availability_zone = availability_zone
self.snapshot_id = snapshot_id
def _obj_to_json(self):
return json.dumps(self._obj_to_json_dict())
def _obj_to_json_dict(self):
sub_dict = {}
sub_dict["display_name"] = self.display_name
sub_dict["display_description"] = self.display_description
sub_dict["size"] = self.size
sub_dict["volume_type"] = self.volume_type
sub_dict["metadata"] = self.metadata
sub_dict["availability_zone"] = self.availability_zone
sub_dict['snapshot_id'] = self.snapshot_id
volume_attrs = {
"size": self.size,
"volume_type": self.volume_type,
"display_name": self.display_name,
"display_description": self.display_description,
"metadata": self.metadata,
"availability_zone": self.availability_zone}
root_dict = {}
root_dict[self.obj_model_key] = self._remove_empty_values(sub_dict)
return root_dict
return {'volume': self._remove_empty_values(volume_attrs)}
def _obj_to_xml_ele(self):
element = ElementTree.Element(self.obj_model_key)
attrs = {}
attrs["display_name"] = self.display_name
attrs["display_description"] = self.display_description
attrs["size"] = str(self.size)
attrs["volume_type"] = self.volume_type
attrs["availability_zone"] = self.availability_zone
element = self._set_xml_etree_element(element, attrs)
element = ElementTree.Element('volume')
volume_attrs = {
"size": self.size,
"volume_type": self.volume_type,
"display_name": self.display_name,
"display_description": self.display_description,
"availability_zone": self.availability_zone}
element = self._set_xml_etree_element(element, volume_attrs)
if len(self.metadata.keys()) > 0:
metadata_element = ElementTree.Element('metadata')
@@ -78,44 +73,37 @@ class Volume(AutoMarshallingModel):
return ElementTree.tostring(self._obj_to_xml_ele())
class VolumeSnapshot(AutoMarshallingModel):
obj_model_key = "snapshot"
class VolumeSnapshotRequest(AutoMarshallingModel):
def __init__(
self, volume_id, force=True, display_name=None, name=None,
display_description=None):
self, volume_id, display_name=None,
display_description=None, force=True):
self.force = force
self.display_name = display_name
self.volume_id = volume_id
self.name = name
self.display_name = display_name
self.display_description = display_description
self.force = force
def _obj_to_json(self):
return json.dumps(self._obj_to_json_dict())
def _obj_to_json_dict(self):
attrs = {}
attrs["snapshot"] = {}
snapshot_attrs = {
"volume_id": self.volume_id,
"display_name": self.display_name,
"display_description": self.display_description,
"force": self.force}
sub_attrs = {}
sub_attrs["volume_id"] = self.volume_id
sub_attrs["force"] = self.force
sub_attrs["display_name"] = self.display_name
sub_attrs["display_description"] = self.display_description
attrs[self.obj_model_key] = self._remove_empty_values(sub_attrs)
return self._remove_empty_values(attrs)
return {"snapshot": self._remove_empty_values(snapshot_attrs)}
def _obj_to_xml(self):
return ElementTree.tostring(self._obj_to_xml_ele())
def _obj_to_xml_ele(self):
element = ElementTree.Element(self.obj_model_key)
attrs = {}
attrs["volume_id"] = self.volume_id
attrs["force"] = str(self.force)
attrs["display_name"] = self.display_name
attrs["display_description"] = self.display_description
element = self._set_xml_etree_element(element, attrs)
return element
element = ElementTree.Element('snapshot')
snapshot_attrs = {
"volume_id": self.volume_id,
"display_name": self.display_name,
"display_description": self.display_description,
"force": str(self.force)}
return self._set_xml_etree_element(element, snapshot_attrs)

View File

@@ -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.
"""

View File

@@ -83,7 +83,7 @@ class _VolumesAPIBaseListModel(AutoMarshallingListModel):
return obj_list
class Volume(_VolumesAPIBaseModel):
class VolumeResponse(_VolumesAPIBaseModel):
obj_model_key = 'volume'
kwarg_map = {
"id_": "id",
@@ -101,10 +101,8 @@ class Volume(_VolumesAPIBaseModel):
def __init__(
self, id_=None, display_name=None, size=None, volume_type=None,
display_description=None, metadata=None, availability_zone=None,
snapshot_id=None, attachments=None, created_at=None, status=None,
xmlns=None):
snapshot_id=None, attachments=None, created_at=None, status=None):
#Common attributes
self.id_ = id_
self.display_name = display_name
self.display_description = display_description
@@ -116,10 +114,9 @@ class Volume(_VolumesAPIBaseModel):
self.attachments = attachments
self.created_at = created_at
self.status = status
self.xmlns = xmlns
class VolumeSnapshot(_VolumesAPIBaseModel):
class VolumeSnapshotResponse(_VolumesAPIBaseModel):
obj_model_key = 'snapshot'
kwarg_map = {
"id_": "id",
@@ -128,13 +125,11 @@ class VolumeSnapshot(_VolumesAPIBaseModel):
"display_description": "display_description",
"status": "status",
"size": "size",
"created_at": "created_at",
"name": "name"}
"created_at": "created_at"}
def __init__(
self, id_=None, volume_id=None, display_name=None,
display_description=None, status=None, size=None, created_at=None,
name=None):
display_description=None, status=None, size=None, created_at=None):
self.id_ = id_
self.volume_id = volume_id
@@ -143,10 +138,9 @@ class VolumeSnapshot(_VolumesAPIBaseModel):
self.status = status
self.size = size
self.created_at = created_at
self.name = name
class VolumeType(_VolumesAPIBaseModel):
class VolumeTypeResponse(_VolumesAPIBaseModel):
obj_model_key = "volume_type"
kwarg_map = {
"id_": "id",
@@ -160,16 +154,16 @@ class VolumeType(_VolumesAPIBaseModel):
self.extra_specs = extra_specs
class VolumeList(_VolumesAPIBaseListModel):
class VolumeListResponse(_VolumesAPIBaseListModel):
list_model_key = 'volumes'
ObjectModel = Volume
ObjectModel = VolumeResponse
class VolumeSnapshotList(_VolumesAPIBaseListModel):
class VolumeSnapshotListResponse(_VolumesAPIBaseListModel):
list_model_key = 'snapshots'
ObjectModel = VolumeSnapshot
ObjectModel = VolumeSnapshotResponse
class VolumeTypeList(_VolumesAPIBaseListModel):
class VolumeTypeListResponse(_VolumesAPIBaseListModel):
list_model_key = 'volume_types'
ObjectModel = VolumeType
ObjectModel = VolumeTypeResponse

View File

@@ -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.
"""

View File

@@ -15,15 +15,16 @@ limitations under the License.
"""
class _CommonStatuses(object):
class _CommonStatus(object):
AVAILABLE = "available"
DELETING = "deleting"
CREATING = "creating"
class VolumeStatuses(_CommonStatuses):
class Volume(_CommonStatus):
ATTACHING = "attaching"
IN_USE = "in-use"
class SnapshotStatuses(_CommonStatuses):
class Snapshot(_CommonStatus):
pass

View File

@@ -0,0 +1,108 @@
"""
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 json
from xml.etree import ElementTree
from cafe.engine.models.base import AutoMarshallingModel
class VolumeRequest(AutoMarshallingModel):
def __init__(
self, size=None, volume_type=None, name=None,
description=None, metadata=None, availability_zone=None):
self.size = size
self.volume_type = volume_type
self.name = name
self.description = description
self.metadata = metadata or dict()
self.availability_zone = availability_zone
def _obj_to_json(self):
return json.dumps(self._obj_to_json_dict())
def _obj_to_json_dict(self):
volume_attrs = {
"size": self.size,
"volume_type": self.volume_type,
"name": self.name,
"description": self.description,
"metadata": self.metadata,
"availability_zone": self.availability_zone}
return {'volume': self._remove_empty_values(volume_attrs)}
def _obj_to_xml_ele(self):
element = ElementTree.Element('volume')
volume_attrs = {
"size": self.size,
"volume_type": self.volume_type,
"name": self.name,
"description": self.description,
"availability_zone": self.availability_zone}
element = self._set_xml_etree_element(element, volume_attrs)
if len(self.metadata.keys()) > 0:
metadata_element = ElementTree.Element('metadata')
for key in self.metadata.keys():
meta_element = ElementTree.Element('meta')
meta_element.set('key', key)
meta_element.text = self.metadata[key]
metadata_element.append(meta_element)
element.append(metadata_element)
return element
def _obj_to_xml(self):
return ElementTree.tostring(self._obj_to_xml_ele())
class VolumeSnapshotRequest(AutoMarshallingModel):
def __init__(
self, volume_id, force=True, name=None,
description=None):
self.volume_id = volume_id
self.name = name
self.description = description
self.force = force
def _obj_to_json(self):
return json.dumps(self._obj_to_json_dict())
def _obj_to_json_dict(self):
snapshot_attrs = {
"volume_id": self.volume_id,
"name": self.name,
"description": self.description,
"force": self.force}
return {"snapshot": self._remove_empty_values(snapshot_attrs)}
def _obj_to_xml(self):
return ElementTree.tostring(self._obj_to_xml_ele())
def _obj_to_xml_ele(self):
element = ElementTree.Element('snapshot')
snapshot_attrs = {
"volume_id": self.volume_id,
"name": self.name,
"description": self.description,
"force": str(self.force)}
return self._set_xml_etree_element(element, snapshot_attrs)