Merge "Volume Attachments API Buildout"
This commit is contained in:
@@ -51,14 +51,14 @@ class VolumesAPI_Behaviors(BaseBehavior):
|
|||||||
|
|
||||||
@behavior(VolumesClient)
|
@behavior(VolumesClient)
|
||||||
def wait_for_volume_status(
|
def wait_for_volume_status(
|
||||||
self, volume_id, expected_status, timeout, wait_period=None):
|
self, volume_id, expected_status, timeout, poll_rate=5):
|
||||||
""" Waits for a specific status and returns None when that status is
|
""" Waits for a specific status and returns None when that status is
|
||||||
observed.
|
observed.
|
||||||
Note: Unreliable for transient statuses like 'deleting'.
|
Note: Unreliable for transient statuses like 'deleting'.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
wait_period = float(
|
poll_rate = int(
|
||||||
wait_period or self.config.volume_status_poll_frequency)
|
poll_rate or self.config.volume_status_poll_frequency)
|
||||||
end_time = time() + int(timeout)
|
end_time = time() + int(timeout)
|
||||||
|
|
||||||
while time() < end_time:
|
while time() < end_time:
|
||||||
@@ -68,7 +68,7 @@ class VolumesAPI_Behaviors(BaseBehavior):
|
|||||||
msg = (
|
msg = (
|
||||||
"wait_for_volume_status() failure: "
|
"wait_for_volume_status() failure: "
|
||||||
"get_volume_info() call failed with status_code {0} while "
|
"get_volume_info() call failed with status_code {0} while "
|
||||||
"waiting for volume to reach the {1} status".format(
|
"waiting for volume to reach the '{1}' status".format(
|
||||||
resp.status_code, expected_status))
|
resp.status_code, expected_status))
|
||||||
self._log.error(msg)
|
self._log.error(msg)
|
||||||
raise VolumesAPIBehaviorException(msg)
|
raise VolumesAPIBehaviorException(msg)
|
||||||
@@ -86,8 +86,7 @@ class VolumesAPI_Behaviors(BaseBehavior):
|
|||||||
'Expected Volume status "{0}" observed as expected'.format(
|
'Expected Volume status "{0}" observed as expected'.format(
|
||||||
expected_status))
|
expected_status))
|
||||||
break
|
break
|
||||||
|
sleep(poll_rate)
|
||||||
sleep(wait_period)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
msg = (
|
msg = (
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
from cafe.engine.clients.rest import AutoMarshallingRestClient
|
from cafe.engine.clients.rest import AutoMarshallingRestClient
|
||||||
from cloudcafe.blockstorage.v1.volumes_api.models.requests import (
|
from cloudcafe.blockstorage.v1.volumes_api.models.requests import (
|
||||||
VolumeRequest,
|
VolumeRequest, VolumeSnapshotRequest)
|
||||||
VolumeSnapshotRequest)
|
|
||||||
|
|
||||||
from cloudcafe.blockstorage.v1.volumes_api.models.responses import(
|
from cloudcafe.blockstorage.v1.volumes_api.models.responses import(
|
||||||
VolumeResponse, VolumeSnapshotResponse, VolumeTypeResponse,
|
VolumeResponse, VolumeSnapshotResponse, VolumeTypeResponse,
|
||||||
|
|||||||
96
cloudcafe/compute/volume_attachments_api/behaviors.py
Normal file
96
cloudcafe/compute/volume_attachments_api/behaviors.py
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
from time import sleep, time
|
||||||
|
from cafe.engine.behaviors import BaseBehavior, behavior
|
||||||
|
from cloudcafe.compute.volume_attachments_api.client import \
|
||||||
|
VolumeAttachmentsAPIClient
|
||||||
|
from cloudcafe.compute.volume_attachments_api.config import \
|
||||||
|
VolumeAttachmentsAPIConfig
|
||||||
|
from cloudcafe.blockstorage.v1.volumes_api.client import VolumesClient
|
||||||
|
from cloudcafe.blockstorage.v1.volumes_api.config import VolumesAPIConfig
|
||||||
|
from cloudcafe.blockstorage.v1.volumes_api.behaviors import \
|
||||||
|
VolumesAPI_Behaviors
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeAttachmentBehaviorError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeAttachmentsAPI_Behaviors(BaseBehavior):
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, volume_attachments_client=None, volumes_client=None,
|
||||||
|
volume_attachments_config=None, volumes_config=None):
|
||||||
|
|
||||||
|
self.client = volume_attachments_client
|
||||||
|
self.config = volume_attachments_config or VolumeAttachmentsAPIConfig()
|
||||||
|
|
||||||
|
self.volumes_client = volumes_client
|
||||||
|
self.volumes_behaviors = VolumesAPI_Behaviors(volumes_client)
|
||||||
|
self.volumes_config = volumes_config or VolumesAPIConfig()
|
||||||
|
|
||||||
|
@behavior(VolumeAttachmentsAPIClient)
|
||||||
|
def wait_for_attachment_to_propagate(
|
||||||
|
self, attachment_id, server_id, timeout=None, poll_rate=5):
|
||||||
|
|
||||||
|
timeout = timeout or self.config.attachment_propagation_timeout
|
||||||
|
poll_rate = poll_rate or self.config.api_poll_rate
|
||||||
|
endtime = time() + int(timeout)
|
||||||
|
while time() < endtime:
|
||||||
|
resp = \
|
||||||
|
self.client.get_volume_attachment_details(
|
||||||
|
attachment_id, server_id)
|
||||||
|
if resp.ok:
|
||||||
|
return True
|
||||||
|
sleep(poll_rate)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@behavior(VolumeAttachmentsAPIClient, VolumesClient)
|
||||||
|
def attach_volume_to_server(
|
||||||
|
self, server_id, volume_id, device=None,
|
||||||
|
expected_volume_status='in-use', volume_status_timeout=120,
|
||||||
|
attachment_propagation_timeout=60):
|
||||||
|
"""Returns a VolumeAttachment object"""
|
||||||
|
|
||||||
|
attachment_propagation_timeout = (
|
||||||
|
attachment_propagation_timeout
|
||||||
|
or self.config.attachment_propagation_timeout)
|
||||||
|
|
||||||
|
resp = self.client.attach_volume(server_id, volume_id, device=device)
|
||||||
|
|
||||||
|
if not resp.ok:
|
||||||
|
raise VolumeAttachmentBehaviorError(
|
||||||
|
"Volume attachment failed in auto_attach_volume_to_server"
|
||||||
|
" with a {0}. Could not attach volume {1} to server {2}"
|
||||||
|
.format(resp.status_code, volume_id, server_id))
|
||||||
|
|
||||||
|
if resp.entity is None:
|
||||||
|
raise VolumeAttachmentBehaviorError(
|
||||||
|
"Volume attachment failed in auto_attach_volume_to_server."
|
||||||
|
" Could not deserialize volume attachment response body. Could"
|
||||||
|
" not attach volume {1} to server {2}".format(
|
||||||
|
volume_id, server_id))
|
||||||
|
|
||||||
|
attachment = resp.entity
|
||||||
|
|
||||||
|
#Confirm volume attachment propagation
|
||||||
|
propagated = self.wait_for_attachment_to_propagate(
|
||||||
|
attachment.id_, server_id, timeout=attachment_propagation_timeout)
|
||||||
|
|
||||||
|
if not propagated:
|
||||||
|
raise VolumeAttachmentBehaviorError(
|
||||||
|
"Volume attachment {0} belonging to server {1} failed to"
|
||||||
|
"propagate to the relevant cell within {2} seconds".format(
|
||||||
|
attachment.id_, server_id, attachment_propagation_timeout))
|
||||||
|
|
||||||
|
# Confirm volume status
|
||||||
|
try:
|
||||||
|
self.volumes_behaviors.wait_for_volume_status(
|
||||||
|
volume_id, expected_volume_status, volume_status_timeout)
|
||||||
|
|
||||||
|
except:
|
||||||
|
raise VolumeAttachmentBehaviorError(
|
||||||
|
"Volume did not attain the '{0}' status within {1}"
|
||||||
|
"seconds after being attached to a server".format(
|
||||||
|
expected_volume_status, volume_status_timeout))
|
||||||
|
|
||||||
|
return attachment
|
||||||
@@ -15,84 +15,80 @@ limitations under the License.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from cafe.engine.clients.rest import AutoMarshallingRestClient
|
from cafe.engine.clients.rest import AutoMarshallingRestClient
|
||||||
from cloudcafe.compute.volume_attachments_api.models.requests. \
|
from cloudcafe.compute.volume_attachments_api.models.requests import \
|
||||||
volume_attachments import VolumeAttachmentRequest
|
VolumeAttachmentRequest
|
||||||
from cloudcafe.compute.volume_attachments_api.models.responses. \
|
from cloudcafe.compute.volume_attachments_api.models.responses import \
|
||||||
volume_attachments import VolumeAttachmentListResponse
|
VolumeAttachmentResponse, VolumeAttachmentListResponse
|
||||||
|
|
||||||
|
|
||||||
class VolumeAttachmentsAPIClient(AutoMarshallingRestClient):
|
class VolumeAttachmentsAPIClient(AutoMarshallingRestClient):
|
||||||
|
|
||||||
def __init__(self, url, auth_token, tenant_id, serialize_format=None,
|
def __init__(
|
||||||
deserialize_format=None):
|
self, url, auth_token, serialize_format=None,
|
||||||
|
deserialize_format=None):
|
||||||
|
|
||||||
super(VolumeAttachmentsAPIClient, self).__init__(
|
super(VolumeAttachmentsAPIClient, self).__init__(
|
||||||
serialize_format, deserialize_format)
|
serialize_format, deserialize_format)
|
||||||
|
|
||||||
self.url = url
|
url = url.rstrip('/')
|
||||||
self.auth_token = auth_token
|
self.auth_token = auth_token
|
||||||
self.tenant_id = tenant_id
|
|
||||||
self.default_headers['X-Auth-Token'] = auth_token
|
self.default_headers['X-Auth-Token'] = auth_token
|
||||||
self.default_headers['Content-Type'] = 'application/{0}'.format(
|
self.default_headers['Content-Type'] = 'application/{0}'.format(
|
||||||
self.serialize_format)
|
self.serialize_format)
|
||||||
self.default_headers['Accept'] = 'application/{0}'.format(
|
self.default_headers['Accept'] = 'application/{0}'.format(
|
||||||
self.deserialize_format)
|
self.deserialize_format)
|
||||||
|
|
||||||
|
self.url = "{0}/servers/{1}/os-volume_attachments".format(
|
||||||
|
url, "{server_id}")
|
||||||
|
|
||||||
def attach_volume(self, server_id, volume_id, device=None,
|
def attach_volume(self, server_id, volume_id, device=None,
|
||||||
requestslib_kwargs=None):
|
requestslib_kwargs=None):
|
||||||
"""
|
"""
|
||||||
POST
|
POST
|
||||||
v2/{tenant_id}/servers/{server_id}/os-volume_attachments
|
servers/{server_id}/os-volume_attachments
|
||||||
"""
|
"""
|
||||||
|
|
||||||
url = '{0}/servers/{1}/os-volume_attachments'.format(
|
url = self.url.format(server_id=server_id)
|
||||||
self.url, server_id)
|
req_ent = VolumeAttachmentRequest(volume_id, device)
|
||||||
va = VolumeAttachmentRequest(volume_id, device)
|
|
||||||
return self.request(
|
return self.request(
|
||||||
'POST', url, response_entity_type=VolumeAttachmentListResponse,
|
'POST', url, response_entity_type=VolumeAttachmentResponse,
|
||||||
request_entity=va, requestslib_kwargs=requestslib_kwargs)
|
request_entity=req_ent, requestslib_kwargs=requestslib_kwargs)
|
||||||
|
|
||||||
def delete_volume_attachment(self, attachment_id, server_id,
|
def delete_volume_attachment(self, attachment_id, server_id,
|
||||||
requestslib_kwargs=None):
|
requestslib_kwargs=None):
|
||||||
"""
|
"""
|
||||||
DELETE
|
DELETE
|
||||||
v2/servers/{server_id}/os-volume_attachments/{attachment_id}
|
servers/{server_id}/os-volume_attachments/{attachment_id}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
url = '{0}/servers/{1}/os-volume_attachments/{2}'.format(
|
url = "{0}/{1}".format(
|
||||||
self.url, server_id, attachment_id)
|
self.url.format(server_id=server_id), attachment_id)
|
||||||
|
|
||||||
params = {
|
|
||||||
'tenant_id': self.tenant_id, 'server_id': server_id,
|
|
||||||
'attachment_id': attachment_id}
|
|
||||||
|
|
||||||
return self.request(
|
return self.request(
|
||||||
'DELETE', url, params=params,
|
'DELETE', url, requestslib_kwargs=requestslib_kwargs)
|
||||||
requestslib_kwargs=requestslib_kwargs)
|
|
||||||
|
|
||||||
def get_server_volume_attachments(self, server_id,
|
def get_server_volume_attachments(self, server_id,
|
||||||
requestslib_kwargs=None):
|
requestslib_kwargs=None):
|
||||||
"""
|
"""
|
||||||
GET
|
GET
|
||||||
v2/servers/{server_id}/os-volume_attachments/
|
servers/{server_id}/os-volume_attachments
|
||||||
"""
|
"""
|
||||||
|
|
||||||
url = '{0}/servers/{1}/os-volume_attachments'.format(
|
url = self.url.format(server_id=server_id)
|
||||||
self.url, server_id)
|
|
||||||
|
|
||||||
params = {'tenant_id': self.tenant_id, 'server_id': server_id}
|
|
||||||
|
|
||||||
return self.request(
|
return self.request(
|
||||||
'GET', url, params=params, requestslib_kwargs=requestslib_kwargs)
|
'GET', url, response_entity_type=VolumeAttachmentListResponse,
|
||||||
|
requestslib_kwargs=requestslib_kwargs)
|
||||||
|
|
||||||
def get_volume_attachment_details(self, attachment_id, server_id,
|
def get_volume_attachment_details(self, attachment_id, server_id,
|
||||||
requestslib_kwargs=None):
|
requestslib_kwargs=None):
|
||||||
url = '{0}/servers/{1}/os-volume_attachments/{2}'.format(
|
"""
|
||||||
self.url, server_id, attachment_id)
|
GET
|
||||||
|
servers/{server_id}/os-volume_attachments/{attachment_id}
|
||||||
|
"""
|
||||||
|
|
||||||
params = {'tenant_id': self.tenant_id,
|
url = "{0}/{1}".format(
|
||||||
'server_id': server_id,
|
self.url.format(server_id=server_id), attachment_id)
|
||||||
'attachment_id': attachment_id}
|
|
||||||
|
|
||||||
return self.request(
|
return self.request(
|
||||||
'GET', url, params=params, requestslib_kwargs=requestslib_kwargs)
|
'GET', url, response_entity_type=VolumeAttachmentResponse,
|
||||||
|
requestslib_kwargs=requestslib_kwargs)
|
||||||
37
cloudcafe/compute/volume_attachments_api/config.py
Normal file
37
cloudcafe/compute/volume_attachments_api/config.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
"""
|
||||||
|
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 cloudcafe.common.models.configuration import ConfigSectionInterface
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeAttachmentsAPIConfig(ConfigSectionInterface):
|
||||||
|
|
||||||
|
SECTION_NAME = 'volume_attachments'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def attachment_propagation_timeout(self):
|
||||||
|
"""
|
||||||
|
Seconds it should take for a new volume attachment instance to
|
||||||
|
propagate.
|
||||||
|
"""
|
||||||
|
return self.get("attachment_propagation_timeout", 60)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def api_poll_rate(self):
|
||||||
|
"""
|
||||||
|
Seconds to wait between polling the os-volume_attachments API in loops.
|
||||||
|
"""
|
||||||
|
return self.get("api_poll_rate", 5)
|
||||||
@@ -15,6 +15,7 @@ limitations under the License.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
from cafe.engine.models.base import AutoMarshallingModel
|
from cafe.engine.models.base import AutoMarshallingModel
|
||||||
|
|
||||||
@@ -23,18 +24,17 @@ class VolumeAttachmentRequest(AutoMarshallingModel):
|
|||||||
|
|
||||||
def __init__(self, volume_id=None, device=None):
|
def __init__(self, volume_id=None, device=None):
|
||||||
super(VolumeAttachmentRequest, self).__init__()
|
super(VolumeAttachmentRequest, self).__init__()
|
||||||
self.id = None
|
|
||||||
self.server_id = None
|
|
||||||
self.volume_id = volume_id
|
self.volume_id = volume_id
|
||||||
self.device = device
|
self.device = device
|
||||||
|
|
||||||
def _obj_to_json(self):
|
def _obj_to_json(self):
|
||||||
return self._obj_to_json_ele()
|
data = {"volumeAttachment": self._obj_to_dict()}
|
||||||
|
return json.dumps(data)
|
||||||
|
|
||||||
def _obj_to_json_ele(self):
|
def _obj_to_xml(self):
|
||||||
sub_body = {"volumeId": self.volume_id}
|
element = ElementTree.Element('volumeAttachment')
|
||||||
sub_body["device"] = self.device
|
element = self._set_xml_etree_element(element, self._obj_to_dict())
|
||||||
sub_body = self._remove_empty_values(sub_body)
|
return ElementTree.tostring(element)
|
||||||
body = {"volumeAttachment": sub_body}
|
|
||||||
body = self._remove_empty_values(body)
|
def _obj_to_dict(self):
|
||||||
return json.dumps(body)
|
return {"volumeId": self.volume_id, "device": self.device}
|
||||||
@@ -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.
|
|
||||||
"""
|
|
||||||
68
cloudcafe/compute/volume_attachments_api/models/responses.py
Normal file
68
cloudcafe/compute/volume_attachments_api/models/responses.py
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
"""
|
||||||
|
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, AutoMarshallingListModel
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeAttachmentResponse(AutoMarshallingModel):
|
||||||
|
|
||||||
|
def __init__(self, id_=None, volume_id=None, server_id=None, device=None):
|
||||||
|
super(VolumeAttachmentResponse, self).__init__()
|
||||||
|
self.id_ = id_
|
||||||
|
self.volume_id = volume_id
|
||||||
|
self.server_id = server_id
|
||||||
|
self.device = device
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _json_to_obj(cls, serialized_str):
|
||||||
|
data = json.loads(serialized_str)
|
||||||
|
data = data.get("volumeAttachment", dict())
|
||||||
|
return cls._dict_to_obj(data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _xml_to_obj(cls, serialized_str):
|
||||||
|
return cls._dict_to_obj(ElementTree.fromstring(serialized_str))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _dict_to_obj(cls, obj_dict):
|
||||||
|
return VolumeAttachmentResponse(
|
||||||
|
id_=obj_dict.get('id', None),
|
||||||
|
volume_id=obj_dict.get('volumeId', None),
|
||||||
|
server_id=obj_dict.get('serverId', None),
|
||||||
|
device=obj_dict.get('device', None))
|
||||||
|
|
||||||
|
|
||||||
|
class VolumeAttachmentListResponse(AutoMarshallingListModel):
|
||||||
|
"""Represents a list of VolumeAttachmentResponse objects"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _xml_to_obj(cls, serialized_str):
|
||||||
|
return cls._xml_ele_to_obj(ElementTree.fromstring(serialized_str))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _json_to_obj(cls, serialized_str):
|
||||||
|
data = json.loads(serialized_str)
|
||||||
|
data = data.get("volumeAttachments")
|
||||||
|
return cls._list_to_obj(data)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _list_to_obj(cls, obj_list):
|
||||||
|
return VolumeAttachmentListResponse(
|
||||||
|
[VolumeAttachmentResponse._dict_to_obj(obj) for obj in obj_list])
|
||||||
@@ -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,76 +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 json
|
|
||||||
|
|
||||||
from cafe.engine.models.base import \
|
|
||||||
AutoMarshallingModel, AutoMarshallingListModel
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeAttachment(AutoMarshallingModel):
|
|
||||||
|
|
||||||
def __init__(self, id_=None, volume_id=None, server_id=None, device=None):
|
|
||||||
super(VolumeAttachment, self).__init__()
|
|
||||||
self.id_ = None
|
|
||||||
self.server_id = None
|
|
||||||
self.volume_id = volume_id
|
|
||||||
self.device = device
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _json_to_obj(cls, serialized_str):
|
|
||||||
json_dict = json.loads(serialized_str)
|
|
||||||
return VolumeAttachment(
|
|
||||||
id_=json_dict.get('id'),
|
|
||||||
volume_id=json_dict.get('volumeId'),
|
|
||||||
server_id=json_dict.get('serverId'),
|
|
||||||
device=json_dict.get('device'))
|
|
||||||
|
|
||||||
|
|
||||||
class VolumeAttachmentListResponse(AutoMarshallingListModel):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _json_to_obj(cls, serialized_str):
|
|
||||||
'''
|
|
||||||
Handles both the single and list version of the Volume
|
|
||||||
call, obviating the need for separate domain objects for "Volumes"
|
|
||||||
and "Lists of Volumes" responses.
|
|
||||||
Returns a list-like VolumeAttachmentListResponse
|
|
||||||
of VolumeAttachment objects, even if there is only one volume
|
|
||||||
attachment present.
|
|
||||||
'''
|
|
||||||
json_dict = json.loads(serialized_str)
|
|
||||||
|
|
||||||
is_list = True if json_dict.get('volumeAttachments', None) else False
|
|
||||||
|
|
||||||
va_list = VolumeAttachmentListResponse()
|
|
||||||
if is_list:
|
|
||||||
for volume_attachment in json_dict.get('volumeAttachments'):
|
|
||||||
va = VolumeAttachment(
|
|
||||||
id_=volume_attachment.get('id'),
|
|
||||||
volume_id=volume_attachment.get('volumeId'),
|
|
||||||
server_id=volume_attachment.get('serverId'),
|
|
||||||
device=volume_attachment.get('device'))
|
|
||||||
va_list.append(va)
|
|
||||||
else:
|
|
||||||
volume_attachment = json_dict.get('volumeAttachment')
|
|
||||||
va_list.append(
|
|
||||||
VolumeAttachment(
|
|
||||||
id_=volume_attachment.get('id'),
|
|
||||||
volume_id=volume_attachment.get('volumeId'),
|
|
||||||
server_id=volume_attachment.get('serverId'),
|
|
||||||
device=volume_attachment.get('device')))
|
|
||||||
va_list.append(va)
|
|
||||||
return va_list
|
|
||||||
Reference in New Issue
Block a user