コード例 #1
0
ファイル: xensm.py プロジェクト: littleidea/nova
class XenSMDriver(VolumeDriver):

    def _convert_config_params(self, conf_str):
        params = dict([item.split("=") for item in conf_str.split()])
        return params

    def _get_introduce_sr_keys(self, params):
        if 'name_label' in params:
            del params['name_label']
        keys = params.keys()
        keys.append('sr_type')
        return keys

    def _create_storage_repo(self, context, backend_ref):
        """Either creates or introduces SR on host
        depending on whether it exists in xapi db."""
        params = self._convert_config_params(backend_ref['config_params'])
        if 'name_label' in params:
            label = params['name_label']
            del params['name_label']
        else:
            label = 'SR-' + str(backend_ref['id'])

        params['sr_type'] = backend_ref['sr_type']

        if backend_ref['sr_uuid'] is None:
            # run the sr create command
            try:
                LOG.debug(_('SR name = %s') % label)
                LOG.debug(_('Params: %s') % str(params))
                sr_uuid = self._volumeops.create_sr(label, params)
            # update sr_uuid and created in db
            except Exception as ex:
                LOG.debug(_("Failed to create sr %s...continuing") \
                          % str(backend_ref['id']))
                raise exception.Error(_('Create failed'))

            LOG.debug(_('SR UUID of new SR is: %s') % sr_uuid)
            try:
                self.db.sm_backend_conf_update(context,
                                               backend_ref['id'],
                                               dict(sr_uuid=sr_uuid))
            except Exception as ex:
                LOG.exception(ex)
                raise exception.Error(_("Failed to update db"))

        else:
            # sr introduce, if not already done
            try:
                self._volumeops.introduce_sr(backend_ref['sr_uuid'], label,
                                              params)
            except Exception as ex:
                LOG.exception(ex)
                LOG.debug(_("Failed to introduce sr %s...continuing") \
                          % str(backend_ref['id']))

    def _create_storage_repos(self, context):
        """Create/Introduce storage repositories at start."""
        backends = self.db.sm_backend_conf_get_all(context)
        for backend in backends:
            try:
                self._create_storage_repo(context, backend)
            except Exception as ex:
                LOG.exception(ex)
                raise exception.Error(_('Failed to reach backend %d') \
                                      % backend['id'])

    def __init__(self, *args, **kwargs):
        """Connect to the hypervisor."""

        # This driver leverages Xen storage manager, and hence requires
        # hypervisor to be Xen
        if FLAGS.connection_type != 'xenapi':
            raise exception.Error(_('XenSMDriver requires xenapi connection'))

        url = FLAGS.xenapi_connection_url
        username = FLAGS.xenapi_connection_username
        password = FLAGS.xenapi_connection_password
        try:
            session = XenAPISession(url, username, password)
            self._volumeops = VolumeOps(session)
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to initiate session"))

        super(XenSMDriver, self).__init__(execute=utils.execute,
                                          sync_exec=utils.execute,
                                          *args, **kwargs)

    def do_setup(self, ctxt):
        """Setup includes creating or introducing storage repos
           existing in the database and destroying deleted ones."""

        # TODO purge storage repos
        self.ctxt = ctxt
        self._create_storage_repos(ctxt)

    def create_volume(self, volume):
        """Creates a logical volume. Can optionally return a Dictionary of
        changes to the volume object to be persisted."""

        # For now the scheduling logic will be to try to fit the volume in
        # the first available backend.
        # TODO better scheduling once APIs are in place
        sm_vol_rec = None
        backends = self.db.sm_backend_conf_get_all(self.ctxt)
        for backend in backends:
            # Ensure that storage repo exists, if not create.
            # This needs to be done because if nova compute and
            # volume are both running on this host, then, as a
            # part of detach_volume, compute could potentially forget SR
            self._create_storage_repo(self.ctxt, backend)
            sm_vol_rec = self._volumeops.\
                              create_volume_for_sm(volume,
                                                   backend['sr_uuid'])
            if sm_vol_rec:
                LOG.debug(_('Volume will be created in backend - %d') \
                          % backend['id'])
                break

        if sm_vol_rec:
            # Update db
            sm_vol_rec['id'] = volume['id']
            sm_vol_rec['backend_id'] = backend['id']
            try:
                self.db.sm_volume_create(self.ctxt, sm_vol_rec)
            except Exception as ex:
                LOG.exception(ex)
                raise exception.Error(_("Failed to update volume in db"))

        else:
            raise exception.Error(_('Unable to create volume'))

    def delete_volume(self, volume):

        vol_rec = self.db.sm_volume_get(self.ctxt, volume['id'])

        try:
            # If compute runs on this node, detach could have disconnected SR
            backend_ref = self.db.sm_backend_conf_get(self.ctxt,
                                                      vol_rec['backend_id'])
            self._create_storage_repo(self.ctxt, backend_ref)
            self._volumeops.delete_volume_for_sm(vol_rec['vdi_uuid'])
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to delete vdi"))

        try:
            self.db.sm_volume_delete(self.ctxt, volume['id'])
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to delete volume in db"))

    def local_path(self, volume):
        return str(volume['id'])

    def undiscover_volume(self, volume):
        """Undiscover volume on a remote host."""
        pass

    def discover_volume(self, context, volume):
        return str(volume['id'])

    def check_for_setup_error(self):
        pass

    def create_export(self, context, volume):
        """Exports the volume."""
        # !!! TODO
        pass

    def remove_export(self, context, volume):
        """Removes an export for a logical volume."""
        pass

    def ensure_export(self, context, volume):
        """Safely, synchronously recreates an export for a logical volume."""
        pass

    def initialize_connection(self, volume, connector):
        try:
            xensm_properties = dict(self.db.sm_volume_get(self.ctxt,
                                                          volume['id']))
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to find volume in db"))

        # Keep the volume id key consistent with what ISCSI driver calls it
        xensm_properties['volume_id'] = xensm_properties['id']
        del xensm_properties['id']

        try:
            backend_conf = self.db.\
                           sm_backend_conf_get(self.ctxt,
                                               xensm_properties['backend_id'])
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to find backend in db"))

        params = self._convert_config_params(backend_conf['config_params'])

        xensm_properties['flavor_id'] = backend_conf['flavor_id']
        xensm_properties['sr_uuid'] = backend_conf['sr_uuid']
        xensm_properties['sr_type'] = backend_conf['sr_type']
        xensm_properties.update(params)
        xensm_properties['introduce_sr_keys'] = self.\
                                                _get_introduce_sr_keys(params)
        return {
            'driver_volume_type': 'xensm',
            'data': xensm_properties
        }

    def terminate_connection(self, volume, connector):
        pass
