Exemplo n.º 1
0
def _verify_application_type_opts(opts):
    if opts['huawei_application_type'] and not opts['applicationname']:
        msg = _('WorkloadType name is None, please set '
                'huawei_application_type:applicationname in extra specs.')
        LOG.error(msg)
        raise exception.InvalidInput(reason=msg)
Exemplo n.º 2
0
    def _get_import_backup(self, context, backup_url):
        """Prepare database backup record for import.

        This method decodes provided backup_url and expects to find the id of
        the backup in there.

        Then checks the DB for the presence of this backup record and if it
        finds it and is not deleted it will raise an exception because the
        record cannot be created or used.

        If the record is in deleted status then we must be trying to recover
        this record, so we'll reuse it.

        If the record doesn't already exist we create it with provided id.

        :param context: running context
        :param backup_url: backup description to be used by the backup driver
        :return: BackupImport object
        :raises InvalidBackup:
        :raises InvalidInput:
        """
        # Deserialize string backup record into a dictionary
        backup_record = objects.Backup.decode_record(backup_url)

        # ID is a required field since it's what links incremental backups
        if 'id' not in backup_record:
            msg = _('Provided backup record is missing an id')
            raise exception.InvalidInput(reason=msg)

        kwargs = {
            'user_id': context.user_id,
            'project_id': context.project_id,
            'volume_id': IMPORT_VOLUME_ID,
            'status': fields.BackupStatus.CREATING,
            'deleted_at': None,
            'deleted': False,
            'metadata': {}
        }

        try:
            # Try to get the backup with that ID in all projects even among
            # deleted entries.
            backup = objects.BackupImport.get_by_id(
                context.elevated(read_deleted='yes'),
                backup_record['id'],
                project_only=False)

            # If record exists and it's not deleted we cannot proceed with the
            # import
            if backup.status != fields.BackupStatus.DELETED:
                msg = _('Backup already exists in database.')
                raise exception.InvalidBackup(reason=msg)

            # Otherwise we'll "revive" delete backup record
            backup.update(kwargs)
            backup.save()

        except exception.BackupNotFound:
            # If record doesn't exist create it with the specific ID
            backup = objects.BackupImport(context=context,
                                          id=backup_record['id'],
                                          **kwargs)
            backup.create()

        return backup
Exemplo n.º 3
0
 def _assert_connector_ok(self, connector):
     if not connector['wwpns']:
         msg = _LE("Connector doesn't provide wwpns")
         LOG.error(msg)
         raise exception.InvalidInput(reason=msg)
Exemplo n.º 4
0
 def check_netapp_driver(location):
     """Checks if the driver requested is a netapp driver."""
     if location.find(".netapp.") == -1:
             raise exception.InvalidInput(
                 reason=_("Only loading netapp drivers supported."))
Exemplo n.º 5
0
    def initialize_iscsi_ports(self, common):
        # map iscsi_ip-> ip_port
        #             -> iqn
        #             -> nsp
        iscsi_ip_list = {}
        temp_iscsi_ip = {}

        # use the 3PAR ip_addr list for iSCSI configuration
        if len(common._client_conf['hpe3par_iscsi_ips']) > 0:
            # add port values to ip_addr, if necessary
            for ip_addr in common._client_conf['hpe3par_iscsi_ips']:
                ip = ip_addr.split(':')
                if len(ip) == 1:
                    temp_iscsi_ip[ip_addr] = {'ip_port': DEFAULT_ISCSI_PORT}
                elif len(ip) == 2:
                    temp_iscsi_ip[ip[0]] = {'ip_port': ip[1]}
                else:
                    LOG.warning("Invalid IP address format '%s'", ip_addr)

        # add the single value iscsi_ip_address option to the IP dictionary.
        # This way we can see if it's a valid iSCSI IP. If it's not valid,
        # we won't use it and won't bother to report it, see below
        if (common._client_conf['iscsi_ip_address'] not in temp_iscsi_ip):
            ip = common._client_conf['iscsi_ip_address']
            ip_port = common._client_conf['iscsi_port']
            temp_iscsi_ip[ip] = {'ip_port': ip_port}

        # get all the valid iSCSI ports from 3PAR
        # when found, add the valid iSCSI ip, ip port, iqn and nsp
        # to the iSCSI IP dictionary
        iscsi_ports = common.get_active_iscsi_target_ports()

        for port in iscsi_ports:
            ip = port['IPAddr']
            if ip in temp_iscsi_ip:
                ip_port = temp_iscsi_ip[ip]['ip_port']
                iscsi_ip_list[ip] = {
                    'ip_port': ip_port,
                    'nsp': port['nsp'],
                    'iqn': port['iSCSIName']
                }
                del temp_iscsi_ip[ip]

        # if the single value iscsi_ip_address option is still in the
        # temp dictionary it's because it defaults to $my_ip which doesn't
        # make sense in this context. So, if present, remove it and move on.
        if common._client_conf['iscsi_ip_address'] in temp_iscsi_ip:
            del temp_iscsi_ip[common._client_conf['iscsi_ip_address']]

        # lets see if there are invalid iSCSI IPs left in the temp dict
        if len(temp_iscsi_ip) > 0:
            LOG.warning(
                "Found invalid iSCSI IP address(s) in "
                "configuration option(s) hpe3par_iscsi_ips or "
                "iscsi_ip_address '%s.'", (", ".join(temp_iscsi_ip)))

        if not len(iscsi_ip_list) > 0:
            msg = _('At least one valid iSCSI IP address must be set.')
            LOG.error(msg)
            raise exception.InvalidInput(reason=msg)
        self.iscsi_ips[common._client_conf['hpe3par_api_url']] = iscsi_ip_list
