From 70014ffe26a75a6ad46c5a3147e47de3f95e4916 Mon Sep 17 00:00:00 2001 From: huangtianhua Date: Wed, 6 May 2015 11:03:23 +0800 Subject: [PATCH] Decouple volumes of aws and os Add 'volume_base' module which include base volume manager and base volume attachment manager. Change-Id: I5c154f6f9fa78c15b39e5327b7e419db4f3d9da4 --- heat/engine/resources/aws/ec2/volume.py | 147 +-------------- .../resources/openstack/cinder/volume.py | 8 +- heat/engine/resources/volume_base.py | 168 ++++++++++++++++++ 3 files changed, 174 insertions(+), 149 deletions(-) create mode 100644 heat/engine/resources/volume_base.py diff --git a/heat/engine/resources/aws/ec2/volume.py b/heat/engine/resources/aws/ec2/volume.py index 0308dca46..c9c82dcd0 100644 --- a/heat/engine/resources/aws/ec2/volume.py +++ b/heat/engine/resources/aws/ec2/volume.py @@ -11,16 +11,13 @@ # License for the specific language governing permissions and limitations # under the License. -from heat.common import exception from heat.common.i18n import _ from heat.engine import constraints from heat.engine import properties -from heat.engine import resource -from heat.engine import scheduler -from heat.engine import volume_tasks as vol_task +from heat.engine.resources import volume_base as vb -class Volume(resource.Resource): +class Volume(vb.BaseVolume): PROPERTIES = ( AVAILABILITY_ZONE, SIZE, BACKUP_ID, TAGS, @@ -77,14 +74,6 @@ class Volume(resource.Resource): _volume_creating_status = ['creating', 'restoring-backup'] - default_client_name = 'cinder' - - def _name(self): - return self.physical_resource_name() - - def _description(self): - return self.physical_resource_name() - def _create_arguments(self): if self.properties[self.TAGS]: tags = dict((tm[self.TAG_KEY], tm[self.TAG_VALUE]) @@ -99,109 +88,8 @@ class Volume(resource.Resource): 'metadata': tags } - def _fetch_name_and_description(self, api_version, name=None, - description=None): - if api_version == 1: - return {'display_name': name or self._name(), - 'display_description': description or self._description()} - else: - return {'name': name or self._name(), - 'description': description or self._description()} - def handle_create(self): - backup_id = self.properties.get(self.BACKUP_ID) - cinder = self.client() - if backup_id is not None: - vol_id = cinder.restores.restore(backup_id).volume_id - - vol = cinder.volumes.get(vol_id) - kwargs = self._fetch_name_and_description( - cinder.volume_api_version) - cinder.volumes.update(vol_id, **kwargs) - else: - kwargs = self._create_arguments() - kwargs.update(self._fetch_name_and_description( - cinder.volume_api_version)) - vol = cinder.volumes.create(**kwargs) - self.resource_id_set(vol.id) - - return vol.id - - def check_create_complete(self, vol_id): - vol = self.client().volumes.get(vol_id) - - if vol.status == 'available': - return True - if vol.status in self._volume_creating_status: - return False - if vol.status == 'error': - raise resource.ResourceInError( - resource_status=vol.status) - else: - raise resource.ResourceUnknownStatus( - resource_status=vol.status, - result=_('Volume create failed')) - - def handle_check(self): - vol = self.client().volumes.get(self.resource_id) - statuses = ['available', 'in-use'] - checks = [ - {'attr': 'status', 'expected': statuses, 'current': vol.status}, - ] - self._verify_check_conditions(checks) - - def _backup(self): - cinder = self.client() - backup = cinder.backups.create(self.resource_id) - while backup.status == 'creating': - yield - backup = cinder.backups.get(backup.id) - if backup.status != 'available': - raise resource.ResourceUnknownStatus( - resource_status=backup.status, - result=_('Volume backup failed')) - - @scheduler.wrappertask - def _delete(self, backup=False): - if self.resource_id is not None: - cinder = self.client() - try: - vol = cinder.volumes.get(self.resource_id) - - if backup: - yield self._backup() - vol = cinder.volumes.get(self.resource_id) - - if vol.status == 'in-use': - raise exception.Error(_('Volume in use')) - # if the volume is already in deleting status, - # just wait for the deletion to complete - if vol.status != 'deleting': - cinder.volumes.delete(self.resource_id) - while True: - yield - vol = cinder.volumes.get(self.resource_id) - except Exception as ex: - self.client_plugin().ignore_not_found(ex) - - def handle_snapshot_delete(self, state): - backup = state not in ((self.CREATE, self.FAILED), - (self.UPDATE, self.FAILED)) - - delete_task = scheduler.TaskRunner(self._delete, backup=backup) - delete_task.start() - return delete_task - - def handle_delete(self): - delete_task = scheduler.TaskRunner(self._delete) - delete_task.start() - return delete_task - - def check_delete_complete(self, delete_task): - return delete_task.step() - - -class VolumeAttachment(resource.Resource): +class VolumeAttachment(vb.BaseVolumeAttachment): PROPERTIES = ( INSTANCE_ID, VOLUME_ID, DEVICE, ) = ( @@ -240,35 +128,6 @@ class VolumeAttachment(resource.Resource): ), } - def handle_create(self): - server_id = self.properties[self.INSTANCE_ID] - volume_id = self.properties[self.VOLUME_ID] - dev = self.properties[self.DEVICE] - - attach_task = vol_task.VolumeAttachTask( - self.stack, server_id, volume_id, dev) - attach_runner = scheduler.TaskRunner(attach_task) - - attach_runner.start() - - self.resource_id_set(attach_task.attachment_id) - - return attach_runner - - def check_create_complete(self, attach_runner): - return attach_runner.step() - - def handle_delete(self): - server_id = self.properties[self.INSTANCE_ID] - detach_task = vol_task.VolumeDetachTask( - self.stack, server_id, self.resource_id) - detach_runner = scheduler.TaskRunner(detach_task) - detach_runner.start() - return detach_runner - - def check_delete_complete(self, detach_runner): - return detach_runner.step() - def resource_mapping(): return { diff --git a/heat/engine/resources/openstack/cinder/volume.py b/heat/engine/resources/openstack/cinder/volume.py index 97b8a16b6..0a3a45fc0 100644 --- a/heat/engine/resources/openstack/cinder/volume.py +++ b/heat/engine/resources/openstack/cinder/volume.py @@ -21,7 +21,7 @@ from heat.common.i18n import _LI from heat.engine import attributes from heat.engine import constraints from heat.engine import properties -from heat.engine.resources.aws.ec2 import volume as aws_vol +from heat.engine.resources import volume_base as vb from heat.engine import scheduler from heat.engine import support from heat.engine import volume_tasks as vol_task @@ -29,7 +29,7 @@ from heat.engine import volume_tasks as vol_task LOG = logging.getLogger(__name__) -class CinderVolume(aws_vol.Volume): +class CinderVolume(vb.BaseVolume): PROPERTIES = ( AVAILABILITY_ZONE, SIZE, SNAPSHOT_ID, BACKUP_ID, NAME, @@ -180,8 +180,6 @@ class CinderVolume(aws_vol.Volume): _volume_creating_status = ['creating', 'restoring-backup', 'downloading'] - default_client_name = 'cinder' - def _name(self): name = self.properties[self.NAME] if name: @@ -361,7 +359,7 @@ class CinderVolume(aws_vol.Volume): return defn.freeze(properties=props) -class CinderVolumeAttachment(aws_vol.VolumeAttachment): +class CinderVolumeAttachment(vb.BaseVolumeAttachment): PROPERTIES = ( INSTANCE_ID, VOLUME_ID, DEVICE, diff --git a/heat/engine/resources/volume_base.py b/heat/engine/resources/volume_base.py new file mode 100644 index 000000000..ed4aeaa7e --- /dev/null +++ b/heat/engine/resources/volume_base.py @@ -0,0 +1,168 @@ +# +# 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 heat.common import exception +from heat.common.i18n import _ +from heat.engine import resource +from heat.engine import scheduler +from heat.engine import volume_tasks as vol_task + + +class BaseVolume(resource.Resource): + ''' + Base Volume Manager. + ''' + + default_client_name = 'cinder' + + def handle_create(self): + backup_id = self.properties.get(self.BACKUP_ID) + cinder = self.client() + if backup_id is not None: + vol_id = cinder.restores.restore(backup_id).volume_id + + vol = cinder.volumes.get(vol_id) + kwargs = self._fetch_name_and_description( + cinder.volume_api_version) + cinder.volumes.update(vol_id, **kwargs) + else: + kwargs = self._create_arguments() + kwargs.update(self._fetch_name_and_description( + cinder.volume_api_version)) + vol = cinder.volumes.create(**kwargs) + self.resource_id_set(vol.id) + + return vol.id + + def check_create_complete(self, vol_id): + vol = self.client().volumes.get(vol_id) + + if vol.status == 'available': + return True + if vol.status in self._volume_creating_status: + return False + if vol.status == 'error': + raise resource.ResourceInError( + resource_status=vol.status) + else: + raise resource.ResourceUnknownStatus( + resource_status=vol.status, + result=_('Volume create failed')) + + def _name(self): + return self.physical_resource_name() + + def _description(self): + return self.physical_resource_name() + + def _fetch_name_and_description(self, api_version, name=None, + description=None): + if api_version == 1: + return {'display_name': name or self._name(), + 'display_description': description or self._description()} + else: + return {'name': name or self._name(), + 'description': description or self._description()} + + def handle_check(self): + vol = self.client().volumes.get(self.resource_id) + statuses = ['available', 'in-use'] + checks = [ + {'attr': 'status', 'expected': statuses, 'current': vol.status}, + ] + self._verify_check_conditions(checks) + + def _backup(self): + cinder = self.client() + backup = cinder.backups.create(self.resource_id) + while backup.status == 'creating': + yield + backup = cinder.backups.get(backup.id) + if backup.status != 'available': + raise resource.ResourceUnknownStatus( + resource_status=backup.status, + result=_('Volume backup failed')) + + @scheduler.wrappertask + def _delete(self, backup=False): + if self.resource_id is not None: + cinder = self.client() + try: + vol = cinder.volumes.get(self.resource_id) + + if backup: + yield self._backup() + vol = cinder.volumes.get(self.resource_id) + + if vol.status == 'in-use': + raise exception.Error(_('Volume in use')) + # if the volume is already in deleting status, + # just wait for the deletion to complete + if vol.status != 'deleting': + cinder.volumes.delete(self.resource_id) + while True: + yield + vol = cinder.volumes.get(self.resource_id) + except Exception as ex: + self.client_plugin().ignore_not_found(ex) + + def handle_snapshot_delete(self, state): + backup = state not in ((self.CREATE, self.FAILED), + (self.UPDATE, self.FAILED)) + + delete_task = scheduler.TaskRunner(self._delete, backup=backup) + delete_task.start() + return delete_task + + def handle_delete(self): + delete_task = scheduler.TaskRunner(self._delete) + delete_task.start() + return delete_task + + def check_delete_complete(self, delete_task): + return delete_task.step() + + +class BaseVolumeAttachment(resource.Resource): + ''' + Base Volume Attachment Manager. + ''' + + def handle_create(self): + server_id = self.properties[self.INSTANCE_ID] + volume_id = self.properties[self.VOLUME_ID] + dev = self.properties[self.DEVICE] + + attach_task = vol_task.VolumeAttachTask( + self.stack, server_id, volume_id, dev) + attach_runner = scheduler.TaskRunner(attach_task) + + attach_runner.start() + + self.resource_id_set(attach_task.attachment_id) + + return attach_runner + + def check_create_complete(self, attach_runner): + return attach_runner.step() + + def handle_delete(self): + server_id = self.properties[self.INSTANCE_ID] + detach_task = vol_task.VolumeDetachTask( + self.stack, server_id, self.resource_id) + detach_runner = scheduler.TaskRunner(detach_task) + detach_runner.start() + return detach_runner + + def check_delete_complete(self, detach_runner): + return detach_runner.step()