Exemple #1
0
    def get_iscsi_properties(self, volume):
        if not volume['provider_location']:
            err = _("Param volume['provider_location'] is invalid.")
            raise exception.InvalidParameterValue(err=err)

        iqn, trg_id = self.get_iqn_and_trgid(volume['provider_location'])

        iscsi_properties = {
            'target_discovered': False,
            'target_iqn': iqn,
            'target_portal': '%(ip)s:%(port)d' % {
                'ip': self.get_ip(),
                'port': self.iscsi_port
            },
            'volume_id': volume['id'],
            'access_mode': 'rw',
            'discard': False
        }
        ips = self.config.safe_get('iscsi_secondary_ip_addresses')
        if ips:
            target_portals = [iscsi_properties['target_portal']]
            for ip in ips:
                target_portals.append('%(ip)s:%(port)d' % {
                    'ip': ip,
                    'port': self.iscsi_port
                })
            iscsi_properties.update(target_portals=target_portals)
            count = len(target_portals)
            iscsi_properties.update(
                target_iqns=[iscsi_properties['target_iqn']] * count)
            iscsi_properties.update(target_lun=0)
            iscsi_properties.update(
                target_luns=[iscsi_properties['target_lun']] * count)

        if 'provider_auth' in volume:
            auth = volume['provider_auth']
            if auth:
                try:
                    (auth_method, auth_username, auth_password) = auth.split()
                    iscsi_properties['auth_method'] = auth_method
                    iscsi_properties['auth_username'] = auth_username
                    iscsi_properties['auth_password'] = auth_password
                except Exception:
                    LOG.error(_LE('Invalid provider_auth: %s'), auth)

        return iscsi_properties
Exemple #2
0
    def get_iqn_and_trgid(self, location):
        if not location:
            err = _('Param [location] is invalid.')
            raise exception.InvalidParameterValue(err=err)

        result = location.split(' ')
        if len(result) < 2:
            raise exception.InvalidInput(reason=location)

        data = result[0].split(',')
        if len(data) < 2:
            raise exception.InvalidInput(reason=location)

        iqn = result[1]
        trg_id = data[1]

        return iqn, int(trg_id, 10)
Exemple #3
0
    def _target_create(self, identifier):
        if not identifier:
            err = _('Param [identifier] is invalid.')
            raise exception.InvalidParameterValue(err=err)

        # 0 for no auth, 1 for single chap, 2 for mutual chap
        auth_type = 0
        chap_username = ''
        chap_password = ''
        provider_auth = ''
        if self.config.safe_get('use_chap_auth') and self.config.use_chap_auth:
            auth_type = 1
            chap_username = (self.config.safe_get('chap_username')
                             or volutils.generate_username(12))
            chap_password = (self.config.safe_get('chap_password')
                             or volutils.generate_password())
            provider_auth = ' '.join(('CHAP', chap_username, chap_password))

        trg_prefix = self.config.safe_get('target_prefix')
        trg_name = (self.TARGET_NAME_PREFIX + '%s') % identifier
        iqn = trg_prefix + trg_name

        try:
            out = self.exec_webapi('SYNO.Core.ISCSI.Target',
                                   'create',
                                   1,
                                   name=trg_name,
                                   iqn=iqn,
                                   auth_type=auth_type,
                                   user=chap_username,
                                   password=chap_password,
                                   max_sessions=0)

            self.check_response(out)

        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception('Failed to _target_create. [%s]', identifier)

        if not self.check_value_valid(out, ['data', 'target_id']):
            msg = _('Failed to get target_id of target [%s]') % trg_name
            raise exception.VolumeDriverException(message=msg)

        trg_id = out['data']['target_id']

        return iqn, trg_id, provider_auth
    def create(self, req, body):
        """Creates a new snapshot."""
        kwargs = {}
        context = req.environ['cinder.context']

        self.assert_valid_body(body, 'snapshot')

        snapshot = body['snapshot']
        kwargs['metadata'] = snapshot.get('metadata', None)

        try:
            volume_id = snapshot['volume_id']
        except KeyError:
            msg = _("'volume_id' must be specified")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            volume = self.volume_api.get(context, volume_id)
        except exception.VolumeNotFound as error:
            raise exc.HTTPNotFound(explanation=error.msg)
        force = snapshot.get('force', False)
        msg = _LI("Create snapshot from volume %s")
        LOG.info(msg, volume_id, context=context)
        self.validate_name_and_description(snapshot)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in snapshot:
            snapshot['display_name'] = snapshot.pop('name')

        try:
            force = strutils.bool_from_string(force, strict=True)
        except ValueError as error:
            msg = _("Invalid value for 'force': '%s'") % error.message
            raise exception.InvalidParameterValue(err=msg)

        if force:
            new_snapshot = self.volume_api.create_snapshot_force(
                context, volume, snapshot.get('display_name'),
                snapshot.get('description'), **kwargs)
        else:
            new_snapshot = self.volume_api.create_snapshot(
                context, volume, snapshot.get('display_name'),
                snapshot.get('description'), **kwargs)
        req.cache_db_snapshot(new_snapshot)

        return self._view_builder.detail(req, new_snapshot)
Exemple #5
0
    def _get_backup_base_name(self,
                              volume_id,
                              backup_id=None,
                              diff_format=False):
        """Return name of base image used for backup.

        Incremental backups use a new base name so we support old and new style
        format.
        """
        # Ensure no unicode
        if diff_format:
            return strutils.safe_encode("volume-%s.backup.base" % (volume_id))
        else:
            if backup_id is None:
                msg = _("Backup id required")
                raise exception.InvalidParameterValue(msg)
            return strutils.safe_encode("volume-%s.backup.%s" %
                                        (volume_id, backup_id))