Exemplo n.º 6
0
    def update(self, req, id, body):
        """Enable/Disable scheduling for a service.

        Includes Freeze/Thaw which sends call down to drivers
        and allows volume.manager for the specified host to
        disable the service rather than accessing the service
        directly in this API layer.
        """
        context = req.environ['cinder.context']
        context.authorize(policy.UPDATE_POLICY)

        support_dynamic_log = req.api_version_request.matches(mv.LOG_LEVEL)
        ext_loaded = self.ext_mgr.is_loaded('os-extended-services')
        ret_val = {}
        if id == "enable":
            disabled, status = self._enable(req, body=body)
        elif id == "disable":
            disabled, status = self._disable(req, body=body)
        elif id == "disable-log-reason" and ext_loaded:
            disabled_reason, disabled, status = (self._disabled_log_reason(
                req, body=body))
            ret_val['disabled_reason'] = disabled_reason
        elif id == "freeze":
            return self._freeze(req, context, body=body)
        elif id == "thaw":
            return self._thaw(req, context, body=body)
        elif id == "failover_host":
            return self._failover(req, context, False, body=body)
        elif (req.api_version_request.matches(mv.REPLICATION_CLUSTER)
              and id == 'failover'):
            return self._failover(req, context, True, body=body)
        elif support_dynamic_log and id == 'set-log':
            return self._set_log(req, context, body=body)
        elif support_dynamic_log and id == 'get-log':
            return self._get_log(req, context, body=body)
        else:
            raise exception.InvalidInput(reason=_("Unknown action"))

        host = common.get_cluster_host(req, body, False)[1]
        ret_val['disabled'] = disabled

        # NOTE(uni): deprecating service request key, binary takes precedence
        # Still keeping service key here for API compatibility sake.
        service = body.get('service', '')
        binary = body.get('binary', '')
        binary_key = binary or service

        # Not found exception will be handled at the wsgi level
        svc = objects.Service.get_by_args(context, host, binary_key)

        svc.disabled = ret_val['disabled']
        if 'disabled_reason' in ret_val:
            svc.disabled_reason = ret_val['disabled_reason']
        svc.save()

        ret_val.update({
            'host': host,
            'service': service,
            'binary': binary,
            'status': status
        })
        return ret_val
Exemplo n.º 7
0
    def _extract_availability_zone(self, availability_zone, snapshot,
                                   source_volume, group):
        """Extracts and returns a validated availability zone.

        This function will extract the availability zone (if not provided) from
        the snapshot or source_volume and then performs a set of validation
        checks on the provided or extracted availability zone and then returns
        the validated availability zone.
        """

        refresh_az = False
        # If the volume will be created in a group, it should be placed in
        # in same availability zone as the group.
        if group:
            try:
                availability_zone = group['availability_zone']
            except (TypeError, KeyError):
                pass

        # Try to extract the availability zone from the corresponding snapshot
        # or source volume if either is valid so that we can be in the same
        # availability zone as the source.
        if availability_zone is None:
            if snapshot:
                try:
                    availability_zone = snapshot['volume']['availability_zone']
                except (TypeError, KeyError):
                    pass
            if source_volume and availability_zone is None:
                try:
                    availability_zone = source_volume['availability_zone']
                except (TypeError, KeyError):
                    pass

        if availability_zone is None:
            if CONF.default_availability_zone:
                availability_zone = CONF.default_availability_zone
            else:
                # For backwards compatibility use the storage_availability_zone
                availability_zone = CONF.storage_availability_zone

        if availability_zone not in self.availability_zones:
            refresh_az = True
            if CONF.allow_availability_zone_fallback:
                original_az = availability_zone
                availability_zone = (
                    CONF.default_availability_zone or
                    CONF.storage_availability_zone)
                LOG.warning("Availability zone '%(s_az)s' "
                            "not found, falling back to "
                            "'%(s_fallback_az)s'.",
                            {'s_az': original_az,
                             's_fallback_az': availability_zone})
            else:
                raise exception.InvalidAvailabilityZone(az=availability_zone)

        # If the configuration only allows cloning to the same availability
        # zone then we need to enforce that.
        if CONF.cloned_volume_same_az:
            snap_az = None
            try:
                snap_az = snapshot['volume']['availability_zone']
            except (TypeError, KeyError):
                pass
            if snap_az and snap_az != availability_zone:
                msg = _("Volume must be in the same "
                        "availability zone as the snapshot")
                raise exception.InvalidInput(reason=msg)
            source_vol_az = None
            try:
                source_vol_az = source_volume['availability_zone']
            except (TypeError, KeyError):
                pass
            if source_vol_az and source_vol_az != availability_zone:
                msg = _("Volume must be in the same "
                        "availability zone as the source volume")
                raise exception.InvalidInput(reason=msg)

        return availability_zone, refresh_az
