Refactor driver BDM attach() to cover all uses

Add separate flags to allow the execution to skip the
volume_api.check_attach step, and to be able to call the
driver.attach_volume if needed. This will make the method modular enough
to be used in all cases where we actually "attach" volumes to instances.

Check attach should always be done in the API, followed immediately by a
reservation, to minimize the chance of a race. However currently we
do the check_attach step on the compute side during boot, so it needs to
stay. Other operations like attach_volume will have it reserved causing
this check to fail on the compute side, so we lay down the groundwork to
be able to skip it when calling attach().

Likewise, when booting, we don't need to call the virt driver
attach_volume method, as all the devices will be taken care of by the
boot code path.

Part of blueprint: use-new-bdm-format-in-attach

Change-Id: Iff3890e39a8c75b1693df0918084ecf4492c4e91
This commit is contained in:
Nikola Dipanov
2013-12-23 21:17:13 +01:00
parent 3e2f304c85
commit 65ee22f7e7
2 changed files with 106 additions and 12 deletions

View File

@@ -17,9 +17,11 @@ import operator
from nova import block_device
from nova.objects import block_device as block_device_obj
from nova.openstack.common import excutils
from nova.openstack.common.gettextutils import _
from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova.volume import encryptors
LOG = logging.getLogger(__name__)
@@ -209,14 +211,12 @@ class DriverVolumeBlockDevice(DriverBlockDevice):
self['connection_info'] = None
@update_db
def attach(self, context, instance, volume_api, virt_driver):
def attach(self, context, instance, volume_api, virt_driver,
do_check_attach=True, do_driver_attach=False):
volume = volume_api.get(context, self.volume_id)
volume_api.check_attach(context, volume, instance=instance)
if do_check_attach:
volume_api.check_attach(context, volume, instance=instance)
# Attach a volume to an instance at boot time. So actual attach
# is done by instance creation.
instance_id = instance['id']
instance_uuid = instance['uuid']
volume_id = volume['id']
context = context.elevated()
@@ -224,12 +224,31 @@ class DriverVolumeBlockDevice(DriverBlockDevice):
connection_info = volume_api.initialize_connection(context,
volume_id,
connector)
volume_api.attach(context, volume_id,
instance_uuid, self['mount_device'])
if 'serial' not in connection_info:
connection_info['serial'] = self.volume_id
# If do_driver_attach is False, we will attach a volume to an instance
# at boot time. So actual attach is done by instance creation code.
if do_driver_attach:
encryption = encryptors.get_encryption_metadata(
context, volume_api, volume_id, connection_info)
try:
virt_driver.attach_volume(
context, connection_info, instance,
self['mount_device'], encryption=encryption)
except Exception: # pylint: disable=W0702
with excutils.save_and_reraise_exception():
LOG.exception(_("Driver failed to attach volume "
"%(volume_id)s at %(mountpoint)s"),
{'volume_id': volume_id,
'mountpoint': self['mount_device']},
context=context, instance=instance)
volume_api.terminate_connection(context, volume_id,
connector)
self['connection_info'] = connection_info
volume_api.attach(context, volume_id,
instance['uuid'], self['mount_device'])
@update_db
def refresh_connection_info(self, context, instance,