Exemple #6
0
    def __init__(self, source_helper, target_device):
        self._source_helper = source_helper
        connection_type = target_device.get('connection_type')
        if connection_type == storage.XIV_CONNECTION_TYPE_FC:
            self._target_helper = (
                helper.DS8KReplicationTargetHelper(target_device))
        elif connection_type == storage.XIV_CONNECTION_TYPE_FC_ECKD:
            self._target_helper = (
                helper.DS8KReplicationTargetECKDHelper(target_device))
        else:
            raise exception.InvalidParameterValue(
                err=(_("Param [connection_type] %s in replication_device "
                       "is invalid.") % connection_type))

        self._target_helper.backend['lss_ids_for_cg'] = (
            self._source_helper.backend['lss_ids_for_cg'])
        self._mm_manager = MetroMirrorManager(self._source_helper,
                                              self._target_helper)
Exemple #7
0
 def _get_lss_ids_for_cg(self):
     lss_range = self._get_value('lss_range_for_cg')
     if lss_range:
         lss_range = lss_range.replace(' ', '').split('-')
         if len(lss_range) == 1:
             begin = int(lss_range[0], 16)
             end = begin
         else:
             begin = int(lss_range[0], 16)
             end = int(lss_range[1], 16)
         if begin > 0xFF or end > 0xFF or begin > end:
             raise exception.InvalidParameterValue(
                 err=_('Param [lss_range_for_cg] is invalid, it '
                       'should be within 00-FF.'))
         self.backend['lss_ids_for_cg'] = set(
             ('%02x' % i).upper() for i in range(begin, end + 1))
     else:
         self.backend['lss_ids_for_cg'] = set()
Exemple #8
0
    def create(self, req, body):
        """Creates a new snapshot."""
        kwargs = {}
        context = req.environ['cinder.context']

        if not self.is_valid_body(body, 'snapshot'):
            raise exc.HTTPBadRequest()

        snapshot = body['snapshot']
        kwargs['metadata'] = snapshot.get('metadata', None)

        volume_id = snapshot['volume_id']
        volume = self.volume_api.get(context, volume_id)
        force = snapshot.get('force', False)
        msg = _("Create snapshot from volume %s")
        LOG.audit(msg, volume_id, context=context)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in snapshot:
            snapshot['display_name'] = snapshot.get('name')
            del snapshot['name']

        if not utils.is_valid_boolstr(force):
            msg = _("Invalid value '%s' for force. ") % force
            raise exception.InvalidParameterValue(err=msg)

        if utils.bool_from_str(force):
            new_snapshot = self.volume_api.create_snapshot_force(
                context,
                volume,
                snapshot.get('display_name'),
                snapshot.get('description'),
                **kwargs)
        else:
            new_snapshot = self.volume_api.create_snapshot(
                context,
                volume,
                snapshot.get('display_name'),
                snapshot.get('description'),
                **kwargs)

        retval = _translate_snapshot_detail_view(context, new_snapshot)

        return {'snapshot': retval}
Exemple #9
0
    def create(self, req, body):
        """Creates a new snapshot."""
        kwargs = {}
        context = req.environ['cinder.context']

        if not self.is_valid_body(body, 'snapshot'):
            raise exc.HTTPUnprocessableEntity()

        snapshot = body['snapshot']
        kwargs['metadata'] = snapshot.get('metadata', None)

        try:
            volume_id = snapshot['volume_id']
        except KeyError:
            msg = _("'volume_id' must be specified")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            volume = self.volume_api.get(context, volume_id)
        except exception.NotFound:
            raise exc.HTTPNotFound()

        force = snapshot.get('force', False)
        msg = _("Create snapshot from volume %s")
        LOG.info(msg, volume_id, context=context)

        if not utils.is_valid_boolstr(force):
            msg = _("Invalid value '%s' for force. ") % force
            raise exception.InvalidParameterValue(err=msg)

        if strutils.bool_from_string(force):
            new_snapshot = self.volume_api.create_snapshot_force(
                context, volume, snapshot.get('display_name'),
                snapshot.get('display_description'), **kwargs)
        else:
            new_snapshot = self.volume_api.create_snapshot(
                context, volume, snapshot.get('display_name'),
                snapshot.get('display_description'), **kwargs)
        req.cache_db_snapshot(new_snapshot)

        retval = _translate_snapshot_detail_view(context, new_snapshot)

        return {'snapshot': retval}