Exemplo n.º 8
0
 def check_for_setup_error(self):
     if not self.configuration.iscsi_ip_address:
         raise exception.InvalidInput(
             reason=_('iscsi_ip_address is not set.'))
Exemplo n.º 9
0
 def assert_not_frozen(self):
     ctxt = self._context.elevated()
     if db.is_backend_frozen(ctxt, self.host, self.cluster_name):
         msg = _('Modification operations are not allowed on frozen '
                 'storage backends.')
         raise exception.InvalidInput(reason=msg)
Exemplo n.º 10
0
    def get_all(self,
                context,
                marker=None,
                limit=None,
                sort_key='created_at',
                sort_dir='desc',
                filters={}):
        check_policy(context, 'get_all')

        try:
            if limit is not None:
                limit = int(limit)
                if limit < 0:
                    msg = _('limit param must be positive')
                    raise exception.InvalidInput(reason=msg)
        except ValueError:
            msg = _('limit param must be an integer')
            raise exception.InvalidInput(reason=msg)

        if (context.is_admin and 'all_tenants' in filters):
            # Need to remove all_tenants to pass the filtering below.
            del filters['all_tenants']
            volumes = self.db.volume_get_all(context, marker, limit, sort_key,
                                             sort_dir)
        else:
            volumes = self.db.volume_get_all_by_project(
                context, context.project_id, marker, limit, sort_key, sort_dir)

        # Non-admin shouldn't see temporary target of a volume migration
        if not context.is_admin:
            filters['no_migration_targets'] = True

        if filters:
            LOG.debug(_("Searching by: %s") % str(filters))

            def _check_metadata_match(volume, searchdict):
                volume_metadata = {}
                for i in volume.get('volume_metadata'):
                    volume_metadata[i['key']] = i['value']

                for k, v in searchdict.iteritems():
                    if (k not in volume_metadata.keys()
                            or volume_metadata[k] != v):
                        return False
                return True

            def _check_migration_target(volume, searchdict):
                status = volume['migration_status']
                if status and status.startswith('target:'):
                    return False
                return True

            # search_option to filter_name mapping.
            filter_mapping = {
                'metadata': _check_metadata_match,
                'no_migration_targets': _check_migration_target
            }

            result = []
            not_found = object()
            for volume in volumes:
                # go over all filters in the list
                for opt, values in filters.iteritems():
                    try:
                        filter_func = filter_mapping[opt]
                    except KeyError:

                        def filter_func(volume, value):
                            return volume.get(opt, not_found) == value

                    if not filter_func(volume, values):
                        break  # volume doesn't match this filter
                else:  # did not break out loop
                    result.append(volume)  # volume matches all filters
            volumes = result

        return volumes
Exemplo n.º 11
0
    def retype(self, context, volume, new_type, migration_policy=None):
        """Attempt to modify the type associated with an existing volume."""
        if volume['status'] not in ['available', 'in-use']:
            msg = _('Unable to update type due to incorrect status '
                    'on volume: %s') % volume['id']
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        if volume['migration_status'] is not None:
            msg = (_("Volume %s is already part of an active migration.") %
                   volume['id'])
            LOG.error(msg)
            raise exception.InvalidVolume(reason=msg)

        if migration_policy and migration_policy not in ['on-demand', 'never']:
            msg = _('migration_policy must be \'on-demand\' or \'never\', '
                    'passed: %s') % str(new_type)
            LOG.error(msg)
            raise exception.InvalidInput(reason=msg)

        # Support specifying volume type by ID or name
        try:
            if uuidutils.is_uuid_like(new_type):
                vol_type = volume_types.get_volume_type(context, new_type)
            else:
                vol_type = volume_types.get_volume_type_by_name(
                    context, new_type)
        except exception.InvalidVolumeType:
            msg = _('Invalid volume_type passed: %s') % str(new_type)
            LOG.error(msg)
            raise exception.InvalidInput(reason=msg)

        vol_type_id = vol_type['id']
        vol_type_qos_id = vol_type['qos_specs_id']

        old_vol_type = None
        old_vol_type_id = volume['volume_type_id']
        old_vol_type_qos_id = None

        # Error if the original and new type are the same
        if volume['volume_type_id'] == vol_type_id:
            msg = _('New volume_type same as original: %s') % str(new_type)
            LOG.error(msg)
            raise exception.InvalidInput(reason=msg)

        if volume['volume_type_id']:
            old_vol_type = volume_types.get_volume_type(
                context, old_vol_type_id)
            old_vol_type_qos_id = old_vol_type['qos_specs_id']

        # We don't support changing encryption requirements yet
        old_enc = volume_types.get_volume_type_encryption(
            context, old_vol_type_id)
        new_enc = volume_types.get_volume_type_encryption(context, vol_type_id)
        if old_enc != new_enc:
            msg = _('Retype cannot change encryption requirements')
            raise exception.InvalidInput(reason=msg)

        # We don't support changing QoS at the front-end yet for in-use volumes
        # TODO(avishay): Call Nova to change QoS setting (libvirt has support
        # - virDomainSetBlockIoTune() - Nova does not have support yet).
        if (volume['status'] != 'available'
                and old_vol_type_qos_id != vol_type_qos_id):
            for qos_id in [old_vol_type_qos_id, vol_type_qos_id]:
                if qos_id:
                    specs = qos_specs.get_qos_specs(context.elevated(), qos_id)
                    if specs['qos_specs']['consumer'] != 'back-end':
                        msg = _('Retype cannot change front-end qos specs for '
                                'in-use volumes')
                        raise exception.InvalidInput(reason=msg)

        self.update(context, volume, {'status': 'retyping'})

        # We're checking here in so that we can report any quota issues as
        # early as possible, but won't commit until we change the type. We
        # pass the reservations onward in case we need to roll back.
        reservations = quota_utils.get_volume_type_reservation(
            context, volume, vol_type_id)
        request_spec = {
            'volume_properties': volume,
            'volume_id': volume['id'],
            'volume_type': vol_type,
            'migration_policy': migration_policy,
            'quota_reservations': reservations
        }

        self.scheduler_rpcapi.retype(context,
                                     CONF.volume_topic,
                                     volume['id'],
                                     request_spec=request_spec,
                                     filter_properties={})
