Initial Commit
This commit is contained in:
16
cloudcafe/blockstorage/__init__.py
Normal file
16
cloudcafe/blockstorage/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
30
cloudcafe/blockstorage/config.py
Normal file
30
cloudcafe/blockstorage/config.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
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 BlockStorageConfig(ConfigSectionInterface):
|
||||
|
||||
SECTION_NAME = 'blockstorage'
|
||||
|
||||
@property
|
||||
def identity_service_name(self):
|
||||
return self.get('identity_service_name')
|
||||
|
||||
@property
|
||||
def region(self):
|
||||
return self.get('region')
|
||||
26
cloudcafe/blockstorage/integration/compute_behaviors.py
Normal file
26
cloudcafe/blockstorage/integration/compute_behaviors.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
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
|
||||
37
cloudcafe/blockstorage/provider.py
Normal file
37
cloudcafe/blockstorage/provider.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 cafe.engine.provider import BaseProvider
|
||||
|
||||
from cloudcafe.blockstorage.config import BlockStorageConfig
|
||||
from cloudcafe.blockstorage.volumes_api.provider import VolumesProvider
|
||||
|
||||
|
||||
class BlockStorageProvider(BaseProvider):
|
||||
|
||||
def get_volumes_provider(self):
|
||||
|
||||
# NEED TO IMPORT IDENTITY AND GET THESE THINGS
|
||||
blockstorage_config = BlockStorageConfig()
|
||||
blockstorage_service_name = blockstorage_config.identity_service_name
|
||||
blockstorage_region = blockstorage_config.region
|
||||
auth_token = '924ur802ur08j2f0984'
|
||||
volumes_url = 'http://volumes_url'
|
||||
tenant_id = '234234'
|
||||
|
||||
return VolumesProvider(volumes_url, auth_token, tenant_id)
|
||||
|
||||
|
||||
16
cloudcafe/blockstorage/volumes_api/__init__.py
Normal file
16
cloudcafe/blockstorage/volumes_api/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
374
cloudcafe/blockstorage/volumes_api/behaviors.py
Normal file
374
cloudcafe/blockstorage/volumes_api/behaviors.py
Normal file
@@ -0,0 +1,374 @@
|
||||
"""
|
||||
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 time import time
|
||||
|
||||
from cafe.engine.behaviors import BaseBehavior, behavior
|
||||
from cloudcafe.blockstorage.volumes_api.volumes_client import VolumesClient
|
||||
from cloudcafe.blockstorage.volumes_api.config import VolumesAPIConfig
|
||||
|
||||
|
||||
class BehaviorResponse(object):
|
||||
'''An object to represent the result of behavior.
|
||||
@ivar response: Last response returned from last client call
|
||||
@ivar ok: Represents the success state of the behavior call
|
||||
@type ok:C{bool}
|
||||
@ivar entity: Data model created via behavior calls, if applicable
|
||||
@TODO: This should probably be moved to the base behavior module,
|
||||
or even into the engine's models
|
||||
'''
|
||||
def __init__(self):
|
||||
self.response = None
|
||||
self.ok = False
|
||||
self.entity = None
|
||||
|
||||
|
||||
class VolumesAPI_Behaviors(BaseBehavior):
|
||||
|
||||
def __init__(self, volumes_client=None):
|
||||
self._client = volumes_client
|
||||
self.config = VolumesAPIConfig()
|
||||
|
||||
@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'.
|
||||
'''
|
||||
wait_period = wait_period or self.config.volume_status_poll_frequency
|
||||
behavior_response = BehaviorResponse()
|
||||
end_time = time() + timeout
|
||||
|
||||
while time() < end_time:
|
||||
resp = self._client.get_volume_info(volume_id=volume_id)
|
||||
behavior_response.response = resp
|
||||
behavior_response.entity = resp.entity
|
||||
|
||||
if not resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"get_volume_info() call failed with status_code {0} while "
|
||||
"waiting for volume status".format(resp.status_code))
|
||||
break
|
||||
|
||||
if resp.entity is None:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"get_volume_info() response body did not deserialize as "
|
||||
"expected")
|
||||
break
|
||||
|
||||
if resp.entity.status == expected_status:
|
||||
behavior_response.ok = True
|
||||
self._log.info('Volume status "{0}" observed'.format(
|
||||
expected_status))
|
||||
break
|
||||
else:
|
||||
behavior_response.ok = False
|
||||
self._log.info(
|
||||
"wait_for_volume_status() ran for {0} seconds and did not "
|
||||
"observe the volume achieving the {1} status.".format(
|
||||
timeout, expected_status))
|
||||
|
||||
return behavior_response
|
||||
|
||||
@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'.
|
||||
'''
|
||||
wait_period = wait_period or self.config.snapshot_status_poll_frequency
|
||||
behavior_response = BehaviorResponse()
|
||||
end_time = time() + timeout
|
||||
|
||||
while time() < end_time:
|
||||
resp = self._client.get_snapshot_info(snapshot_id=snapshot_id)
|
||||
behavior_response.response = resp
|
||||
behavior_response.entity = resp.entity
|
||||
|
||||
if not resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"get_snapshot_info() call failed with status_code {0} "
|
||||
"while waiting for snapshot status".format(
|
||||
resp.status_code))
|
||||
break
|
||||
|
||||
if resp.entity is None:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"get_snapshot_info() response body did not deserialize as "
|
||||
"expected")
|
||||
break
|
||||
|
||||
if resp.entity.status == expected_status:
|
||||
behavior_response.ok = True
|
||||
self._log.info('Snapshot status "{0}" observed'.format(
|
||||
expected_status))
|
||||
break
|
||||
else:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"wait_for_snapshot_status() ran for {0} seconds and did not "
|
||||
"observe the snapshot achieving the '{1}' status.".format(
|
||||
timeout, expected_status))
|
||||
|
||||
return behavior_response
|
||||
|
||||
@behavior(VolumesClient)
|
||||
def create_available_volume(
|
||||
self, display_name, size, volume_type, display_description=None,
|
||||
metadata=None, availability_zone=None, timeout=None,
|
||||
wait_period=None):
|
||||
|
||||
expected_status = 'available'
|
||||
metadata = metadata or {}
|
||||
timeout = timeout or self.config.volume_create_timeout
|
||||
behavior_response = BehaviorResponse()
|
||||
|
||||
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,
|
||||
display_description=display_description, metadata=metadata,
|
||||
availability_zone=availability_zone)
|
||||
|
||||
behavior_response.response = resp
|
||||
behavior_response.entity = resp.entity
|
||||
|
||||
if not resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"create_available_volume() call failed with status_code {0} "
|
||||
"while attempting to create a volume".format(resp.status_code))
|
||||
|
||||
if resp.entity is None:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"create_available_volume() response body did not deserialize "
|
||||
"as expected")
|
||||
|
||||
#Bail on fail
|
||||
if not behavior_response.ok:
|
||||
return behavior_response
|
||||
|
||||
# Wait for expected_status on success
|
||||
wait_resp = self.wait_for_volume_status(
|
||||
resp.entity.id_, expected_status, timeout, wait_period)
|
||||
|
||||
if not wait_resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"Something went wrong while create_available_volume() was "
|
||||
"waiting for the volume to reach the '{0}' status")
|
||||
else:
|
||||
behavior_response.ok = True
|
||||
|
||||
return behavior_response
|
||||
|
||||
@behavior(VolumesClient)
|
||||
def create_available_snapshot(
|
||||
self, volume_id, display_name=None, display_description=None,
|
||||
force_create='False', name=None, timeout=None, wait_period=None):
|
||||
|
||||
expected_status = 'available'
|
||||
timeout = timeout or self.config.snapshot_create_timeout
|
||||
behavior_response = BehaviorResponse()
|
||||
|
||||
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)
|
||||
|
||||
behavior_response.response = resp
|
||||
behavior_response.entity = resp.entity
|
||||
|
||||
if not resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"create_available_volume() call failed with status_code {0} "
|
||||
"while attempting to create a volume".format(resp.status_code))
|
||||
|
||||
if resp.entity is None:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"create_available_volume() response body did not deserialize "
|
||||
"as expected")
|
||||
|
||||
# Bail on fail
|
||||
if not behavior_response.ok:
|
||||
return behavior_response
|
||||
|
||||
# Wait for expected_status on success
|
||||
wait_resp = self.wait_for_volume_status(
|
||||
resp.entity.id_, expected_status, timeout, wait_period)
|
||||
|
||||
if not wait_resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"Something went wrong while create_available_volume() was "
|
||||
"waiting for the volume to reach the '{0}' status")
|
||||
else:
|
||||
behavior_response.ok = True
|
||||
|
||||
return behavior_response
|
||||
|
||||
@behavior(VolumesClient)
|
||||
def list_volume_snapshots(self, volume_id):
|
||||
behavior_response = BehaviorResponse()
|
||||
|
||||
# List all snapshots
|
||||
resp = self._client.list_all_snapshots_info()
|
||||
behavior_response.response = resp
|
||||
behavior_response.entity = resp.entity
|
||||
|
||||
if not resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"list_volume_snapshots() failed to get a list of all snapshots"
|
||||
"due to a '{0}' response from list_all_snapshots_info()"
|
||||
.format(resp.status_code))
|
||||
return behavior_response
|
||||
|
||||
if resp.entity is None:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"list_all_snapshots_info() response body did not deserialize "
|
||||
"as expected")
|
||||
return behavior_response
|
||||
|
||||
# Expects an entity of type VolumeSnapshotList
|
||||
volume_snapshots = [s for s in resp.entity if s.volume_id == volume_id]
|
||||
behavior_response.entity = volume_snapshots
|
||||
|
||||
return behavior_response
|
||||
|
||||
@behavior(VolumesClient)
|
||||
def delete_volume_confirmed(
|
||||
self, volume_id, size=None, timeout=None, wait_period=None):
|
||||
|
||||
if size is not None:
|
||||
if self.config.volume_delete_wait_per_gig is not None:
|
||||
wait_per_gig = self.config.volume_snapshot_delete_wait_per_gig
|
||||
timeout = timeout or size * wait_per_gig
|
||||
|
||||
if self.config.volume_delete_min_timeout is not None:
|
||||
min_timeout = self.config.volume_snapshot_delete_min_timeout
|
||||
timeout = timeout if timeout > min_timeout else min_timeout
|
||||
|
||||
if self.config.volume_delete_max_timeout is not None:
|
||||
max_timeout = self.config.volume_snapshot_delete_max_timeout
|
||||
timeout = timeout if timeout < max_timeout else max_timeout
|
||||
|
||||
end = time() + timeout
|
||||
while time() < end:
|
||||
#issue DELETE request on volume
|
||||
behavior_response = BehaviorResponse()
|
||||
resp = self._client.delete_volume(volume_id)
|
||||
behavior_response.response = resp
|
||||
behavior_response.entity = resp.entity
|
||||
|
||||
if not resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"delete_volume_confirmed() call to delete_volume() failed "
|
||||
"with a '{0}'".format(resp.status_code))
|
||||
return behavior_response
|
||||
|
||||
#Poll volume status to make sure it deleted properly
|
||||
status_resp = self._client.get_volume_info(volume_id)
|
||||
if status_resp.status_code == 404:
|
||||
behavior_response.ok = True
|
||||
self._log.info(
|
||||
"Status request on volume {0} returned 404, volume delete"
|
||||
"confirmed".format(volume_id))
|
||||
break
|
||||
|
||||
if (not status_resp.ok) and (status_resp.status_code != 404):
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"Status request on volume {0} failed with a {0}".format(
|
||||
volume_id))
|
||||
break
|
||||
else:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"delete_volume_confirmed() was unable to verify the volume"
|
||||
"delete withing the alloted {0} second timeout".format())
|
||||
|
||||
return behavior_response
|
||||
|
||||
@behavior(VolumesClient)
|
||||
def delete_snapshot_confirmed(
|
||||
self, snapshot_id, size=None, timeout=None, wait_period=None):
|
||||
|
||||
if size is not None:
|
||||
if self.config.volume_snapshot_delete_wait_per_gig is not None:
|
||||
wait_per_gig = self.config.volume_snapshot_delete_wait_per_gig
|
||||
timeout = timeout or size * wait_per_gig
|
||||
|
||||
if self.config.volume_snapshot_delete_min_timeout is not None:
|
||||
min_timeout = self.config.volume_snapshot_delete_min_timeout
|
||||
timeout = timeout if timeout > min_timeout else min_timeout
|
||||
|
||||
if self.config.volume_snapshot_delete_max_timeout is not None:
|
||||
max_timeout = self.config.volume_snapshot_delete_max_timeout
|
||||
timeout = timeout if timeout < max_timeout else max_timeout
|
||||
|
||||
end = time() + timeout
|
||||
while time() < end:
|
||||
# issue DELETE request on volume snapshot
|
||||
behavior_response = BehaviorResponse()
|
||||
resp = self._client.delete_snapshot(snapshot_id)
|
||||
behavior_response.response = resp
|
||||
behavior_response.entity = resp.entity
|
||||
|
||||
if not resp.ok:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"delete_snapshot_confirmed() call to delete_snapshot()"
|
||||
"failed with a '{0}'".format(resp.status_code))
|
||||
return behavior_response
|
||||
|
||||
# Poll snapshot status to make sure it deleted properly
|
||||
status_resp = self._client.get_snapshot_info(snapshot_id)
|
||||
if status_resp.status_code == 404:
|
||||
behavior_response.ok = True
|
||||
self._log.info(
|
||||
"Status request on snapshot {0} returned 404, snapshot"
|
||||
"delete confirmed".format(snapshot_id))
|
||||
break
|
||||
|
||||
if (not status_resp.ok) and (status_resp.status_code != 404):
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"Status request on snapshot {0} failed with a {0}".format(
|
||||
snapshot_id))
|
||||
break
|
||||
else:
|
||||
behavior_response.ok = False
|
||||
self._log.error(
|
||||
"delete_snapshot_confirmed() was unable to verify the snapshot"
|
||||
"delete withing the alloted {0} second timeout".format())
|
||||
|
||||
return behavior_response
|
||||
|
||||
@behavior(VolumesClient)
|
||||
def delete_volume_with_snapshots_confirmed(self):
|
||||
pass
|
||||
201
cloudcafe/blockstorage/volumes_api/client.py
Normal file
201
cloudcafe/blockstorage/volumes_api/client.py
Normal file
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
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.clients.rest import AutoMarshallingRestClient
|
||||
from cloudcafe.blockstorage.volumes_api.models.requests.volumes_api import \
|
||||
Volume as VolumeRequest, VolumeSnapshot as VolumeSnapshotRequest\
|
||||
|
||||
from cloudcafe.blockstorage.volumes_api.models.responses.volumes_api import \
|
||||
Volume as VolumeResponse, VolumeSnapshot as VolumeSnapshotResponse,\
|
||||
VolumeType, VolumeList, VolumeTypeList, VolumeSnapshotList
|
||||
|
||||
|
||||
class VolumesClient(AutoMarshallingRestClient):
|
||||
def __init__(
|
||||
self, url, auth_token, tenant_id, serialize_format=None,
|
||||
deserialize_format=None):
|
||||
|
||||
super(VolumesClient, self).__init__(
|
||||
serialize_format, deserialize_format)
|
||||
|
||||
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
|
||||
|
||||
def create_volume(
|
||||
self, display_name, size, volume_type, availability_zone=None,
|
||||
metadata={}, display_description='', snapshot_id=None,
|
||||
requestslib_kwargs=None):
|
||||
|
||||
'''POST v1/{tenant_id}/volumes'''
|
||||
|
||||
url = '{0}/volumes'.format(self.url)
|
||||
|
||||
volume_request_entity = VolumeRequest(
|
||||
display_name=display_name,
|
||||
size=size,
|
||||
volume_type=volume_type,
|
||||
display_description=display_description,
|
||||
metadata=metadata,
|
||||
availability_zone=availability_zone,
|
||||
snapshot_id=snapshot_id)
|
||||
|
||||
return self.request(
|
||||
'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={},
|
||||
requestslib_kwargs=None):
|
||||
|
||||
'''POST v1/{tenant_id}/volumes'''
|
||||
|
||||
url = '{0}/volumes'.format(self.url)
|
||||
|
||||
volume_request_entity = VolumeRequest(
|
||||
display_name=display_name,
|
||||
size=size,
|
||||
volume_type=volume_type,
|
||||
display_description=display_description,
|
||||
metadata=metadata,
|
||||
availability_zone=availability_zone,
|
||||
snapshot_id=snapshot_id)
|
||||
|
||||
return self.request(
|
||||
'POST', url, response_entity_type=VolumeResponse,
|
||||
request_entity=volume_request_entity,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def list_all_volumes(self, requestslib_kwargs=None):
|
||||
|
||||
'''GET v1/{tenant_id}/volumes'''
|
||||
|
||||
url = '{0}/volumes'.format(self.url)
|
||||
return self.request(
|
||||
'GET', url, response_entity_type=VolumeList,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def list_all_volumes_info(self, requestslib_kwargs=None):
|
||||
|
||||
'''GET v1/{tenant_id}/volumes/detail'''
|
||||
|
||||
url = '{0}/volumes/detail'.format(self.url)
|
||||
return self.request(
|
||||
'GET', url, response_entity_type=VolumeList,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def get_volume_info(self, volume_id, requestslib_kwargs=None):
|
||||
|
||||
'''GET v1/{tenant_id}/volumes/{volume_id}'''
|
||||
|
||||
url = '{0}/volumes/{1}'.format(self.url, volume_id)
|
||||
return self.request(
|
||||
'GET', url, response_entity_type=VolumeResponse,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def delete_volume(self, volume_id, requestslib_kwargs=None):
|
||||
|
||||
'''DELETE v1/{tenant_id}/volumes/{volume_id}'''
|
||||
|
||||
url = '{0}/volumes/{1}'.format(self.url, volume_id)
|
||||
return self.request(
|
||||
'DELETE', url, response_entity_type=VolumeResponse,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
#Volume Types API
|
||||
def list_all_volume_types(self, requestslib_kwargs=None):
|
||||
|
||||
'''GET v1/{tenant_id}/types '''
|
||||
|
||||
url = '{0}/types'.format(self.url)
|
||||
return self.request(
|
||||
'GET', url, response_entity_type=VolumeTypeList,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def get_volume_type_info(self, volume_type_id, requestslib_kwargs=None):
|
||||
|
||||
'''GET v1/{tenant_id}/types/{volume_type_id}'''
|
||||
|
||||
url = '{0}/types/{1}'.format(self.url, volume_type_id)
|
||||
return self.request(
|
||||
'GET', url, response_entity_type=VolumeType,
|
||||
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):
|
||||
|
||||
'''POST v1/{tenant_id}/snapshots'''
|
||||
|
||||
url = '{0}/snapshots'.format(self.url)
|
||||
|
||||
volume_snapshot_request_entity = VolumeSnapshotRequest(
|
||||
volume_id,
|
||||
force=force_create,
|
||||
display_name=display_name,
|
||||
name=name,
|
||||
display_description=display_description)
|
||||
|
||||
return self.request(
|
||||
'POST', url, response_entity_type=VolumeSnapshotResponse,
|
||||
request_entity=volume_snapshot_request_entity,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def list_all_snapshots(self, requestslib_kwargs=None):
|
||||
|
||||
'''GET v1/{tenant_id}/snapshots'''
|
||||
|
||||
url = '{0}/snapshots'.format(self.url)
|
||||
|
||||
return self.request(
|
||||
'GET', url, response_entity_type=VolumeSnapshotList,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def list_all_snapshots_info(self, requestslib_kwargs=None):
|
||||
|
||||
'''GET v1/{tenant_id}/snapshots/detail'''
|
||||
|
||||
url = '{0}/snapshots/detail'.format(self.url)
|
||||
return self.request(
|
||||
'GET', url, response_entity_type=VolumeSnapshotList,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def get_snapshot_info(self, snapshot_id, requestslib_kwargs=None):
|
||||
|
||||
'''GET v1/{tenant_id}/snapshots/{snapshot_id}'''
|
||||
|
||||
url = '{0}/snapshots/{1}'.format(self.url, snapshot_id)
|
||||
|
||||
return self.request(
|
||||
'GET', url, response_entity_type=VolumeSnapshotResponse,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
def delete_snapshot(self, snapshot_id, requestslib_kwargs=None):
|
||||
|
||||
'''DELETE v1/{tenant_id}/snapshots/{snapshot_id}'''
|
||||
|
||||
url = '{0}/snapshots/{1}'.format(self.url, snapshot_id)
|
||||
return self.request(
|
||||
'DELETE', url, response_entity_type=VolumeSnapshotResponse,
|
||||
requestslib_kwargs=requestslib_kwargs)
|
||||
|
||||
75
cloudcafe/blockstorage/volumes_api/config.py
Normal file
75
cloudcafe/blockstorage/volumes_api/config.py
Normal file
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
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.models.configuration import BaseConfigSectionInterface
|
||||
|
||||
|
||||
class VolumesAPIConfig(BaseConfigSectionInterface):
|
||||
SECTION_NAME = 'volumes_api'
|
||||
|
||||
@property
|
||||
def serialize_format(self):
|
||||
return self.get("serialize_format")
|
||||
|
||||
@property
|
||||
def deserialize_format(self):
|
||||
return self.get("deserialize_format")
|
||||
|
||||
@property
|
||||
def max_volume_size(self):
|
||||
return self.get("max_volume_size", default='1024')
|
||||
|
||||
@property
|
||||
def min_volume_size(self):
|
||||
return self.get("min_volume_size", default='1')
|
||||
|
||||
@property
|
||||
def volume_create_timeout(self):
|
||||
return self.get("volume_create_timeout", default='10')
|
||||
|
||||
@property
|
||||
def volume_status_poll_frequency(self):
|
||||
return self.get("volume_status_poll_frequency", default='30')
|
||||
|
||||
@property
|
||||
def volume_delete_wait_per_gig(self):
|
||||
return self.get("volume_delete_wait_per_gig", default='30')
|
||||
|
||||
@property
|
||||
def snapshot_create_timeout(self):
|
||||
return self.get("snapshot_create_timeout", default='10')
|
||||
|
||||
@property
|
||||
def snapshot_status_poll_frequency(self):
|
||||
return self.get("snapshot_status_poll_frequency", default='30')
|
||||
|
||||
@property
|
||||
def volume_snapshot_delete_min_timeout(self):
|
||||
"""Absolute lower limit on calculated volume snapshot delete timeouts
|
||||
"""
|
||||
return self.get("volume_delete_wait_per_gig", default=None)
|
||||
|
||||
@property
|
||||
def volume_snapshot_delete_max_timeout(self):
|
||||
"""Absolute upper limit on calculated volume snapshot delete timeouts
|
||||
"""
|
||||
return self.get("volume_delete_wait_per_gig", default=None)
|
||||
|
||||
@property
|
||||
def volume_snapshot_delete_wait_per_gig(self):
|
||||
"""If set, volume snapshot delete behaviors can estimate the time
|
||||
it will take a particular volume to delete given it's size"""
|
||||
return self.get("volume_delete_wait_per_gig", default=None)
|
||||
16
cloudcafe/blockstorage/volumes_api/models/__init__.py
Normal file
16
cloudcafe/blockstorage/volumes_api/models/__init__.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
"""
|
||||
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 Volume(AutoMarshallingModel):
|
||||
|
||||
def __init__(
|
||||
self, display_name=None, size=None, volume_type=None,
|
||||
display_description=None, metadata=None, availability_zone=None,
|
||||
snapshot_id=None, attachments=None, xmlns=None):
|
||||
|
||||
self.display_name = display_name
|
||||
self.display_description = display_description
|
||||
self.size = size
|
||||
self.volume_type = volume_type
|
||||
self.metadata = metadata or {}
|
||||
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
|
||||
|
||||
root_dict = {}
|
||||
root_dict["volume"] = self._remove_empty_values(sub_dict)
|
||||
return root_dict
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
element = ElementTree.Element('volume')
|
||||
attrs = {}
|
||||
attrs["xmlns"] = self.xmlns
|
||||
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)
|
||||
|
||||
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 VolumeSnapshot(AutoMarshallingModel):
|
||||
|
||||
def __init__(
|
||||
self, volume_id, force=True, display_name=None, name=None,
|
||||
display_description=None):
|
||||
|
||||
self.force = force
|
||||
self.display_name = display_name
|
||||
self.volume_id = volume_id
|
||||
self.name = name
|
||||
self.display_description = display_description
|
||||
|
||||
def _obj_to_json(self):
|
||||
return json.dumps(self._obj_to_json_dict())
|
||||
|
||||
def _obj_to_json_dict(self):
|
||||
attrs = {}
|
||||
attrs["snapshot"] = {}
|
||||
|
||||
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["snapshot"] = self._remove_empty_values({}, sub_attrs)
|
||||
return self._remove_empty_values({}, attrs)
|
||||
|
||||
def _obj_to_xml(self):
|
||||
return ElementTree.tostring(self._obj_to_xml_ele())
|
||||
|
||||
def _obj_to_xml_ele(self):
|
||||
element = ElementTree.Element('snapshot')
|
||||
attrs = {}
|
||||
attrs["xmlns"] = self.xmlns
|
||||
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
|
||||
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
"""
|
||||
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 Volume(AutoMarshallingModel):
|
||||
TAG = 'volume'
|
||||
'''@TODO Make sub data model for attachments element'''
|
||||
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):
|
||||
|
||||
#Common attributes
|
||||
self.id_ = id_
|
||||
self.display_name = display_name
|
||||
self.display_description = display_description
|
||||
self.size = size
|
||||
self.volume_type = volume_type
|
||||
self.metadata = metadata or {}
|
||||
self.availability_zone = availability_zone
|
||||
self.snapshot_id = snapshot_id
|
||||
self.attachments = attachments
|
||||
self.created_at = created_at
|
||||
self.status = status
|
||||
self.xmlns = xmlns
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
volume_dict = json_dict.get(cls.TAG)
|
||||
return cls._json_dict_to_obj(volume_dict)
|
||||
|
||||
@classmethod
|
||||
def _json_dict_to_obj(cls, json_dict):
|
||||
return Volume(
|
||||
id_=json_dict.get('id'),
|
||||
display_name=json_dict.get('display_name'),
|
||||
size=json_dict.get('size'),
|
||||
volume_type=json_dict.get('volume_type'),
|
||||
display_description=json_dict.get('display_description'),
|
||||
metadata=json_dict.get('metadata'),
|
||||
availability_zone=json_dict.get('availability_zone'),
|
||||
snapshot_id=json_dict.get('snapshot_id'),
|
||||
attachments=json_dict.get('attachments'),
|
||||
created_at=json_dict.get('created_at'),
|
||||
status=json_dict.get('status'))
|
||||
|
||||
#Response Deserializers
|
||||
@classmethod
|
||||
def _xml_to_obj(cls, serialized_str):
|
||||
element = ElementTree.fromstring(serialized_str)
|
||||
return cls._xml_ele_to_obj(element)
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, element):
|
||||
return Volume(
|
||||
id_=element.get('id'),
|
||||
display_name=element.get('display_name'),
|
||||
size=element.get('size'),
|
||||
volume_type=element.get('volume_type'),
|
||||
display_description=element.get('display_description'),
|
||||
metadata=element.get('metadata'),
|
||||
availability_zone=element.get('availability_zone'),
|
||||
snapshot_id=element.get('snapshot_id'),
|
||||
attachments=element.get('attachments'),
|
||||
created_at=element.get('created_at'),
|
||||
status=element.get('status'))
|
||||
|
||||
|
||||
class VolumeList(AutoMarshallingListModel):
|
||||
TAG = 'volumes'
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
volume_dict_list = json_dict.get(cls.TAG)
|
||||
return cls._json_dict_to_obj(volume_dict_list)
|
||||
|
||||
@classmethod
|
||||
def _json_dict_to_obj(cls, json_dict):
|
||||
volume_list = VolumeList()
|
||||
for volume_dict in json_dict:
|
||||
volume_list.append(Volume._json_dict_to_obj(volume_dict))
|
||||
return volume_list
|
||||
|
||||
@classmethod
|
||||
def _xml_to_obj(cls, serialized_str):
|
||||
volume_list_element = ElementTree.fromstring(serialized_str)
|
||||
return cls._xml_ele_to_obj(volume_list_element)
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, xml_etree_element):
|
||||
volume_list = VolumeList()
|
||||
for volume_element in xml_etree_element:
|
||||
volume_list.append(Volume._xml_ele_to_obj(volume_element))
|
||||
return volume_list
|
||||
|
||||
|
||||
class VolumeSnapshot(AutoMarshallingModel):
|
||||
TAG = 'snapshot'
|
||||
|
||||
def __init__(self, id_=None, volume_id=None, display_name=None,
|
||||
display_description=None, status=None,
|
||||
size=None, created_at=None, name=None):
|
||||
|
||||
self.id_ = id_
|
||||
self.volume_id = volume_id
|
||||
self.display_name = display_name
|
||||
self.display_description = display_description
|
||||
self.status = status
|
||||
self.size = size
|
||||
self.created_at = created_at
|
||||
self.name = name
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
volume_snap_dict = json_dict.get(cls.TAG)
|
||||
return cls._json_dict_to_obj(volume_snap_dict)
|
||||
|
||||
@classmethod
|
||||
def _json_dict_to_obj(cls, json_dict):
|
||||
return VolumeSnapshot(
|
||||
id_=json_dict.get('id'),
|
||||
volume_id=json_dict.get('volume_id'),
|
||||
display_name=json_dict.get('display_name'),
|
||||
display_description=json_dict.get('display_description'),
|
||||
status=json_dict.get('status'),
|
||||
size=json_dict.get('size'),
|
||||
created_at=json_dict.get('created_at'),
|
||||
name=json_dict.get('name'))
|
||||
|
||||
@classmethod
|
||||
def _xml_to_obj(cls, serialized_str):
|
||||
volume_snap_element = ElementTree.fromstring(serialized_str)
|
||||
return cls._xml_ele_to_obj(volume_snap_element)
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, xml_etree_element):
|
||||
return VolumeSnapshot(
|
||||
id_=xml_etree_element.get('id'),
|
||||
volume_id=xml_etree_element.get('volume_id'),
|
||||
display_name=xml_etree_element.get('display_name'),
|
||||
display_description=xml_etree_element.get('display_description'),
|
||||
status=xml_etree_element.get('status'),
|
||||
size=xml_etree_element.get('size'),
|
||||
created_at=xml_etree_element.get('created_at'),
|
||||
name=xml_etree_element.get('name'))
|
||||
|
||||
|
||||
class VolumeSnapshotList(AutoMarshallingListModel):
|
||||
TAG = 'snapshots'
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
volume_snap_dict_list = json_dict.get(cls.TAG)
|
||||
return cls._json_dict_to_obj(volume_snap_dict_list)
|
||||
|
||||
@classmethod
|
||||
def _json_dict_to_obj(cls, json_dict):
|
||||
volume_snap_list = VolumeSnapshotList()
|
||||
for volume_snap_dict in json_dict:
|
||||
volume_snap_list.append(VolumeSnapshot._json_dict_to_obj(
|
||||
volume_snap_dict))
|
||||
return volume_snap_list
|
||||
|
||||
@classmethod
|
||||
def _xml_to_obj(cls, serialized_str):
|
||||
volume_snap_list_element = ElementTree.fromstring(serialized_str)
|
||||
return cls._xml_ele_to_obj(volume_snap_list_element)
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, xml_etree_element):
|
||||
volume_snap_list = VolumeSnapshotList()
|
||||
for volume_snap_element in xml_etree_element:
|
||||
volume_snap_list.append(VolumeSnapshot._xml_ele_to_obj(
|
||||
volume_snap_element))
|
||||
return volume_snap_list
|
||||
|
||||
|
||||
class VolumeType(AutoMarshallingModel):
|
||||
TAG = 'volume_type'
|
||||
|
||||
def __init__(self, id_=None, name=None, extra_specs=None):
|
||||
|
||||
self.id_ = id_
|
||||
self.name = name
|
||||
self.extra_specs = extra_specs
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
volume_type_dict = json_dict.get(cls.TAG)
|
||||
return cls._json_dict_to_obj(volume_type_dict)
|
||||
|
||||
@classmethod
|
||||
def _json_dict_to_obj(cls, json_dict):
|
||||
return VolumeType(
|
||||
id_=json_dict.get('id_'),
|
||||
name=json_dict.get('name'),
|
||||
extra_specs=json_dict.get('extra_specs'))
|
||||
|
||||
@classmethod
|
||||
def _xml_to_obj(cls, serialized_str):
|
||||
volume_type_element = ElementTree.fromstring(serialized_str)
|
||||
return cls._xml_ele_to_obj(volume_type_element)
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, xml_etree_element):
|
||||
return VolumeType(
|
||||
id_=xml_etree_element.get('id_'),
|
||||
name=xml_etree_element.get('name'),
|
||||
extra_specs=xml_etree_element.get('extra_specs'))
|
||||
|
||||
|
||||
class VolumeTypeList(AutoMarshallingListModel):
|
||||
TAG = 'volume_types'
|
||||
|
||||
@classmethod
|
||||
def _json_to_obj(cls, serialized_str):
|
||||
json_dict = json.loads(serialized_str)
|
||||
volume_type_dict_list = json_dict.get(cls.TAG)
|
||||
return cls._json_dict_to_obj(volume_type_dict_list)
|
||||
|
||||
@classmethod
|
||||
def _json_dict_to_obj(cls, json_dict):
|
||||
volume_type_list = VolumeTypeList()
|
||||
for volume_type_dict in json_dict:
|
||||
volume_type_list.append(VolumeType._json_dict_to_obj(
|
||||
volume_type_dict))
|
||||
return volume_type_list
|
||||
|
||||
@classmethod
|
||||
def _xml_to_obj(cls, serialized_str):
|
||||
volume_type_list_element = ElementTree.fromstring(serialized_str)
|
||||
return cls._xml_ele_to_obj(volume_type_list_element)
|
||||
|
||||
@classmethod
|
||||
def _xml_ele_to_obj(cls, xml_etree_element):
|
||||
volume_type_list = VolumeTypeList()
|
||||
for volume_type_element in xml_etree_element:
|
||||
volume_type_list.append(VolumeType._xml_ele_to_obj(
|
||||
volume_type_element))
|
||||
return volume_type_list
|
||||
35
cloudcafe/blockstorage/volumes_api/provider.py
Normal file
35
cloudcafe/blockstorage/volumes_api/provider.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""
|
||||
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.provider import BaseProvider
|
||||
|
||||
from cloudcafe.blockstorage.volumes_api.config import VolumesAPIConfig
|
||||
from cloudcafe.blockstorage.volumes_api.behaviors import VolumesBehaviors
|
||||
from cloudcafe.blockstorage.volumes_api.client import VolumesClient
|
||||
|
||||
|
||||
class VolumesProvider(BaseProvider):
|
||||
|
||||
def __init__(self, url, auth_token, tenant_id):
|
||||
|
||||
volumes_config = VolumesAPIConfig()
|
||||
serialize_format = volumes_config.serialize_format
|
||||
deserialize_format = volumes_config.deserialize_format
|
||||
|
||||
self.client = VolumesClient(
|
||||
url, auth_token, tenant_id, serialize_format=serialize_format,
|
||||
deserialize_format=deserialize_format)
|
||||
self.behaviors = VolumesBehaviors(self.client)
|
||||
Reference in New Issue
Block a user