Exemple #10
0
    def do_setup(self, context):
        """Sets up and verify Hitachi HNAS storage connection."""
        self.context = context
        self._check_fs_list()

        version_info = self.backend.get_version()
        LOG.info(_LI("HNAS iSCSI driver."))
        LOG.info(_LI("HNAS model: %(mdl)s"), {'mdl': version_info['model']})
        LOG.info(_LI("HNAS version: %(version)s"),
                 {'version': version_info['version']})
        LOG.info(_LI("HNAS hardware: %(hw)s"),
                 {'hw': version_info['hardware']})
        LOG.info(_LI("HNAS S/N: %(sn)s"), {'sn': version_info['serial']})
        service_list = self.config['services'].keys()
        for svc in service_list:
            svc = self.config['services'][svc]
            pool = {}
            pool['pool_name'] = svc['volume_type']
            pool['service_label'] = svc['volume_type']
            pool['fs'] = svc['hdp']

            self.pools.append(pool)

        LOG.debug("Configured pools: %(pool)s", {'pool': self.pools})

        evs_info = self.backend.get_evs_info()
        LOG.info(_LI("Configured EVSs: %(evs)s"), {'evs': evs_info})

        for svc in self.config['services'].keys():
            svc_ip = self.config['services'][svc]['iscsi_ip']
            if svc_ip in evs_info.keys():
                LOG.info(_LI("iSCSI portal found for service: %(svc_ip)s"),
                         {'svc_ip': svc_ip})
                self.config['services'][svc]['evs'] = (
                    evs_info[svc_ip]['evs_number'])
                self.config['services'][svc]['iscsi_port'] = '3260'
                self.config['services'][svc]['port'] = '0'
            else:
                LOG.error(
                    _LE("iSCSI portal not found "
                        "for service: %(svc)s"), {'svc': svc_ip})
                raise exception.InvalidParameterValue(err=svc_ip)
        LOG.info(_LI("HNAS iSCSI Driver loaded successfully."))
    def _parse_ns_output(self, switch_data):
        """Parses name server data.

        Parses nameserver raw data and adds the device port wwns to the list

        :returns: List -- list of device port wwn from ns info
        """
        return_list = []
        for line in switch_data:
            if not (" NL " in line or " N " in line):
                continue
            linesplit = line.split(';')
            if len(linesplit) > 2:
                node_port_wwn = linesplit[2]
                return_list.append(node_port_wwn)
            else:
                msg = _("Malformed nameserver string: %s") % line
                LOG.error(msg)
                raise exception.InvalidParameterValue(err=msg)
        return return_list
Exemple #12
0
    def setup(self, ctxt):
        LOG.info("Initiating connection to IBM DS8K storage system.")
        connection_type = self.configuration.safe_get('connection_type')
        replication_devices = self.configuration.safe_get('replication_device')
        if connection_type == storage.XIV_CONNECTION_TYPE_FC:
            if not replication_devices:
                self._helper = helper.DS8KCommonHelper(self.configuration,
                                                       self._connector_obj)
            else:
                self._helper = (helper.DS8KReplicationSourceHelper(
                    self.configuration, self._connector_obj))
        elif connection_type == storage.XIV_CONNECTION_TYPE_FC_ECKD:
            self._helper = helper.DS8KECKDHelper(self.configuration,
                                                 self._connector_obj)
        else:
            err = (_("Param [connection_type] %s is invalid.") %
                   connection_type)
            raise exception.InvalidParameterValue(err=err)

        if replication_devices:
            self._do_replication_setup(replication_devices, self._helper)
Exemple #13
0
    def show_target(self, tid, iqn=None, **kwargs):

        conf_file = self.ctl_conf

        if iqn is None:
            raise exception.InvalidParameterValue(
                err=_('valid iqn needed for show_target'))

        if os.path.exists(conf_file):
            with utils.temporary_chown(conf_file):
                try:
                    ctl_conf_text = open(conf_file, 'r+')
                    full_txt = ctl_conf_text.readlines()
                    for line in full_txt:
                        if re.search(iqn, line):
                            ctl_conf_text.close()
                            return
                finally:
                    ctl_conf_text.close()

        raise exception.NotFound()
Exemple #14
0
    def _get_lun_status(self, lun_name):
        if not lun_name:
            err = _('Param [lun_name] is invalid.')
            raise exception.InvalidParameterValue(err=err)

        try:
            lun_info = self._get_lun_info(lun_name,
                                          ['status', 'is_action_locked'])
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception('Failed to _get_lun_status. [%s]', lun_name)

        if not self.check_value_valid(lun_info, ['status'], string_types):
            raise exception.MalformedResponse(cmd='_get_lun_status',
                                              reason=_('status not found'))
        if not self.check_value_valid(lun_info, ['is_action_locked'], bool):
            raise exception.MalformedResponse(cmd='_get_lun_status',
                                              reason=_('action_locked '
                                                       'not found'))

        return lun_info['status'], lun_info['is_action_locked']
    def file_clone(self, fs_label, src, name):
        """Clones NFS files to a new one named 'name'.

        Clone primitive used to support all NFS snapshot/cloning functions.

        :param fs_label:  file system label of the new file
        :param src: source file
        :param name: target path of the new created file
        """
        fs_list = self._get_fs_list()
        fs = fs_list.get(fs_label)
        if not fs:
            LOG.error("Can't find file %(file)s in FS %(label)s",
                      {'file': src, 'label': fs_label})
            msg = _('FS label: %(fs_label)s') % {'fs_label': fs_label}
            raise exception.InvalidParameterValue(err=msg)

        self._run_cmd("console-context", "--evs", fs['evsid'],
                      'file-clone-create', '-f', fs_label, src, name)
        LOG.debug('file_clone: fs:%(fs_label)s %(src)s/src: -> %(name)s/dst',
                  {'fs_label': fs_label, 'src': src, 'name': name})
Exemple #16
0
    def get_all(self, context, search_opts=None, marker=None, limit=None,
                offset=None, sort_keys=None, sort_dirs=None):
        context.authorize(policy.GET_ALL_POLICY)

        search_opts = search_opts or {}

        all_tenants = search_opts.pop('all_tenants', '0')
        if not strutils.is_valid_boolstr(all_tenants):
            msg = _("all_tenants must be a boolean, got '%s'.") % all_tenants
            raise exception.InvalidParameterValue(err=msg)

        if context.is_admin and strutils.bool_from_string(all_tenants):
            backups = objects.BackupList.get_all(context, search_opts,
                                                 marker, limit, offset,
                                                 sort_keys, sort_dirs)
        else:
            backups = objects.BackupList.get_all_by_project(
                context, context.project_id, search_opts,
                marker, limit, offset, sort_keys, sort_dirs
            )

        return backups