Exemplo n.º 12
0
    def create(self,
               context,
               size,
               name,
               description,
               snapshot=None,
               image_id=None,
               volume_type=None,
               metadata=None,
               availability_zone=None,
               source_volume=None,
               scheduler_hints=None,
               backup_source_volume=None):

        if source_volume and volume_type:
            if volume_type['id'] != source_volume['volume_type_id']:
                msg = _("Invalid volume_type provided (requested type "
                        "must match source volume, or be omitted). "
                        "You should omit the argument.")
                raise exception.InvalidInput(reason=msg)

        if snapshot and volume_type:
            if volume_type['id'] != snapshot['volume_type_id']:
                msg = _("Invalid volume_type provided (requested type "
                        "must match source snapshot, or be omitted). "
                        "You should omit the argument.")
                raise exception.InvalidInput(reason=msg)

        def check_volume_az_zone(availability_zone):
            try:
                return self._valid_availability_zone(availability_zone)
            except exception.CinderException:
                LOG.exception(
                    _("Unable to query if %s is in the "
                      "availability zone set"), availability_zone)
                return False

        create_what = {
            'context': context,
            'raw_size': size,
            'name': name,
            'description': description,
            'snapshot': snapshot,
            'image_id': image_id,
            'raw_volume_type': volume_type,
            'metadata': metadata,
            'raw_availability_zone': availability_zone,
            'source_volume': source_volume,
            'scheduler_hints': scheduler_hints,
            'key_manager': self.key_manager,
            'backup_source_volume': backup_source_volume,
        }

        try:
            flow_engine = create_volume.get_flow(self.scheduler_rpcapi,
                                                 self.volume_rpcapi, self.db,
                                                 self.image_service,
                                                 check_volume_az_zone,
                                                 create_what)
        except Exception:
            LOG.exception(_("Failed to create api volume flow"))
            raise exception.CinderException(
                _("Failed to create api volume flow"))

        flow_engine.run()
        volume = flow_engine.storage.fetch('volume')
        return volume
Exemplo n.º 13
0
def _verify_smartpartition_opts(opts):
    if opts['smartpartition'] and not opts['partitionname']:
        msg = _('Partition name is not specified, please set '
                'smartpartition:partitionname in extra specs.')
        LOG.error(msg)
        raise exception.InvalidInput(reason=msg)
Exemplo n.º 14
0
def _verify_controller_opts(opts):
    if opts['huawei_controller'] and not opts['controllername']:
        msg = _('Controller name is None, please set '
                'huawei_controller:controllername in extra specs.')
        LOG.error(msg)
        raise exception.InvalidInput(reason=msg)
Exemplo n.º 15
0
 def _assert_connector_ok(self, connector, connector_element):
     if not connector[connector_element]:
         msg = _("Connector does not provide: %s") % connector_element
         LOG.error(msg)
         raise exception.InvalidInput(reason=msg)
