def driver_detach(self, context, instance, volume_api, virt_driver): connection_info = self['connection_info'] mp = self['mount_device'] volume_id = self.volume_id LOG.info('Attempting to driver detach volume %(volume_id)s from ' 'mountpoint %(mp)s', {'volume_id': volume_id, 'mp': mp}, instance=instance) try: if not virt_driver.instance_exists(instance): LOG.warning('Detaching volume from unknown instance', instance=instance) encryption = encryptors.get_encryption_metadata(context, volume_api, volume_id, connection_info) virt_driver.detach_volume(connection_info, instance, mp, encryption=encryption) except exception.DiskNotFound as err: LOG.warning('Ignoring DiskNotFound exception while ' 'detaching volume %(volume_id)s from ' '%(mp)s : %(err)s', {'volume_id': volume_id, 'mp': mp, 'err': err}, instance=instance) except Exception: with excutils.save_and_reraise_exception(): LOG.exception('Failed to detach volume ' '%(volume_id)s from %(mp)s', {'volume_id': volume_id, 'mp': mp}, instance=instance) volume_api.roll_detaching(context, volume_id)
def driver_detach(self, context, instance, volume_api, virt_driver): connection_info = self['connection_info'] mp = self['mount_device'] volume_id = self.volume_id LOG.info('Attempting to driver detach volume %(volume_id)s from ' 'mountpoint %(mp)s', {'volume_id': volume_id, 'mp': mp}, instance=instance) try: if not virt_driver.instance_exists(instance): LOG.warning('Detaching volume from unknown instance', instance=instance) encryption = encryptors.get_encryption_metadata(context, volume_api, volume_id, connection_info) virt_driver.detach_volume(context, connection_info, instance, mp, encryption=encryption) except exception.DiskNotFound as err: LOG.warning('Ignoring DiskNotFound exception while ' 'detaching volume %(volume_id)s from ' '%(mp)s : %(err)s', {'volume_id': volume_id, 'mp': mp, 'err': err}, instance=instance) except exception.DeviceDetachFailed as err: with excutils.save_and_reraise_exception(): LOG.warning('Guest refused to detach volume %(vol)s', {'vol': volume_id}, instance=instance) volume_api.roll_detaching(context, volume_id) except Exception: with excutils.save_and_reraise_exception(): LOG.exception('Failed to detach volume ' '%(volume_id)s from %(mp)s', {'volume_id': volume_id, 'mp': mp}, instance=instance) volume_api.roll_detaching(context, volume_id)
def _volume_attach(self, context, volume, connector, instance, volume_api, virt_driver, attachment_id, do_driver_attach=False): # This is where we actually (finally) make a call down to the device # driver and actually create/establish the connection. We'll go from # here to block driver-->os-brick and back up. volume_id = volume['id'] if self.volume_size is None: self.volume_size = volume.get('size') vol_multiattach = volume.get('multiattach', False) virt_multiattach = virt_driver.capabilities.get( 'supports_multiattach', False) if vol_multiattach and not virt_multiattach: raise exception.MultiattachNotSupportedByVirtDriver( volume_id=volume_id) LOG.debug("Updating existing volume attachment record: %s", attachment_id, instance=instance) connection_info = volume_api.attachment_update( context, attachment_id, connector, self['mount_device'])['connection_info'] if 'serial' not in connection_info: connection_info['serial'] = self.volume_id self._preserve_multipath_id(connection_info) if vol_multiattach: # This will be used by the volume driver to determine the proper # disk configuration. # TODO(mriedem): Long-term we should stop stashing the multiattach # flag in the bdm.connection_info since that should be an untouched # set of values we can refresh from Cinder as needed. Putting the # multiattach flag on the bdm directly will require schema and # online data migrations, plus some refactoring to anything that # needs to get a block device disk config, like spawn/migrate/swap # and the LibvirtLiveMigrateBDMInfo would also need to store the # value. connection_info['multiattach'] = True 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'], disk_bus=self['disk_bus'], device_type=self['device_type'], encryption=encryption) except Exception: 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'] }, instance=instance) volume_api.attachment_delete(context, attachment_id) # NOTE(mriedem): save our current state so connection_info is in # the database before the volume status goes to 'in-use' because # after that we can detach and connection_info is required for # detach. # TODO(mriedem): Technically for the new flow, we shouldn't have to # rely on the BlockDeviceMapping.connection_info since it's stored # with the attachment in Cinder (see refresh_connection_info). # Therefore we should phase out code that relies on the # BDM.connection_info and get it from Cinder if it's needed. self['connection_info'] = connection_info self.save() try: # This marks the volume as "in-use". volume_api.attachment_complete(context, attachment_id) except Exception: with excutils.save_and_reraise_exception(): if do_driver_attach: # Disconnect the volume from the host. try: virt_driver.detach_volume(context, connection_info, instance, self['mount_device'], encryption=encryption) except Exception: LOG.warning( "Driver failed to detach volume " "%(volume_id)s at %(mount_point)s.", { 'volume_id': volume_id, 'mount_point': self['mount_device'] }, exc_info=True, instance=instance) # Delete the attachment to mark the volume as "available". volume_api.attachment_delete(context, self['attachment_id'])
def _legacy_volume_attach(self, context, volume, connector, instance, volume_api, virt_driver, do_driver_attach=False): volume_id = volume['id'] connection_info = volume_api.initialize_connection( context, volume_id, connector) if 'serial' not in connection_info: connection_info['serial'] = self.volume_id self._preserve_multipath_id(connection_info) # 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'], disk_bus=self['disk_bus'], device_type=self['device_type'], encryption=encryption) except Exception: 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'] }, instance=instance) volume_api.terminate_connection(context, volume_id, connector) self['connection_info'] = connection_info if self.volume_size is None: self.volume_size = volume.get('size') mode = 'rw' if 'data' in connection_info: mode = connection_info['data'].get('access_mode', 'rw') if volume['attach_status'] == "detached": # NOTE(mriedem): save our current state so connection_info is in # the database before the volume status goes to 'in-use' because # after that we can detach and connection_info is required for # detach. self.save() try: volume_api.attach(context, volume_id, instance.uuid, self['mount_device'], mode=mode) except Exception: with excutils.save_and_reraise_exception(): if do_driver_attach: try: virt_driver.detach_volume(context, connection_info, instance, self['mount_device'], encryption=encryption) except Exception: LOG.warning( "Driver failed to detach volume " "%(volume_id)s at %(mount_point)s.", { 'volume_id': volume_id, 'mount_point': self['mount_device'] }, exc_info=True, instance=instance) volume_api.terminate_connection(context, volume_id, connector) # Cinder-volume might have completed volume attach. So # we should detach the volume. If the attach did not # happen, the detach request will be ignored. volume_api.detach(context, volume_id)
def attach(self, context, instance, volume_api, virt_driver, do_driver_attach=False, **kwargs): volume = volume_api.get(context, self.volume_id) volume_api.check_availability_zone(context, volume, instance=instance) volume_id = volume['id'] context = context.elevated() connector = virt_driver.get_volume_connector(instance) connection_info = volume_api.initialize_connection(context, volume_id, connector) if 'serial' not in connection_info: connection_info['serial'] = self.volume_id self._preserve_multipath_id(connection_info) # 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'], disk_bus=self['disk_bus'], device_type=self['device_type'], encryption=encryption) except Exception: 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']}, instance=instance) volume_api.terminate_connection(context, volume_id, connector) self['connection_info'] = connection_info if self.volume_size is None: self.volume_size = volume.get('size') mode = 'rw' if 'data' in connection_info: mode = connection_info['data'].get('access_mode', 'rw') if volume['attach_status'] == "detached": # NOTE(mriedem): save our current state so connection_info is in # the database before the volume status goes to 'in-use' because # after that we can detach and connection_info is required for # detach. self.save() try: volume_api.attach(context, volume_id, instance.uuid, self['mount_device'], mode=mode) except Exception: with excutils.save_and_reraise_exception(): if do_driver_attach: try: virt_driver.detach_volume(connection_info, instance, self['mount_device'], encryption=encryption) except Exception: LOG.warning("Driver failed to detach volume " "%(volume_id)s at %(mount_point)s.", {'volume_id': volume_id, 'mount_point': self['mount_device']}, exc_info=True, instance=instance) volume_api.terminate_connection(context, volume_id, connector) # Cinder-volume might have completed volume attach. So # we should detach the volume. If the attach did not # happen, the detach request will be ignored. volume_api.detach(context, volume_id)
def _volume_attach(self, context, volume, connector, instance, volume_api, virt_driver, attachment_id, do_driver_attach=False): # This is where we actually (finally) make a call down to the device # driver and actually create/establish the connection. We'll go from # here to block driver-->os-brick and back up. volume_id = volume['id'] if self.volume_size is None: self.volume_size = volume.get('size') vol_multiattach = volume.get('multiattach', False) virt_multiattach = virt_driver.capabilities.get( 'supports_multiattach', False) if vol_multiattach and not virt_multiattach: raise exception.MultiattachNotSupportedByVirtDriver( volume_id=volume_id) LOG.debug("Updating existing volume attachment record: %s", attachment_id, instance=instance) connection_info = volume_api.attachment_update( context, attachment_id, connector, self['mount_device'])['connection_info'] if 'serial' not in connection_info: connection_info['serial'] = self.volume_id self._preserve_multipath_id(connection_info) if vol_multiattach: # This will be used by the volume driver to determine the proper # disk configuration. # TODO(mriedem): Long-term we should stop stashing the multiattach # flag in the bdm.connection_info since that should be an untouched # set of values we can refresh from Cinder as needed. Putting the # multiattach flag on the bdm directly will require schema and # online data migrations, plus some refactoring to anything that # needs to get a block device disk config, like spawn/migrate/swap # and the LibvirtLiveMigrateBDMInfo would also need to store the # value. connection_info['multiattach'] = True 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'], disk_bus=self['disk_bus'], device_type=self['device_type'], encryption=encryption) except Exception: 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']}, instance=instance) volume_api.attachment_delete(context, attachment_id) # NOTE(mriedem): save our current state so connection_info is in # the database before the volume status goes to 'in-use' because # after that we can detach and connection_info is required for # detach. # TODO(mriedem): Technically for the new flow, we shouldn't have to # rely on the BlockDeviceMapping.connection_info since it's stored # with the attachment in Cinder (see refresh_connection_info). # Therefore we should phase out code that relies on the # BDM.connection_info and get it from Cinder if it's needed. self['connection_info'] = connection_info self.save() try: # This marks the volume as "in-use". volume_api.attachment_complete(context, attachment_id) except Exception: with excutils.save_and_reraise_exception(): if do_driver_attach: # Disconnect the volume from the host. try: virt_driver.detach_volume(context, connection_info, instance, self['mount_device'], encryption=encryption) except Exception: LOG.warning("Driver failed to detach volume " "%(volume_id)s at %(mount_point)s.", {'volume_id': volume_id, 'mount_point': self['mount_device']}, exc_info=True, instance=instance) # Delete the attachment to mark the volume as "available". volume_api.attachment_delete(context, self['attachment_id'])
def _volume_attach(self, context, volume, connector, instance, volume_api, virt_driver, attachment_id, do_driver_attach=False): # This is where we actually (finally) make a call down to the device # driver and actually create/establish the connection. We'll go from # here to block driver-->os-brick and back up. volume_id = volume['id'] if self.volume_size is None: self.volume_size = volume.get('size') LOG.debug("Updating existing volume attachment record: %s", attachment_id, instance=instance) connection_info = volume_api.attachment_update( context, attachment_id, connector, self['mount_device'])['connection_info'] if 'serial' not in connection_info: connection_info['serial'] = self.volume_id self._preserve_multipath_id(connection_info) 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'], disk_bus=self['disk_bus'], device_type=self['device_type'], encryption=encryption) except Exception: 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'] }, instance=instance) volume_api.attachment_delete(context, attachment_id) # NOTE(mriedem): save our current state so connection_info is in # the database before the volume status goes to 'in-use' because # after that we can detach and connection_info is required for # detach. # TODO(mriedem): Technically for the new flow, we shouldn't have to # rely on the BlockDeviceMapping.connection_info since it's stored # with the attachment in Cinder (see refresh_connection_info). # Therefore we should phase out code that relies on the # BDM.connection_info and get it from Cinder if it's needed. self['connection_info'] = connection_info self.save() try: # This marks the volume as "in-use". volume_api.attachment_complete(context, attachment_id) except Exception: with excutils.save_and_reraise_exception(): if do_driver_attach: # Disconnect the volume from the host. try: virt_driver.detach_volume(connection_info, instance, self['mount_device'], encryption=encryption) except Exception: LOG.warning( "Driver failed to detach volume " "%(volume_id)s at %(mount_point)s.", { 'volume_id': volume_id, 'mount_point': self['mount_device'] }, exc_info=True, instance=instance) # Delete the attachment to mark the volume as "available". volume_api.attachment_delete(context, self['attachment_id'])
def _test_volume_attach(self, driver_bdm, bdm_dict, fake_volume, fail_check_av_zone=False, driver_attach=False, fail_driver_attach=False, volume_attach=True, fail_volume_attach=False, access_mode='rw', availability_zone=None): elevated_context = self.context.elevated() self.stubs.Set(self.context, 'elevated', lambda: elevated_context) self.mox.StubOutWithMock(driver_bdm._bdm_obj, 'save') self.mox.StubOutWithMock(encryptors, 'get_encryption_metadata') instance_detail = {'id': '123', 'uuid': uuids.uuid, 'availability_zone': availability_zone} instance = fake_instance.fake_instance_obj(self.context, **instance_detail) connector = {'ip': 'fake_ip', 'host': 'fake_host'} connection_info = {'data': {'access_mode': access_mode}} expected_conn_info = {'data': {'access_mode': access_mode}, 'serial': fake_volume['id']} enc_data = {'fake': 'enc_data'} self.volume_api.get(self.context, fake_volume['id']).AndReturn(fake_volume) if not fail_check_av_zone: self.volume_api.check_availability_zone(self.context, fake_volume, instance=instance).AndReturn(None) else: self.volume_api.check_availability_zone(self.context, fake_volume, instance=instance).AndRaise( test.TestingException) driver_bdm._bdm_obj.save().AndReturn(None) return instance, expected_conn_info self.virt_driver.get_volume_connector(instance).AndReturn(connector) self.volume_api.initialize_connection( elevated_context, fake_volume['id'], connector).AndReturn(connection_info) if driver_attach: encryptors.get_encryption_metadata( elevated_context, self.volume_api, fake_volume['id'], connection_info).AndReturn(enc_data) if not fail_driver_attach: self.virt_driver.attach_volume( elevated_context, expected_conn_info, instance, bdm_dict['device_name'], disk_bus=bdm_dict['disk_bus'], device_type=bdm_dict['device_type'], encryption=enc_data).AndReturn(None) else: self.virt_driver.attach_volume( elevated_context, expected_conn_info, instance, bdm_dict['device_name'], disk_bus=bdm_dict['disk_bus'], device_type=bdm_dict['device_type'], encryption=enc_data).AndRaise(test.TestingException) self.volume_api.terminate_connection( elevated_context, fake_volume['id'], connector).AndReturn(None) driver_bdm._bdm_obj.save().AndReturn(None) return instance, expected_conn_info if volume_attach: driver_bdm._bdm_obj.save().AndReturn(None) if not fail_volume_attach: self.volume_api.attach(elevated_context, fake_volume['id'], uuids.uuid, bdm_dict['device_name'], mode=access_mode).AndReturn(None) else: self.volume_api.attach(elevated_context, fake_volume['id'], uuids.uuid, bdm_dict['device_name'], mode=access_mode).AndRaise( test.TestingException) if driver_attach: self.virt_driver.detach_volume( expected_conn_info, instance, bdm_dict['device_name'], encryption=enc_data).AndReturn(None) self.volume_api.terminate_connection( elevated_context, fake_volume['id'], connector).AndReturn(None) self.volume_api.detach(elevated_context, fake_volume['id']).AndReturn(None) driver_bdm._bdm_obj.save().AndReturn(None) return instance, expected_conn_info