Exemple #17
0
    def _get_lun_info(self, lun_name, additional=None):
        if not lun_name:
            err = _('Param [lun_name] is invalid.')
            raise exception.InvalidParameterValue(err=err)

        params = {'uuid': lun_name}
        if additional is not None:
            params['additional'] = additional

        try:
            out = self.exec_webapi('SYNO.Core.ISCSI.LUN', 'get', 1, **params)

            self.check_response(out, uuid=lun_name)
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception('Failed to _get_lun_info. [%s]', lun_name)

        if not self.check_value_valid(out, ['data', 'lun'], object):
            raise exception.MalformedResponse(cmd='_get_lun_info',
                                              reason=_('lun info not found'))

        return out['data']['lun']
    def _lun_map_unmap_target(self, volume_name, is_map, trg_id):
        if 0 > trg_id:
            err = _('trg_id is invalid: %d.') % trg_id
            raise exception.InvalidParameterValue(err=err)

        try:
            lun_uuid = self._get_lun_uuid(volume_name)
            out = self.exec_webapi('SYNO.Core.ISCSI.LUN',
                                   'map_target' if is_map else 'unmap_target',
                                   1,
                                   uuid=lun_uuid,
                                   target_ids=['%d' % trg_id])

            self.check_response(out)

        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception(_LE('Failed to _lun_map_unmap_target.'
                                  '[%(action)s][%(vol)s].'),
                              {'action': ('map_target' if is_map
                                          else 'unmap_target'),
                               'vol': volume_name})
Exemple #19
0
    def _clear_volume(self, volume, is_snapshot=False):
        # zero out old volumes to prevent data leaking between users
        # TODO(ja): reclaiming space should be done lazy and low priority
        if is_snapshot:
            # if the volume to be cleared is a snapshot of another volume
            # we need to clear out the volume using the -cow instead of the
            # directly volume path.  We need to skip this if we are using
            # thin provisioned LVs.
            # bug# lp1191812
            dev_path = self.local_path(volume) + "-cow"
        else:
            dev_path = self.local_path(volume)

        # TODO(jdg): Maybe we could optimize this for snaps by looking at
        # the cow table and only overwriting what's necessary?
        # for now we're still skipping on snaps due to hang issue
        if not os.path.exists(dev_path):
            msg = (_('Volume device file path %s does not exist.') % dev_path)
            LOG.error(msg)
            raise exception.VolumeBackendAPIException(data=msg)

        size_in_g = (volume.get('volume_size')
                     if is_snapshot else volume.get('size'))
        if size_in_g is None:
            msg = (_("Size for volume: %s not found, cannot secure delete.") %
                   volume['id'])
            LOG.error(msg)
            raise exception.InvalidParameterValue(msg)

        # clear_volume expects sizes in MiB, we store integer GiB
        # be sure to convert before passing in
        vol_sz_in_meg = size_in_g * units.Ki

        volutils.clear_volume(
            vol_sz_in_meg,
            dev_path,
            volume_clear=self.configuration.volume_clear,
            volume_clear_size=self.configuration.volume_clear_size)
Exemple #20
0
 def _create_client(self):
     san_ip = self._get_value('san_ip')
     try:
         clear_pass = cryptish.decrypt(self._get_value('san_password'))
     except TypeError:
         raise exception.InvalidParameterValue(
             err=_('Param [san_password] is invalid.'))
     verify = self._get_certificate(san_ip)
     try:
         self._client = restclient.RESTScheduler(
             san_ip,
             self._get_value('san_login'),
             clear_pass,
             self._connector_obj,
             verify)
     except restclient.TimeoutException:
         raise restclient.APIException(
             data=(_("Can't connect to %(host)s") % {'host': san_ip}))
     self.backend['rest_version'] = self._get_version()['bundle_version']
     LOG.info("Connection to DS8K storage system %(host)s has been "
              "established successfully, the version of REST is %(rest)s.",
              {'host': self._get_value('san_ip'),
               'rest': self.backend['rest_version']})
Exemple #21
0
    def _get_snapshot_status(self, snapshot_uuid):
        if not snapshot_uuid:
            err = _('Param [snapshot_uuid] is invalid.')
            raise exception.InvalidParameterValue(err=err)

        try:
            snapshot_info = self._get_snapshot_info(
                snapshot_uuid, ['status', 'is_action_locked'])
        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception('Failed to _get_snapshot_info. [%s]',
                              snapshot_uuid)

        if not self.check_value_valid(snapshot_info, ['status'], string_types):
            raise exception.MalformedResponse(cmd='_get_snapshot_status',
                                              reason=_('status not found'))
        if not self.check_value_valid(snapshot_info, ['is_action_locked'],
                                      bool):
            raise exception.MalformedResponse(cmd='_get_snapshot_status',
                                              reason=_('action_locked '
                                                       'not found'))

        return snapshot_info['status'], snapshot_info['is_action_locked']
Exemple #22
0
    def test_query_aggr_options(self):
        na_server = netapp_api.NaServer('127.0.0.1')
        body = etree.XML("""<results status="passed">
        <options>
        <aggr-option-info>
        <name>ha_policy</name>
        <value>cfo</value>
        </aggr-option-info>
        <aggr-option-info>
        <name>raidtype</name>
        <value>raid_dp</value>
        </aggr-option-info>
        </options>
        </results>""")

        self.mock_object(ssc_cmode.netapp_api, 'invoke_api',
                         mock.Mock(return_value=[netapp_api.NaElement(body)]))

        aggr_attribs = ssc_cmode.query_aggr_options(na_server, 'aggr0')
        if aggr_attribs:
            self.assertEqual('cfo', aggr_attribs['ha_policy'])
            self.assertEqual('raid_dp', aggr_attribs['raid_type'])
        else:
            raise exception.InvalidParameterValue("Incorrect aggr options")