Exemplo n.º 16
0
    def _get_import_backup(self, context, backup_url):
        """Prepare database backup record for import.

        This method decodes provided backup_url and expects to find the id of
        the backup in there.

        Then checks the DB for the presence of this backup record and if it
        finds it and is not deleted it will raise an exception because the
        record cannot be created or used.

        If the record is in deleted status then we must be trying to recover
        this record, so we'll reuse it.

        If the record doesn't already exist we create it with provided id.

        :param context: running context
        :param backup_url: backup description to be used by the backup driver
        :return: BackupImport object
        :raises InvalidBackup:
        :raises InvalidInput:
        """
        reservations = None
        backup = None
        # Deserialize string backup record into a dictionary
        backup_record = objects.Backup.decode_record(backup_url)

        # ID is a required field since it's what links incremental backups
        if 'id' not in backup_record:
            msg = _('Provided backup record is missing an id')
            raise exception.InvalidInput(reason=msg)

        # Since we use size to reserve&commit quota, size is another required
        # field.
        if 'size' not in backup_record:
            msg = _('Provided backup record is missing size attribute')
            raise exception.InvalidInput(reason=msg)

        try:
            reserve_opts = {
                'backups': 1,
                'backup_gigabytes': backup_record['size']
            }
            reservations = QUOTAS.reserve(context, **reserve_opts)
        except exception.OverQuota as e:
            quota_utils.process_reserve_over_quota(context,
                                                   e,
                                                   resource='backups',
                                                   size=backup_record['size'])

        kwargs = {
            'user_id': context.user_id,
            'project_id': context.project_id,
            'volume_id': IMPORT_VOLUME_ID,
            'status': fields.BackupStatus.CREATING,
            'deleted_at': None,
            'deleted': False,
            'metadata': {}
        }

        try:
            try:
                # Try to get the backup with that ID in all projects even among
                # deleted entries.
                backup = objects.BackupImport.get_by_id(
                    context.elevated(read_deleted='yes'),
                    backup_record['id'],
                    project_only=False)

                # If record exists and it's not deleted we cannot proceed
                # with the import
                if backup.status != fields.BackupStatus.DELETED:
                    msg = _('Backup already exists in database.')
                    raise exception.InvalidBackup(reason=msg)

                # Otherwise we'll "revive" delete backup record
                backup.update(kwargs)
                backup.save()
                QUOTAS.commit(context, reservations)
            except exception.BackupNotFound:
                # If record doesn't exist create it with the specific ID
                backup = objects.BackupImport(context=context,
                                              id=backup_record['id'],
                                              **kwargs)
                backup.create()
                QUOTAS.commit(context, reservations)
        except Exception:
            with excutils.save_and_reraise_exception():
                try:
                    if backup and 'id' in backup:
                        backup.destroy()
                finally:
                    QUOTAS.rollback(context, reservations)
        return backup
Exemplo n.º 17
0
 def _volume_api_proxy(self, fun, *args):
     try:
         return fun(*args)
     except exception.ServiceNotFound as ex:
         raise exception.InvalidInput(ex.msg)
Exemplo n.º 18
0
    def manage_existing(self, volume, existing_ref):
        """Manage an existing LeftHand volume.

        existing_ref is a dictionary of the form:
        {'source-name': <name of the virtual volume>}
        """
        # Check API Version
        self._check_api_version()

        target_vol_name = self._get_existing_volume_ref_name(existing_ref)

        # Check for the existence of the virtual volume.
        client = self._login()
        try:
            volume_info = client.getVolumeByName(target_vol_name)
        except hpexceptions.HTTPNotFound:
            err = (_("Virtual volume '%s' doesn't exist on array.") %
                   target_vol_name)
            LOG.error(err)
            raise exception.InvalidInput(reason=err)
        finally:
            self._logout(client)

        # Generate the new volume information based on the new ID.
        new_vol_name = 'volume-' + volume['id']

        volume_type = None
        if volume['volume_type_id']:
            try:
                volume_type = self._get_volume_type(volume['volume_type_id'])
            except Exception:
                reason = (_("Volume type ID '%s' is invalid.") %
                          volume['volume_type_id'])
                raise exception.ManageExistingVolumeTypeMismatch(reason=reason)

        new_vals = {"name": new_vol_name}

        client = self._login()
        try:
            # Update the existing volume with the new name.
            client.modifyVolume(volume_info['id'], new_vals)
        finally:
            self._logout(client)

        LOG.info(_LI("Virtual volume '%(ref)s' renamed to '%(new)s'."), {
            'ref': existing_ref['source-name'],
            'new': new_vol_name
        })

        display_name = None
        if volume['display_name']:
            display_name = volume['display_name']

        if volume_type:
            LOG.info(
                _LI("Virtual volume %(disp)s '%(new)s' is "
                    "being retyped."), {
                        'disp': display_name,
                        'new': new_vol_name
                    })

            try:
                self.retype(None, volume, volume_type,
                            volume_type['extra_specs'], volume['host'])
                LOG.info(
                    _LI("Virtual volume %(disp)s successfully retyped to "
                        "%(new_type)s."), {
                            'disp': display_name,
                            'new_type': volume_type.get('name')
                        })
            except Exception:
                with excutils.save_and_reraise_exception():
                    LOG.warning(
                        _LW("Failed to manage virtual volume %(disp)s "
                            "due to error during retype."),
                        {'disp': display_name})
                    # Try to undo the rename and clear the new comment.
                    client = self._login()
                    try:
                        client.modifyVolume(volume_info['id'],
                                            {'name': target_vol_name})
                    finally:
                        self._logout(client)

        updates = {'display_name': display_name}

        LOG.info(
            _LI("Virtual volume %(disp)s '%(new)s' is "
                "now being managed."), {
                    'disp': display_name,
                    'new': new_vol_name
                })

        # Return display name to update the name displayed in the GUI and
        # any model updates from retype.
        return updates
Exemplo n.º 19
0
 def validate_int(size):
     if not isinstance(size, six.integer_types) or size <= 0:
         msg = _("Volume size '%(size)s' must be an integer and"
                 " greater than 0") % {'size': size}
         raise exception.InvalidInput(reason=msg)
