예제 #1
0
    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)
예제 #2
0
    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)
예제 #5
0
    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)
예제 #6
0
    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'])
예제 #7
0
    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'])
예제 #8
0
    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