Exemple #23
0
    def update(self, req, id, body):
        """Update Quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects only immediate parent admin or the
        CLOUD admin are able to perform an update.

        :param req: request
        :param id: target project id that needs to be updated
        :param body: key, value pair that that will be
                     applied to the resources if the update
                     succeeds
        """
        context = req.environ['cinder.context']
        authorize_update(context)
        self.validate_string_length(id,
                                    'quota_set_name',
                                    min_length=1,
                                    max_length=255)

        self.assert_valid_body(body, 'quota_set')

        # Get the optional argument 'skip_validation' from body,
        # if skip_validation is False, then validate existing resource.
        skip_flag = body.get('skip_validation', True)
        if not utils.is_valid_boolstr(skip_flag):
            msg = _("Invalid value '%s' for skip_validation.") % skip_flag
            raise exception.InvalidParameterValue(err=msg)
        skip_flag = strutils.bool_from_string(skip_flag)

        target_project_id = id
        bad_keys = []

        # NOTE(ankit): Pass #1 - In this loop for body['quota_set'].items(),
        # we figure out if we have any bad keys.
        for key, value in body['quota_set'].items():
            if (key not in QUOTAS and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue

        if len(bad_keys) > 0:
            msg = _("Bad key(s) in quota set: %s") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # Saving off this value since we need to use it multiple times
        use_nested_quotas = QUOTAS.using_nested_quotas()
        if use_nested_quotas:
            # Get the parent_id of the target project to verify whether we are
            # dealing with hierarchical namespace or non-hierarchical namespace
            target_project = quota_utils.get_project_hierarchy(
                context, target_project_id, parents_as_ids=True)
            parent_id = target_project.parent_id

            if parent_id:
                # Get the children of the project which the token is scoped to
                # in order to know if the target_project is in its hierarchy.
                context_project = quota_utils.get_project_hierarchy(
                    context,
                    context.project_id,
                    subtree_as_ids=True,
                    is_admin_project=context.is_admin)
                self._authorize_update_or_delete(context_project,
                                                 target_project.id, parent_id)

        # NOTE(ankit): Pass #2 - In this loop for body['quota_set'].keys(),
        # we validate the quota limits to ensure that we can bail out if
        # any of the items in the set is bad. Meanwhile we validate value
        # to ensure that the value can't be lower than number of existing
        # resources.
        quota_values = QUOTAS.get_project_quotas(context,
                                                 target_project_id,
                                                 defaults=False)
        valid_quotas = {}
        reservations = []
        for key in body['quota_set'].keys():
            if key in NON_QUOTA_KEYS:
                continue

            value = utils.validate_integer(body['quota_set'][key],
                                           key,
                                           min_value=-1,
                                           max_value=db.MAX_INT)

            # Can't skip the validation of nested quotas since it could mess up
            # hierarchy if parent limit is less than childrens' current usage
            if not skip_flag or use_nested_quotas:
                self._validate_existing_resource(key, value, quota_values)

            if use_nested_quotas:
                try:
                    reservations += self._update_nested_quota_allocated(
                        context, target_project, quota_values, key, value)
                except exception.OverQuota as e:
                    if reservations:
                        db.reservation_rollback(context, reservations)
                    raise webob.exc.HTTPBadRequest(explanation=e.message)

            valid_quotas[key] = value

        # NOTE(ankit): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one shot
        # without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                db.quota_update(context, target_project_id, key, value)
            except exception.ProjectQuotaNotFound:
                db.quota_create(context, target_project_id, key, value)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()

        if reservations:
            db.reservation_commit(context, reservations)
        return {'quota_set': self._get_quotas(context, target_project_id)}
Exemple #24
0
    def get_device_mapping_from_network(self, initiator_wwn_list,
                                        target_wwn_list):
        """Provides the initiator/target map for available SAN contexts.

        Looks up nameserver of each fc SAN configured to find logged in devices
        and returns a map of initiator and target port WWNs for each fabric.

        :param initiator_wwn_list: List of initiator port WWN
        :param target_wwn_list: List of target port WWN
        :returns: List -- device wwn map in following format

        .. code-block:: default

            {
                <San name>: {
                    'initiator_port_wwn_list':
                    ('200000051e55a100', '200000051e55a121'..)
                    'target_port_wwn_list':
                    ('100000051e55a100', '100000051e55a121'..)
                }
            }

        :raises Exception: when connection to fabric is failed
        """
        device_map = {}
        formatted_target_list = []
        formatted_initiator_list = []
        fabric_map = {}
        fabric_names = self.configuration.fc_fabric_names
        fabrics = None
        if not fabric_names:
            raise exception.InvalidParameterValue(
                err=_("Missing Fibre Channel SAN configuration "
                      "param - fc_fabric_names"))

        fabrics = [x.strip() for x in fabric_names.split(',')]
        LOG.debug("FC Fabric List: %s", fabrics)
        if fabrics:
            for t in target_wwn_list:
                formatted_target_list.append(fczm_utils.get_formatted_wwn(t))

            for i in initiator_wwn_list:
                formatted_initiator_list.append(
                    fczm_utils.get_formatted_wwn(i))

            for fabric_name in fabrics:
                fabric_ip = self.fabric_configs[fabric_name].safe_get(
                    'fc_fabric_address')

                # Get name server data from fabric and find the targets
                # logged in
                nsinfo = ''
                try:
                    LOG.debug("Getting name server data for "
                              "fabric %s", fabric_ip)
                    conn = self._get_southbound_client(fabric_name)
                    nsinfo = conn.get_nameserver_info()
                except exception.FCSanLookupServiceException:
                    with excutils.save_and_reraise_exception():
                        LOG.error(
                            "Failed collecting name server info from"
                            " fabric %s", fabric_ip)
                except Exception as e:
                    msg = _("SSH connection failed "
                            "for %(fabric)s with error: %(err)s") % {
                                'fabric': fabric_ip,
                                'err': e
                            }
                    LOG.error(msg)
                    raise exception.FCSanLookupServiceException(message=msg)

                LOG.debug("Lookup service:nsinfo-%s", nsinfo)
                LOG.debug("Lookup service:initiator list from "
                          "caller-%s", formatted_initiator_list)
                LOG.debug("Lookup service:target list from "
                          "caller-%s", formatted_target_list)
                visible_targets = [
                    x for x in nsinfo if x in formatted_target_list
                ]
                visible_initiators = [
                    x for x in nsinfo if x in formatted_initiator_list
                ]

                if visible_targets:
                    LOG.debug("Filtered targets is: %s", visible_targets)
                    # getting rid of the : before returning
                    for idx, elem in enumerate(visible_targets):
                        elem = str(elem).replace(':', '')
                        visible_targets[idx] = elem
                else:
                    LOG.debug("No targets are in the nameserver for SAN %s",
                              fabric_name)

                if visible_initiators:
                    # getting rid of the : before returning ~sk
                    for idx, elem in enumerate(visible_initiators):
                        elem = str(elem).replace(':', '')
                        visible_initiators[idx] = elem
                else:
                    LOG.debug(
                        "No initiators are in the nameserver "
                        "for SAN %s", fabric_name)

                fabric_map = {
                    'initiator_port_wwn_list': visible_initiators,
                    'target_port_wwn_list': visible_targets
                }
                device_map[fabric_name] = fabric_map
        LOG.debug("Device map for SAN context: %s", device_map)
        return device_map
    def get_device_mapping_from_network(self, initiator_wwn_list,
                                        target_wwn_list):
        """Provides the initiator/target map for available SAN contexts.

        Looks up fcns database of each fc SAN configured to find logged in
        devices and returns a map of initiator and target port WWNs for each
        fabric.

        :param initiator_wwn_list: List of initiator port WWN
        :param target_wwn_list: List of target port WWN
        :returns: List -- device wwn map in following format
            {
                <San name>: {
                    'initiator_port_wwn_list':
                    ('200000051e55a100', '200000051e55a121'..)
                    'target_port_wwn_list':
                    ('100000051e55a100', '100000051e55a121'..)
                }
            }
        :raises: Exception when connection to fabric is failed
        """
        device_map = {}
        formatted_target_list = []
        formatted_initiator_list = []
        fabric_map = {}
        fabric_names = self.configuration.fc_fabric_names

        if not fabric_names:
            raise exception.InvalidParameterValue(
                err=_("Missing Fibre Channel SAN configuration "
                      "param - fc_fabric_names"))

        fabrics = [x.strip() for x in fabric_names.split(',')]

        LOG.debug("FC Fabric List: %s", fabrics)
        if fabrics:
            for t in target_wwn_list:
                formatted_target_list.append(zm_utils.get_formatted_wwn(t))

            for i in initiator_wwn_list:
                formatted_initiator_list.append(zm_utils.get_formatted_wwn(i))

            for fabric_name in fabrics:
                self.switch_ip = self.fabric_configs[fabric_name].safe_get(
                    'cisco_fc_fabric_address')
                self.switch_user = self.fabric_configs[fabric_name].safe_get(
                    'cisco_fc_fabric_user')
                self.switch_pwd = self.fabric_configs[fabric_name].safe_get(
                    'cisco_fc_fabric_password')
                self.switch_port = self.fabric_configs[fabric_name].safe_get(
                    'cisco_fc_fabric_port')
                zoning_vsan = self.fabric_configs[fabric_name].safe_get(
                    'cisco_zoning_vsan')

                # Get name server data from fabric and find the targets
                # logged in
                nsinfo = ''
                LOG.debug("show fcns database for vsan %s", zoning_vsan)
                nsinfo = self.get_nameserver_info(zoning_vsan)

                LOG.debug("Lookup service:fcnsdatabase-%s", nsinfo)
                LOG.debug("Lookup service:initiator list from caller-%s",
                          formatted_initiator_list)
                LOG.debug("Lookup service:target list from caller-%s",
                          formatted_target_list)
                visible_targets = [
                    x for x in nsinfo if x in formatted_target_list
                ]
                visible_initiators = [
                    x for x in nsinfo if x in formatted_initiator_list
                ]

                if visible_targets:
                    LOG.debug("Filtered targets is: %s", visible_targets)
                    # getting rid of the : before returning
                    for idx, elem in enumerate(visible_targets):
                        elem = str(elem).replace(':', '')
                        visible_targets[idx] = elem
                else:
                    LOG.debug(
                        "No targets are in the fcns database"
                        " for vsan %s", zoning_vsan)

                if visible_initiators:
                    # getting rid of the : before returning ~sk
                    for idx, elem in enumerate(visible_initiators):
                        elem = str(elem).replace(':', '')
                        visible_initiators[idx] = elem
                else:
                    LOG.debug(
                        "No initiators are in the fcns database"
                        " for vsan %s", zoning_vsan)

                fabric_map = {
                    'initiator_port_wwn_list': visible_initiators,
                    'target_port_wwn_list': visible_targets
                }
                device_map[zoning_vsan] = fabric_map
        LOG.debug("Device map for SAN context: %s", device_map)
        return device_map
Exemple #26
0
    def update(self, req, id, body):
        """Update Quota for a particular tenant

        This works for hierarchical and non-hierarchical projects. For
        hierarchical projects only immediate parent admin or the
        CLOUD admin are able to perform an update.

        :param req: request
        :param id: target project id that needs to be updated
        :param body: key, value pair that that will be
                     applied to the resources if the update
                     succeeds
        """
        context = req.environ['cinder.context']
        authorize_update(context)
        self.validate_string_length(id,
                                    'quota_set_name',
                                    min_length=1,
                                    max_length=255)

        self.assert_valid_body(body, 'quota_set')

        # Get the optional argument 'skip_validation' from body,
        # if skip_validation is False, then validate existing resource.
        skip_flag = body.get('skip_validation', True)
        if not utils.is_valid_boolstr(skip_flag):
            msg = _("Invalid value '%s' for skip_validation.") % skip_flag
            raise exception.InvalidParameterValue(err=msg)
        skip_flag = strutils.bool_from_string(skip_flag)

        target_project_id = id
        bad_keys = []

        # NOTE(ankit): Pass #1 - In this loop for body['quota_set'].items(),
        # we figure out if we have any bad keys.
        for key, value in body['quota_set'].items():
            if (key not in QUOTAS and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue

        if len(bad_keys) > 0:
            msg = _("Bad key(s) in quota set: %s") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # Get the parent_id of the target project to verify whether we are
        # dealing with hierarchical namespace or non-hierarchical namespace.
        target_project = self._get_project(context, target_project_id)
        parent_id = target_project.parent_id

        if parent_id:
            # Get the children of the project which the token is scoped to
            # in order to know if the target_project is in its hierarchy.
            context_project = self._get_project(context,
                                                context.project_id,
                                                subtree_as_ids=True)
            self._authorize_update_or_delete(context_project,
                                             target_project.id, parent_id)
            parent_project_quotas = QUOTAS.get_project_quotas(
                context, parent_id)

        # NOTE(ankit): Pass #2 - In this loop for body['quota_set'].keys(),
        # we validate the quota limits to ensure that we can bail out if
        # any of the items in the set is bad. Meanwhile we validate value
        # to ensure that the value can't be lower than number of existing
        # resources.
        quota_values = QUOTAS.get_project_quotas(context,
                                                 target_project_id,
                                                 defaults=False)
        valid_quotas = {}
        allocated_quotas = {}
        for key in body['quota_set'].keys():
            if key in NON_QUOTA_KEYS:
                continue

            if not skip_flag:
                self._validate_existing_resource(key, value, quota_values)

            if parent_id:
                value = self._validate_quota_limit(body['quota_set'], key,
                                                   quota_values,
                                                   parent_project_quotas)
                original_quota = 0
                if quota_values.get(key):
                    original_quota = quota_values[key]['limit']

                allocated_quotas[key] = (
                    parent_project_quotas[key].get('allocated', 0) + value -
                    original_quota)
            else:
                value = self._validate_quota_limit(body['quota_set'], key)
            valid_quotas[key] = value

        # NOTE(ankit): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one shot
        # without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                db.quota_update(context, target_project_id, key, value)
            except exception.ProjectQuotaNotFound:
                db.quota_create(context, target_project_id, key, value)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
            # If hierarchical projects, update child's quota first
            # and then parents quota. In future this needs to be an
            # atomic operation.
            if parent_id:
                if key in allocated_quotas.keys():
                    try:
                        db.quota_allocated_update(context, parent_id, key,
                                                  allocated_quotas[key])
                    except exception.ProjectQuotaNotFound:
                        parent_limit = parent_project_quotas[key]['limit']
                        db.quota_create(context,
                                        parent_id,
                                        key,
                                        parent_limit,
                                        allocated=allocated_quotas[key])

        return {
            'quota_set':
            self._get_quotas(context,
                             target_project_id,
                             parent_project_id=parent_id)
        }
Exemple #27
0
    def do_setup(self, context):
        """Perform internal driver setup."""
        version_info = self.backend.get_version()
        LOG.info("HNAS NFS driver.")
        LOG.info("HNAS model: %(mdl)s", {'mdl': version_info['model']})
        LOG.info("HNAS version: %(ver)s", {'ver': version_info['version']})
        LOG.info("HNAS hardware: %(hw)s", {'hw': version_info['hardware']})
        LOG.info("HNAS S/N: %(sn)s", {'sn': version_info['serial']})

        self.context = context
        self._load_shares_config(
            getattr(self.configuration, self.driver_prefix + '_shares_config'))
        LOG.info("Review shares: %(shr)s", {'shr': self.shares})

        elist = self.backend.get_export_list()

        # Check for all configured exports
        for svc_name, svc_info in self.config['services'].items():
            server_ip = svc_info['hdp'].split(':')[0]
            mountpoint = svc_info['hdp'].split(':')[1]

            # Ensure export are configured in HNAS
            export_configured = False
            for export in elist:
                if mountpoint == export['name'] and server_ip in export['evs']:
                    svc_info['export'] = export
                    export_configured = True

            # Ensure export are reachable
            try:
                out, err = self._execute('showmount', '-e', server_ip)
            except processutils.ProcessExecutionError:
                LOG.exception("NFS server %(srv)s not reachable!",
                              {'srv': server_ip})
                raise

            export_list = out.split('\n')[1:]
            export_list.pop()
            mountpoint_not_found = mountpoint not in map(
                lambda x: x.split()[0], export_list)
            if (len(export_list) < 1 or mountpoint_not_found
                    or not export_configured):
                LOG.error(
                    "Configured share %(share)s is not present"
                    "in %(srv)s.", {
                        'share': mountpoint,
                        'srv': server_ip
                    })
                msg = _('Section: %(svc_name)s') % {'svc_name': svc_name}
                raise exception.InvalidParameterValue(err=msg)

        LOG.debug("Loading services: %(svc)s",
                  {'svc': self.config['services']})

        service_list = self.config['services'].keys()
        for svc in service_list:
            svc = self.config['services'][svc]
            pool = {}
            pool['pool_name'] = svc['pool_name']
            pool['service_label'] = svc['pool_name']
            pool['fs'] = svc['hdp']

            self.pools.append(pool)

        LOG.debug("Configured pools: %(pool)s", {'pool': self.pools})
        LOG.info("HNAS NFS Driver loaded successfully.")
Exemple #28
0
    def update(self, req, id, body):
        context = req.environ['cinder.context']
        authorize_update(context)
        self.validate_string_length(id,
                                    'quota_set_name',
                                    min_length=1,
                                    max_length=255)

        project_id = id
        self.assert_valid_body(body, 'quota_set')

        # Get the optional argument 'skip_validation' from body,
        # if skip_validation is False, then validate existing resource.
        skip_flag = body.get('skip_validation', True)
        if not utils.is_valid_boolstr(skip_flag):
            msg = _("Invalid value '%s' for skip_validation.") % skip_flag
            raise exception.InvalidParameterValue(err=msg)
        skip_flag = strutils.bool_from_string(skip_flag)

        bad_keys = []

        # NOTE(ankit): Pass #1 - In this loop for body['quota_set'].items(),
        # we figure out if we have any bad keys.
        for key, value in body['quota_set'].items():
            if (key not in QUOTAS and key not in NON_QUOTA_KEYS):
                bad_keys.append(key)
                continue

        if len(bad_keys) > 0:
            msg = _("Bad key(s) in quota set: %s") % ",".join(bad_keys)
            raise webob.exc.HTTPBadRequest(explanation=msg)

        # NOTE(ankit): Pass #2 - In this loop for body['quota_set'].keys(),
        # we validate the quota limits to ensure that we can bail out if
        # any of the items in the set is bad. Meanwhile we validate value
        # to ensure that the value can't be lower than number of existing
        # resources.
        quota_values = QUOTAS.get_project_quotas(context, project_id)
        valid_quotas = {}
        for key in body['quota_set'].keys():
            if key in NON_QUOTA_KEYS:
                continue

            valid_quotas[key] = self.validate_integer(body['quota_set'][key],
                                                      key,
                                                      min_value=-1,
                                                      max_value=db.MAX_INT)

            if not skip_flag:
                self._validate_existing_resource(key, value, quota_values)

        # NOTE(ankit): Pass #3 - At this point we know that all the keys and
        # values are valid and we can iterate and update them all in one shot
        # without having to worry about rolling back etc as we have done
        # the validation up front in the 2 loops above.
        for key, value in valid_quotas.items():
            try:
                db.quota_update(context, project_id, key, value)
            except exception.ProjectQuotaNotFound:
                db.quota_create(context, project_id, key, value)
            except exception.AdminRequired:
                raise webob.exc.HTTPForbidden()
        return {'quota_set': self._get_quotas(context, id)}
    def create(self, req, body):
        """Creates a new snapshot."""
        # cinderclient中传入的值:
        # body = {'snapshot': {'volume_id': volume_id,
        #                      'force': force,
        #                      'name': name,
        #                      'description': description,
        #                      'metadata': snapshot_metadata}}
        kwargs = {}
        context = req.environ['cinder.context']

        # 检查body是否合法, body需要满足以下条件:
        # 1.body存在
        # 2.'snapshot' in body
        # 3.body['snapshot']是个字典
        self.assert_valid_body(body, 'snapshot')

        snapshot = body['snapshot']
        kwargs['metadata'] = snapshot.get('metadata', None)

        try:
            volume_id = snapshot['volume_id']
        except KeyError:
            msg = _("'volume_id' must be specified")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            volume = self.volume_api.get(context, volume_id)
        except exception.VolumeNotFound as error:
            raise exc.HTTPNotFound(explanation=error.msg)
        force = snapshot.get('force', False)
        msg = _LI("Create snapshot from volume %s")
        LOG.info(msg, volume_id, context=context)
        # 对name和description做如下修改和判定:
        # 1.如果name不为None, 去除name首尾的空白字符
        # 2.检查name的长度, 要求0<len<255
        # 3.如果description不为None, 检查description的长度, 要求0<len<255
        self.validate_name_and_description(snapshot)

        # NOTE(thingee): v2 API allows name instead of display_name
        if 'name' in snapshot:
            snapshot['display_name'] = snapshot.pop('name')

        try:
            force = strutils.bool_from_string(force, strict=True)
        except ValueError as error:
            msg = _("Invalid value for 'force': '%s'") % error.message
            raise exception.InvalidParameterValue(err=msg)

        # 如果force为True, 调用volume_api.create_snapshot_force
        # 如果force为False, 调用volume_api.create_snapshot
        if force:
            new_snapshot = self.volume_api.create_snapshot_force(
                context, volume, snapshot.get('display_name'),
                snapshot.get('description'), **kwargs)
        else:
            new_snapshot = self.volume_api.create_snapshot(
                context, volume, snapshot.get('display_name'),
                snapshot.get('description'), **kwargs)
        req.cache_db_snapshot(new_snapshot)

        return self._view_builder.detail(req, new_snapshot)