def paginate_query(query,
                   model,
                   limit,
                   sort_keys,
                   marker=None,
                   sort_dir=None,
                   sort_dirs=None):
    """Returns a query with sorting / pagination criteria added.

    Pagination works by requiring a unique sort_key, specified by sort_keys.
    (If sort_keys is not unique, then we risk looping through values.)
    We use the last row in the previous page as the 'marker' for pagination.
    So we must return values that follow the passed marker in the order.
    With a single-valued sort_key, this would be easy: sort_key > X.
    With a compound-values sort_key, (k1, k2, k3) we must do this to repeat
    the lexicographical ordering:
    (k1 > X1) or (k1 == X1 && k2 > X2) or (k1 == X1 && k2 == X2 && k3 > X3)

    We also have to cope with different sort_directions.

    Typically, the id of the last row is used as the client-facing pagination
    marker, then the actual marker object must be fetched from the db and
    passed in to us as marker.

    :param query: the query object to which we should add paging/sorting
    :param model: the ORM model class
    :param limit: maximum number of items to return
    :param sort_keys: array of attributes by which results should be sorted
    :param marker: the last item of the previous page; we returns the next
                    results after this value.
    :param sort_dir: direction in which results should be sorted (asc, desc)
    :param sort_dirs: per-column array of sort_dirs, corresponding to sort_keys

    :rtype: sqlalchemy.orm.query.Query
    :return: The query with sorting/pagination added.
    """

    if 'id' not in sort_keys:
        # TODO(justinsb): If this ever gives a false-positive, check
        # the actual primary key, rather than assuming its id
        LOG.warn(_('Id not in sort_keys; is sort_keys unique?'))

    assert (not (sort_dir and sort_dirs))

    # Default the sort direction to ascending
    if sort_dirs is None and sort_dir is None:
        sort_dir = 'asc'

    # Ensure a per-column sort direction
    if sort_dirs is None:
        sort_dirs = [sort_dir for _sort_key in sort_keys]

    assert (len(sort_dirs) == len(sort_keys))

    # Add sorting
    for current_sort_key, current_sort_dir in zip(sort_keys, sort_dirs):
        sort_dir_func = {
            'asc': sqlalchemy.asc,
            'desc': sqlalchemy.desc,
        }[current_sort_dir]

        try:
            sort_key_attr = getattr(model, current_sort_key)
        except AttributeError:
            raise exception.InvalidInput(reason='Invalid sort key')
        query = query.order_by(sort_dir_func(sort_key_attr))

    # Add pagination
    if marker is not None:
        marker_values = []
        for sort_key in sort_keys:
            v = getattr(marker, sort_key)
            marker_values.append(v)

        # Build up an array of sort criteria as in the docstring
        criteria_list = []
        for i in xrange(0, len(sort_keys)):
            crit_attrs = []
            for j in xrange(0, i):
                model_attr = getattr(model, sort_keys[j])
                crit_attrs.append((model_attr == marker_values[j]))

            model_attr = getattr(model, sort_keys[i])
            if sort_dirs[i] == 'desc':
                crit_attrs.append((model_attr < marker_values[i]))
            elif sort_dirs[i] == 'asc':
                crit_attrs.append((model_attr > marker_values[i]))
            else:
                raise ValueError(
                    _("Unknown sort direction, "
                      "must be 'desc' or 'asc'"))

            criteria = sqlalchemy.sql.and_(*crit_attrs)
            criteria_list.append(criteria)

        f = sqlalchemy.sql.or_(*criteria_list)
        query = query.filter(f)

    if limit is not None:
        query = query.limit(limit)

    return query
Exemplo n.º 21
0
 def check_for_setup_error(self):
     super(DellEQLSanISCSIDriver, self).check_for_setup_error()
     if self.configuration.eqlx_cli_max_retries < 0:
         raise exception.InvalidInput(
             reason=_("eqlx_cli_max_retries must be greater than or "
                      "equal to 0"))
 def _assert_text_result(self, text, mess):
     if not text:
         msg = _("%s is not configured.") % mess
         LOG.error(msg)
         raise exception.InvalidInput(reason=msg)