コード例 #2
0
ファイル: xensm.py プロジェクト: yamahata/nova
class XenSMDriver(VolumeDriver):
    def _convert_config_params(self, conf_str):
        params = dict([item.split("=") for item in conf_str.split()])
        return params

    def _get_introduce_sr_keys(self, params):
        if 'name_label' in params:
            del params['name_label']
        keys = params.keys()
        keys.append('sr_type')
        return keys

    def _create_storage_repo(self, context, backend_ref):
        """Either creates or introduces SR on host
        depending on whether it exists in xapi db."""
        params = self._convert_config_params(backend_ref['config_params'])
        if 'name_label' in params:
            label = params['name_label']
            del params['name_label']
        else:
            label = 'SR-' + str(backend_ref['id'])

        params['sr_type'] = backend_ref['sr_type']

        if backend_ref['sr_uuid'] == None:
            # run the sr create command
            try:
                LOG.debug(_('SR name = %s') % label)
                LOG.debug(_('Params: %s') % str(params))
                sr_uuid = self._volumeops.create_sr(label, params)
            # update sr_uuid and created in db
            except Exception as ex:
                LOG.debug(_("Failed to create sr %s...continuing") \
                          % str(backend_ref['id']))
                raise exception.Error(_('Create failed'))

            LOG.debug(_('SR UUID of new SR is: %s') % sr_uuid)
            try:
                self.db.sm_backend_conf_update(context, backend_ref['id'],
                                               dict(sr_uuid=sr_uuid))
            except Exception as ex:
                LOG.exception(ex)
                raise exception.Error(_("Failed to update db"))

        else:
            # sr introduce, if not already done
            try:
                self._volumeops.introduce_sr(backend_ref['sr_uuid'], label,
                                             params)
            except Exception as ex:
                LOG.exception(ex)
                LOG.debug(_("Failed to introduce sr %s...continuing") \
                          % str(backend_ref['id']))

    def _create_storage_repos(self, context):
        """Create/Introduce storage repositories at start."""
        backends = self.db.sm_backend_conf_get_all(context)
        for backend in backends:
            try:
                self._create_storage_repo(context, backend)
            except Exception as ex:
                LOG.exception(ex)
                raise exception.Error(_('Failed to reach backend %d') \
                                      % backend['id'])

    def __init__(self, *args, **kwargs):
        """Connect to the hypervisor."""

        # This driver leverages Xen storage manager, and hence requires
        # hypervisor to be Xen
        if FLAGS.connection_type != 'xenapi':
            raise exception.Error(_('XenSMDriver requires xenapi connection'))

        url = FLAGS.xenapi_connection_url
        username = FLAGS.xenapi_connection_username
        password = FLAGS.xenapi_connection_password
        try:
            session = XenAPISession(url, username, password)
            self._volumeops = VolumeOps(session)
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to initiate session"))

        super(XenSMDriver, self).__init__(execute=utils.execute,
                                          sync_exec=utils.execute,
                                          *args,
                                          **kwargs)

    def do_setup(self, ctxt):
        """Setup includes creating or introducing storage repos
           existing in the database and destroying deleted ones."""

        # TODO purge storage repos
        self.ctxt = ctxt
        self._create_storage_repos(ctxt)

    def create_volume(self, volume):
        """Creates a logical volume. Can optionally return a Dictionary of
        changes to the volume object to be persisted."""

        # For now the scheduling logic will be to try to fit the volume in
        # the first available backend.
        # TODO better scheduling once APIs are in place
        sm_vol_rec = None
        backends = self.db.sm_backend_conf_get_all(self.ctxt)
        for backend in backends:
            # Ensure that storage repo exists, if not create.
            # This needs to be done because if nova compute and
            # volume are both running on this host, then, as a
            # part of detach_volume, compute could potentially forget SR
            self._create_storage_repo(self.ctxt, backend)
            sm_vol_rec = self._volumeops.\
                              create_volume_for_sm(volume,
                                                   backend['sr_uuid'])
            if sm_vol_rec:
                LOG.debug(_('Volume will be created in backend - %d') \
                          % backend['id'])
                break

        if sm_vol_rec:
            # Update db
            sm_vol_rec['id'] = volume['id']
            sm_vol_rec['backend_id'] = backend['id']
            try:
                self.db.sm_volume_create(self.ctxt, sm_vol_rec)
            except Exception as ex:
                LOG.exception(ex)
                raise exception.Error(_("Failed to update volume in db"))

        else:
            raise exception.Error(_('Unable to create volume'))

    def delete_volume(self, volume):

        vol_rec = self.db.sm_volume_get(self.ctxt, volume['id'])

        try:
            # If compute runs on this node, detach could have disconnected SR
            backend_ref = self.db.sm_backend_conf_get(self.ctxt,
                                                      vol_rec['backend_id'])
            self._create_storage_repo(self.ctxt, backend_ref)
            self._volumeops.delete_volume_for_sm(vol_rec['vdi_uuid'])
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to delete vdi"))

        try:
            self.db.sm_volume_delete(self.ctxt, volume['id'])
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to delete volume in db"))

    def local_path(self, volume):
        return str(volume['id'])

    def undiscover_volume(self, volume):
        """Undiscover volume on a remote host."""
        pass

    def discover_volume(self, context, volume):
        return str(volume['id'])

    def check_for_setup_error(self):
        pass

    def create_export(self, context, volume):
        """Exports the volume."""
        # !!! TODO
        pass

    def remove_export(self, context, volume):
        """Removes an export for a logical volume."""
        pass

    def ensure_export(self, context, volume):
        """Safely, synchronously recreates an export for a logical volume."""
        pass

    def initialize_connection(self, volume, address):
        try:
            xensm_properties = dict(
                self.db.sm_volume_get(self.ctxt, volume['id']))
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to find volume in db"))

        # Keep the volume id key consistent with what ISCSI driver calls it
        xensm_properties['volume_id'] = xensm_properties['id']
        del xensm_properties['id']

        try:
            backend_conf = self.db.\
                           sm_backend_conf_get(self.ctxt,
                                               xensm_properties['backend_id'])
        except Exception as ex:
            LOG.exception(ex)
            raise exception.Error(_("Failed to find backend in db"))

        params = self._convert_config_params(backend_conf['config_params'])

        xensm_properties['flavor_id'] = backend_conf['flavor_id']
        xensm_properties['sr_uuid'] = backend_conf['sr_uuid']
        xensm_properties['sr_type'] = backend_conf['sr_type']
        xensm_properties.update(params)
        xensm_properties['introduce_sr_keys'] = self.\
                                                _get_introduce_sr_keys(params)
        return {'driver_volume_type': 'xensm', 'data': xensm_properties}

    def terminate_connection(self, volume, address):
        pass