Exemplo n.º 23
0
    def create(self,
               context,
               size,
               name,
               description,
               snapshot=None,
               image_id=None,
               volume_type=None,
               metadata=None,
               availability_zone=None,
               source_volume=None,
               scheduler_hints=None,
               source_replica=None,
               consistencygroup=None,
               cgsnapshot=None,
               multiattach=False,
               source_cg=None):

        if not volume_type:
            volume_type = volume_types.get_default_volume_type()

        if self._is_lunr_volume_type(context, volume_type):
            # Lunr has size limits by volume type. Fail here instead of
            # getting an 'error' volume.
            self._validate_lunr_volume_type(volume_type, size)

            if not CONF.lunr_copy_image_enabled:
                image_id = None
            if not CONF.lunr_volume_clone_enabled:
                source_volume = None
            if snapshot:
                if self._is_lunr_volume_type(context,
                                             snapshot['volume_type_id']):
                    snapshot['volume_type_id'] = volume_type['id']
            if source_volume:
                # validate if source is in use
                if self._is_lunr_volume_type(context,
                                             source_volume['volume_type_id']):
                    source_volume['volume_type_id'] = volume_type['id']
            if metadata:
                if all(k in metadata
                       for k in ("different_node", "different_rack")):
                    msg = _("Cannot specify both different_node "
                            "and different_rack metadata keys")
                    raise exception.InvalidInput(reason=msg)

        kwargs = {}
        if snapshot is not None:
            kwargs['snapshot'] = snapshot
        if image_id is not None:
            kwargs['image_id'] = image_id
        if volume_type is not None:
            kwargs['volume_type'] = volume_type
        if metadata is not None:
            kwargs['metadata'] = metadata
        if availability_zone is not None:
            kwargs['availability_zone'] = availability_zone
        if source_volume is not None:
            kwargs['source_volume'] = source_volume
        if scheduler_hints is not None:
            kwargs['scheduler_hints'] = scheduler_hints
        if multiattach is not None:
            kwargs['multiattach'] = multiattach
        if source_replica is not None:
            kwargs['source_replica'] = source_replica
        if consistencygroup is not None:
            kwargs['consistencygroup'] = consistencygroup
        if cgsnapshot is not None:
            kwargs['cgsnapshot'] = cgsnapshot
        if source_cg is not None:
            kwargs['source_cg'] = source_cg

        if source_volume is not None:
            LOG.info("Finding on going operations on source volume %s." %
                     source_volume)
            siblings = self.db.snapshot_get_all_for_volume(
                context, source_volume["id"])
            in_progess_snapshots = [
                snapshot for snapshot in siblings
                if snapshot['status'] == 'creating'
            ]

            if in_progess_snapshots:
                raise SnapshotConflict(reason="Snapshot conflict",
                                       volume_id=source_volume["id"])
            self._check_clone_conflict(context, volume_id=source_volume["id"])
        return super(API, self).create(context, size, name, description,
                                       **kwargs)
Exemplo n.º 24
0
 def fake_backup_api_restore_throwing_InvalidInput(
         cls, context, backup_id, volume_id):
     msg = _("Invalid input")
     raise exception.InvalidInput(reason=msg)
Exemplo n.º 25
0
 def _check_config(self):
     """Ensure that the flags we care about are set."""
     required_config = ['san_ip', 'san_login', 'san_password']
     for attr in required_config:
         if not getattr(self.configuration, attr, None):
             raise exception.InvalidInput(reason=_('%s is not set.') % attr)
Exemplo n.º 26
0
    def create(self, req, body):
        """Creates a new volume.

        :param req: the request
        :param body: the request body
        :returns: dict -- the new volume dictionary
        :raises HTTPNotFound, HTTPBadRequest:
        """
        self.assert_valid_body(body, 'volume')

        LOG.debug('Create volume request body: %s', body)
        context = req.environ['cinder.context']

        req_version = req.api_version_request
        # Remove group_id from body if max version is less than GROUP_VOLUME.
        if req_version.matches(None, mv.get_prior_version(mv.GROUP_VOLUME)):
            # NOTE(xyang): The group_id is from a group created with a
            # group_type. So with this group_id, we've got a group_type
            # for this volume. Also if group_id is passed in, that means
            # we already know which backend is hosting the group and the
            # volume will be created on the same backend as well. So it
            # won't go through the scheduler again if a group_id is
            # passed in.
            try:
                body.get('volume', {}).pop('group_id', None)
            except AttributeError:
                msg = (_("Invalid body provided for creating volume. "
                         "Request API version: %s.") % req_version)
                raise exc.HTTPBadRequest(explanation=msg)

        volume = body['volume']
        kwargs = {}
        self.validate_name_and_description(volume)

        # Check up front for legacy replication parameters to quick fail
        source_replica = volume.get('source_replica')
        if source_replica:
            msg = _("Creating a volume from a replica source was part of the "
                    "replication v1 implementation which is no longer "
                    "available.")
            raise exception.InvalidInput(reason=msg)

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

        # NOTE(thingee): v2 API allows description instead of
        #                display_description
        if 'description' in volume:
            volume['display_description'] = volume.pop('description')

        if 'image_id' in volume:
            volume['imageRef'] = volume.pop('image_id')

        req_volume_type = volume.get('volume_type', None)
        if req_volume_type:
            # Not found exception will be handled at the wsgi level
            kwargs['volume_type'] = (objects.VolumeType.get_by_name_or_id(
                context, req_volume_type))

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

        snapshot_id = volume.get('snapshot_id')
        if snapshot_id is not None:
            if not uuidutils.is_uuid_like(snapshot_id):
                msg = _("Snapshot ID must be in UUID form.")
                raise exc.HTTPBadRequest(explanation=msg)
            # Not found exception will be handled at the wsgi level
            kwargs['snapshot'] = self.volume_api.get_snapshot(
                context, snapshot_id)
        else:
            kwargs['snapshot'] = None

        source_volid = volume.get('source_volid')
        if source_volid is not None:
            if not uuidutils.is_uuid_like(source_volid):
                msg = _("Source volume ID '%s' must be a "
                        "valid UUID.") % source_volid
                raise exc.HTTPBadRequest(explanation=msg)
            # Not found exception will be handled at the wsgi level
            kwargs['source_volume'] = (self.volume_api.get_volume(
                context, source_volid))
        else:
            kwargs['source_volume'] = None

        kwargs['group'] = None
        kwargs['consistencygroup'] = None
        consistencygroup_id = volume.get('consistencygroup_id')
        if consistencygroup_id is not None:
            if not uuidutils.is_uuid_like(consistencygroup_id):
                msg = _("Consistency group ID '%s' must be a "
                        "valid UUID.") % consistencygroup_id
                raise exc.HTTPBadRequest(explanation=msg)
            # Not found exception will be handled at the wsgi level
            kwargs['group'] = self.group_api.get(context, consistencygroup_id)

        # Get group_id if volume is in a group.
        group_id = volume.get('group_id')
        if group_id is not None:
            # Not found exception will be handled at the wsgi level
            kwargs['group'] = self.group_api.get(context, group_id)

        size = volume.get('size', None)
        if size is None and kwargs['snapshot'] is not None:
            size = kwargs['snapshot']['volume_size']
        elif size is None and kwargs['source_volume'] is not None:
            size = kwargs['source_volume']['size']

        LOG.info("Create volume of %s GB", size)

        if self.ext_mgr.is_loaded('os-image-create'):
            image_ref = volume.get('imageRef')
            if image_ref is not None:
                image_uuid = self._image_uuid_from_ref(image_ref, context)
                kwargs['image_id'] = image_uuid

        kwargs['availability_zone'] = volume.get('availability_zone', None)
        kwargs['scheduler_hints'] = volume.get('scheduler_hints', None)
        multiattach = volume.get('multiattach', False)
        kwargs['multiattach'] = multiattach

        new_volume = self.volume_api.create(context, size,
                                            volume.get('display_name'),
                                            volume.get('display_description'),
                                            **kwargs)

        retval = self._view_builder.detail(req, new_volume)

        return retval
Exemplo n.º 27
0
 def check_flags(self, options, required_flags):
     for flag in required_flags:
         if not getattr(options, flag, None):
             msg = _LE('%s configuration option is not set') % flag
             LOG.error(msg)
             raise exception.InvalidInput(reason=msg)
Exemplo n.º 28
0
    def create(self, context, size, name, description, snapshot=None,
                image_id=None, volume_type=None, metadata=None,
                availability_zone=None):
        check_policy(context, 'create')
        if snapshot is not None:
            if snapshot['status'] != "available":
                msg = _("status must be available")
                raise exception.InvalidSnapshot(reason=msg)
            if not size:
                size = snapshot['volume_size']

            snapshot_id = snapshot['id']
        else:
            snapshot_id = None

        def as_int(s):
            try:
                return int(s)
            except ValueError:
                return s

        # tolerate size as stringified int
        size = as_int(size)

        if not isinstance(size, int) or size <= 0:
            msg = (_("Volume size '%s' must be an integer and greater than 0")
                   % size)
            raise exception.InvalidInput(reason=msg)
        try:
            reservations = QUOTAS.reserve(context, volumes=1, gigabytes=size)
        except exception.OverQuota as e:
            overs = e.kwargs['overs']
            usages = e.kwargs['usages']
            quotas = e.kwargs['quotas']

            def _consumed(name):
                return (usages[name]['reserved'] + usages[name]['in_use'])

            pid = context.project_id
            if 'gigabytes' in overs:
                consumed = _consumed('gigabytes')
                quota = quotas['gigabytes']
                LOG.warn(_("Quota exceeded for %(pid)s, tried to create "
                           "%(size)sG volume (%(consumed)dG of %(quota)dG "
                           "already consumed)") % locals())
                raise exception.VolumeSizeExceedsAvailableQuota()
            elif 'volumes' in overs:
                consumed = _consumed('volumes')
                LOG.warn(_("Quota exceeded for %(pid)s, tried to create "
                           "volume (%(consumed)d volumes already consumed)")
                           % locals())
                raise exception.VolumeLimitExceeded(allowed=quotas['volumes'])

        if image_id:
            # check image existence
            image_meta = self.image_service.show(context, image_id)
            image_size_in_gb = (int(image_meta['size']) + GB - 1) / GB
            #check image size is not larger than volume size.
            if image_size_in_gb > size:
                msg = _('Size of specified image is larger than volume size.')
                raise exception.InvalidInput(reason=msg)

        if availability_zone is None:
            availability_zone = FLAGS.storage_availability_zone

        if volume_type is None:
            volume_type_id = None
        else:
            volume_type_id = volume_type.get('id', None)

        options = {
            'size': size,
            'user_id': context.user_id,
            'project_id': context.project_id,
            'snapshot_id': snapshot_id,
            'availability_zone': availability_zone,
            'status': "creating",
            'attach_status': "detached",
            'display_name': name,
            'display_description': description,
            'volume_type_id': volume_type_id,
            'metadata': metadata,
            }

        volume = self.db.volume_create(context, options)

        QUOTAS.commit(context, reservations)

        self._cast_create_volume(context, volume['id'], snapshot_id,
                                 image_id)
        return volume
Exemplo n.º 29
0
def check_flags(required_flags, configuration):
    """Ensure that the flags we care about are set."""
    for flag in required_flags:
        if not getattr(configuration, flag, None):
            msg = _('Configuration value %s is not set.') % flag
            raise exception.InvalidInput(reason=msg)
Exemplo n.º 30
0
def _verify_smartcache_opts(opts):
    if opts['smartcache'] and not opts['cachename']:
        msg = _('Cache name is not specified, please set '
                'smartcache:cachename in extra specs.')
        LOG.error(msg)
        raise exception.InvalidInput(reason=msg)