def share_server_migration_start(self, req, id, body): """Migrate a share server to the specified host.""" context = req.environ['manila.context'] try: share_server = db_api.share_server_get(context, id) except exception.ShareServerNotFound as e: raise exc.HTTPNotFound(explanation=e.msg) params = body.get('migration_start') if not params: raise exc.HTTPBadRequest(explanation=_("Request is missing body.")) bool_params = ['writable', 'nondisruptive', 'preserve_snapshots'] mandatory_params = bool_params + ['host'] utils.check_params_exist(mandatory_params, params) bool_param_values = utils.check_params_are_boolean(bool_params, params) pool_was_specified = len(params['host'].split('#')) > 1 if pool_was_specified: msg = _('The destination host can not contain pool information.') raise exc.HTTPBadRequest(explanation=msg) new_share_network = None new_share_network_id = params.get('new_share_network_id', None) if new_share_network_id: try: new_share_network = db_api.share_network_get( context, new_share_network_id) except exception.NotFound: msg = _("Share network %s not " "found.") % new_share_network_id raise exc.HTTPBadRequest(explanation=msg) common.check_share_network_is_active(new_share_network) else: share_network_id = ( share_server['share_network_subnet']['share_network_id']) current_share_network = db_api.share_network_get( context, share_network_id) common.check_share_network_is_active(current_share_network) try: self.share_api.share_server_migration_start( context, share_server, params['host'], bool_param_values['writable'], bool_param_values['nondisruptive'], bool_param_values['preserve_snapshots'], new_share_network=new_share_network) except exception.ServiceIsDown as e: # NOTE(dviroel): user should check if the host is healthy raise exc.HTTPBadRequest(explanation=e.msg) except exception.InvalidShareServer as e: # NOTE(dviroel): invalid share server meaning that some internal # resource have a invalid state. raise exc.HTTPConflict(explanation=e.msg)
def _promote(self, req, id, body): """Promote a replica to active state.""" context = req.environ['manila.context'] try: replica = db.share_replica_get(context, id) except exception.ShareReplicaNotFound: msg = _("No replica exists with ID %s.") raise exc.HTTPNotFound(explanation=msg % id) share_network_id = replica.get('share_network_id') if share_network_id: share_network = db.share_network_get(context, share_network_id) common.check_share_network_is_active(share_network) replica_state = replica.get('replica_state') if replica_state == constants.REPLICA_STATE_ACTIVE: return webob.Response(status_int=http_client.OK) try: replica = self.share_api.promote_share_replica(context, replica) except exception.ReplicationException as e: raise exc.HTTPBadRequest(explanation=six.text_type(e)) except exception.AdminRequired as e: raise exc.HTTPForbidden(explanation=six.text_type(e)) return self._view_builder.detail(req, replica)
def _allow_access(self, req, id, body, enable_ceph=False, allow_on_error_status=False, enable_ipv6=False, enable_metadata=False): """Add share access rule.""" context = req.environ['manila.context'] access_data = body.get('allow_access', body.get('os-allow_access')) if not enable_metadata: access_data.pop('metadata', None) share = self.share_api.get(context, id) share_network_id = share.get('share_network_id') if share_network_id: share_network = db.share_network_get(context, share_network_id) common.check_share_network_is_active(share_network) if (not allow_on_error_status and self._any_instance_has_errored_rules(share)): msg = _("Access rules cannot be added while the share or any of " "its replicas or migration copies has its " "access_rules_status set to %(instance_rules_status)s. " "Deny any rules in %(rule_state)s state and try " "again.") % { 'instance_rules_status': constants.SHARE_INSTANCE_RULES_ERROR, 'rule_state': constants.ACCESS_STATE_ERROR, } raise webob.exc.HTTPBadRequest(explanation=msg) access_type = access_data['access_type'] access_to = access_data['access_to'] common.validate_access(access_type=access_type, access_to=access_to, enable_ceph=enable_ceph, enable_ipv6=enable_ipv6) try: access = self.share_api.allow_access( context, share, access_type, access_to, access_data.get('access_level'), access_data.get('metadata')) except exception.ShareAccessExists as e: raise webob.exc.HTTPBadRequest(explanation=e.msg) except exception.InvalidMetadata as error: raise exc.HTTPBadRequest(explanation=error.msg) except exception.InvalidMetadataSize as error: raise exc.HTTPBadRequest(explanation=error.msg) return self._access_view_builder.view(req, access)
def _unmanage(self, req, id, body=None): context = req.environ['manila.context'] LOG.debug("Unmanage Share Server with id: %s", id) # force's default value is False # force will be True if body is {'unmanage': {'force': True}} force = (body.get('unmanage') or {}).get('force', False) or False try: share_server = db_api.share_server_get(context, id) except exception.ShareServerNotFound as e: raise exc.HTTPNotFound(explanation=e.msg) network_subnet_id = share_server.get('share_network_subnet_id', None) if network_subnet_id: subnet = db_api.share_network_subnet_get(context, network_subnet_id) share_network_id = subnet['share_network_id'] else: share_network_id = share_server.get('share_network_id') share_network = db_api.share_network_get(context, share_network_id) common.check_share_network_is_active(share_network) allowed_statuses = [ constants.STATUS_ERROR, constants.STATUS_ACTIVE, constants.STATUS_MANAGE_ERROR, constants.STATUS_UNMANAGE_ERROR ] if share_server['status'] not in allowed_statuses: data = { 'status': share_server['status'], 'allowed_statuses': ', '.join(allowed_statuses), } msg = _("Share server's actual status is %(status)s, allowed " "statuses for unmanaging are " "%(allowed_statuses)s.") % data raise exc.HTTPBadRequest(explanation=msg) try: self.share_api.unmanage_share_server(context, share_server, force=force) except (exception.ShareServerInUse, exception.PolicyNotAuthorized) as e: raise exc.HTTPBadRequest(explanation=e.msg) return webob.Response(status_int=http_client.ACCEPTED)
def _create(self, req, body): """Add a replica to an existing share.""" context = req.environ['manila.context'] if not self.is_valid_body(body, 'share_replica'): msg = _("Body does not contain 'share_replica' information.") raise exc.HTTPUnprocessableEntity(explanation=msg) share_id = body.get('share_replica').get('share_id') availability_zone = body.get('share_replica').get('availability_zone') if not share_id: msg = _("Must provide Share ID to add replica.") raise exc.HTTPBadRequest(explanation=msg) try: share_ref = db.share_get(context, share_id) except exception.NotFound: msg = _("No share exists with ID %s.") raise exc.HTTPNotFound(explanation=msg % share_id) share_network_id = share_ref.get('share_network_id', None) if share_network_id: share_network = db.share_network_get(context, share_network_id) common.check_share_network_is_active(share_network) try: new_replica = self.share_api.create_share_replica( context, share_ref, availability_zone=availability_zone, share_network_id=share_network_id) except exception.AvailabilityZoneNotFound as e: raise exc.HTTPBadRequest(explanation=six.text_type(e)) except exception.ReplicationException as e: raise exc.HTTPBadRequest(explanation=six.text_type(e)) except exception.ShareBusyException as e: raise exc.HTTPBadRequest(explanation=six.text_type(e)) return self._view_builder.detail(req, new_replica)
def _deny_access(self, req, id, body): """Remove share access rule.""" context = req.environ['manila.context'] access_id = body.get('deny_access', body.get('os-deny_access'))['access_id'] share = self.share_api.get(context, id) share_network_id = share.get('share_network_id', None) if share_network_id: share_network = db.share_network_get(context, share_network_id) common.check_share_network_is_active(share_network) try: access = self.share_api.access_get(context, access_id) if access.share_id != id: raise exception.NotFound() share = self.share_api.get(context, id) except exception.NotFound as error: raise webob.exc.HTTPNotFound(explanation=six.text_type(error)) self.share_api.deny_access(context, share, access) return webob.Response(status_int=http_client.ACCEPTED)
def migration_start(self, req, id, body): """Migrate a share to the specified host.""" context = req.environ['manila.context'] try: share = self.share_api.get(context, id) except exception.NotFound: msg = _("Share %s not found.") % id raise exc.HTTPNotFound(explanation=msg) params = body.get('migration_start') if not params: raise exc.HTTPBadRequest(explanation=_("Request is missing body.")) driver_assisted_params = [ 'preserve_metadata', 'writable', 'nondisruptive', 'preserve_snapshots' ] bool_params = (driver_assisted_params + ['force_host_assisted_migration']) mandatory_params = driver_assisted_params + ['host'] utils.check_params_exist(mandatory_params, params) bool_param_values = utils.check_params_are_boolean(bool_params, params) new_share_network = None new_share_type = None new_share_network_id = params.get('new_share_network_id', None) if new_share_network_id: try: new_share_network = db.share_network_get( context, new_share_network_id) except exception.NotFound: msg = _("Share network %s not " "found.") % new_share_network_id raise exc.HTTPBadRequest(explanation=msg) common.check_share_network_is_active(new_share_network) else: share_network_id = share.get('share_network_id', None) if share_network_id: current_share_network = db.share_network_get( context, share_network_id) common.check_share_network_is_active(current_share_network) new_share_type_id = params.get('new_share_type_id', None) if new_share_type_id: try: new_share_type = db.share_type_get(context, new_share_type_id) except exception.NotFound: msg = _("Share type %s not found.") % new_share_type_id raise exc.HTTPBadRequest(explanation=msg) try: return_code = self.share_api.migration_start( context, share, params['host'], bool_param_values['force_host_assisted_migration'], bool_param_values['preserve_metadata'], bool_param_values['writable'], bool_param_values['nondisruptive'], bool_param_values['preserve_snapshots'], new_share_network=new_share_network, new_share_type=new_share_type) except exception.Conflict as e: raise exc.HTTPConflict(explanation=e.msg) return webob.Response(status_int=return_code)
def _validate_manage_share_server_parameters(self, context, body): if not (body and self.is_valid_body(body, 'share_server')): msg = _("Share Server entity not found in request body") raise exc.HTTPUnprocessableEntity(explanation=msg) required_parameters = ('host', 'share_network_id', 'identifier') data = body['share_server'] for parameter in required_parameters: if parameter not in data: msg = _("Required parameter %s not found") % parameter raise exc.HTTPBadRequest(explanation=msg) if not data.get(parameter): msg = _("Required parameter %s is empty") % parameter raise exc.HTTPBadRequest(explanation=msg) identifier = data['identifier'] host, share_network_id = data['host'], data['share_network_id'] network_subnet_id = data.get('share_network_subnet_id') if network_subnet_id: try: network_subnet = db_api.share_network_subnet_get( context, network_subnet_id) except exception.ShareNetworkSubnetNotFound: msg = _("The share network subnet %s does not " "exist.") % network_subnet_id raise exc.HTTPBadRequest(explanation=msg) else: network_subnet = db_api.share_network_subnet_get_default_subnet( context, share_network_id) if network_subnet is None: msg = _("The share network %s does have a default subnet. Create " "one or use a specific subnet to manage this share server " "with API version >= 2.51.") % share_network_id raise exc.HTTPBadRequest(explanation=msg) common.check_share_network_is_active(network_subnet['share_network']) if share_utils.extract_host(host, 'pool'): msg = _("Host parameter should not contain pool.") raise exc.HTTPBadRequest(explanation=msg) try: utils.validate_service_host(context, share_utils.extract_host(host)) except exception.ServiceNotFound as e: raise exc.HTTPBadRequest(explanation=e.msg) except exception.PolicyNotAuthorized as e: raise exc.HTTPForbidden(explanation=e.msg) except exception.AdminRequired as e: raise exc.HTTPForbidden(explanation=e.msg) except exception.ServiceIsDown as e: raise exc.HTTPBadRequest(explanation=e.msg) try: share_network = db_api.share_network_get(context, share_network_id) except exception.ShareNetworkNotFound as e: raise exc.HTTPBadRequest(explanation=e.msg) driver_opts = data.get('driver_options') if driver_opts is not None and not isinstance(driver_opts, dict): msg = _("Driver options must be in dictionary format.") raise exc.HTTPBadRequest(explanation=msg) return identifier, host, share_network, driver_opts, network_subnet
def _create(self, req, body, check_create_share_from_snapshot_support=False, check_availability_zones_extra_spec=False): """Creates a new share.""" context = req.environ['manila.context'] if not self.is_valid_body(body, 'share'): raise exc.HTTPUnprocessableEntity() share = body['share'] share = common.validate_public_share_policy(context, share) # NOTE(rushiagr): Manila API allows 'name' instead of 'display_name'. if share.get('name'): share['display_name'] = share.get('name') del share['name'] # NOTE(rushiagr): Manila API allows 'description' instead of # 'display_description'. if share.get('description'): share['display_description'] = share.get('description') del share['description'] size = share['size'] share_proto = share['share_proto'].upper() msg = ("Create %(share_proto)s share of %(size)s GB" % { 'share_proto': share_proto, 'size': size }) LOG.info(msg, context=context) availability_zone_id = None availability_zone = share.get('availability_zone') if availability_zone: try: availability_zone_id = db.availability_zone_get( context, availability_zone).id except exception.AvailabilityZoneNotFound as e: raise exc.HTTPNotFound(explanation=six.text_type(e)) share_group_id = share.get('share_group_id') if share_group_id: try: share_group = db.share_group_get(context, share_group_id) except exception.ShareGroupNotFound as e: raise exc.HTTPNotFound(explanation=six.text_type(e)) sg_az_id = share_group['availability_zone_id'] if availability_zone and availability_zone_id != sg_az_id: msg = _("Share cannot have AZ ('%(s_az)s') different than " "share group's one (%(sg_az)s).") % { 's_az': availability_zone_id, 'sg_az': sg_az_id } raise exception.InvalidInput(msg) availability_zone_id = sg_az_id kwargs = { 'availability_zone': availability_zone_id, 'metadata': share.get('metadata'), 'is_public': share.get('is_public', False), 'share_group_id': share_group_id, } snapshot_id = share.get('snapshot_id') if snapshot_id: snapshot = self.share_api.get_snapshot(context, snapshot_id) else: snapshot = None kwargs['snapshot_id'] = snapshot_id share_network_id = share.get('share_network_id') parent_share_type = {} if snapshot: # Need to check that share_network_id from snapshot's # parents share equals to share_network_id from args. # If share_network_id is empty then update it with # share_network_id of parent share. parent_share = self.share_api.get(context, snapshot['share_id']) parent_share_net_id = parent_share.instance['share_network_id'] parent_share_type = share_types.get_share_type( context, parent_share.instance['share_type_id']) if share_network_id: if share_network_id != parent_share_net_id: msg = ("Share network ID should be the same as snapshot's" " parent share's or empty") raise exc.HTTPBadRequest(explanation=msg) elif parent_share_net_id: share_network_id = parent_share_net_id # Verify that share can be created from a snapshot if (check_create_share_from_snapshot_support and not parent_share['create_share_from_snapshot_support']): msg = (_("A new share may not be created from snapshot '%s', " "because the snapshot's parent share does not have " "that capability.") % snapshot_id) LOG.error(msg) raise exc.HTTPBadRequest(explanation=msg) if share_network_id: try: share_network = self.share_api.get_share_network( context, share_network_id) except exception.ShareNetworkNotFound as e: raise exc.HTTPNotFound(explanation=e.msg) common.check_share_network_is_active(share_network) if availability_zone_id: if not db.share_network_subnet_get_by_availability_zone_id( context, share_network_id, availability_zone_id=availability_zone_id): msg = _("A share network subnet was not found for the " "requested availability zone.") raise exc.HTTPBadRequest(explanation=msg) display_name = share.get('display_name') display_description = share.get('display_description') if 'share_type' in share and 'volume_type' in share: msg = 'Cannot specify both share_type and volume_type' raise exc.HTTPBadRequest(explanation=msg) req_share_type = share.get('share_type', share.get('volume_type')) share_type = None if req_share_type: try: if not uuidutils.is_uuid_like(req_share_type): share_type = share_types.get_share_type_by_name( context, req_share_type) else: share_type = share_types.get_share_type( context, req_share_type) except (exception.ShareTypeNotFound, exception.ShareTypeNotFoundByName): msg = _("Share type not found.") raise exc.HTTPNotFound(explanation=msg) elif not snapshot: def_share_type = share_types.get_default_share_type() if def_share_type: share_type = def_share_type # Only use in create share feature. Create share from snapshot # and create share with share group features not # need this check. if (not share_network_id and not snapshot and not share_group_id and share_type and share_type.get('extra_specs') and (strutils.bool_from_string( share_type.get('extra_specs').get( 'driver_handles_share_servers')))): msg = _('Share network must be set when the ' 'driver_handles_share_servers is true.') raise exc.HTTPBadRequest(explanation=msg) type_chosen = share_type or parent_share_type if type_chosen and check_availability_zones_extra_spec: type_azs = type_chosen.get('extra_specs', {}).get('availability_zones', '') type_azs = type_azs.split(',') if type_azs else [] kwargs['availability_zones'] = type_azs if (availability_zone and type_azs and availability_zone not in type_azs): msg = _("Share type %(type)s is not supported within the " "availability zone chosen %(az)s.") type_chosen = (req_share_type or "%s (from source snapshot)" % (parent_share_type.get('name') or parent_share_type.get('id'))) payload = {'type': type_chosen, 'az': availability_zone} raise exc.HTTPBadRequest(explanation=msg % payload) if share_type: kwargs['share_type'] = share_type if share_network_id: kwargs['share_network_id'] = share_network_id new_share = self.share_api.create(context, share_proto, size, display_name, display_description, **kwargs) return self._view_builder.detail(req, new_share)
def _check_if_share_share_network_is_active(self, context, snapshot): share_network_id = snapshot['share'].get('share_network_id') if share_network_id: share_network = db_api.share_network_get(context, share_network_id) common.check_share_network_is_active(share_network)
def create(self, context, name=None, description=None, share_type_ids=None, source_share_group_snapshot_id=None, share_network_id=None, share_group_type_id=None, availability_zone_id=None, availability_zone=None): """Create new share group.""" share_group_snapshot = None original_share_group = None # NOTE(gouthamr): share_server_id is inherited from the # parent share group if a share group snapshot is specified, # else, it will be set in the share manager. share_server_id = None if source_share_group_snapshot_id: share_group_snapshot = self.db.share_group_snapshot_get( context, source_share_group_snapshot_id) if share_group_snapshot['status'] != constants.STATUS_AVAILABLE: msg = (_("Share group snapshot status must be %s.") % constants.STATUS_AVAILABLE) raise exception.InvalidShareGroupSnapshot(reason=msg) original_share_group = self.db.share_group_get( context, share_group_snapshot['share_group_id']) share_type_ids = [ s['share_type_id'] for s in original_share_group['share_types'] ] share_network_id = original_share_group['share_network_id'] share_server_id = original_share_group['share_server_id'] availability_zone_id = original_share_group['availability_zone_id'] # Get share_type_objects share_type_objects = [] driver_handles_share_servers = None for share_type_id in (share_type_ids or []): try: share_type_object = share_types.get_share_type( context, share_type_id) except exception.ShareTypeNotFound: msg = _("Share type with id %s could not be found.") raise exception.InvalidInput(msg % share_type_id) share_type_objects.append(share_type_object) extra_specs = share_type_object.get('extra_specs') if extra_specs: share_type_handle_ss = strutils.bool_from_string( extra_specs.get( constants.ExtraSpecs.DRIVER_HANDLES_SHARE_SERVERS)) if driver_handles_share_servers is None: driver_handles_share_servers = share_type_handle_ss elif not driver_handles_share_servers == share_type_handle_ss: # NOTE(ameade): if the share types have conflicting values # for driver_handles_share_servers then raise bad request msg = _("The specified share_types cannot have " "conflicting values for the " "driver_handles_share_servers extra spec.") raise exception.InvalidInput(reason=msg) if (not share_type_handle_ss) and share_network_id: msg = _("When using a share types with the " "driver_handles_share_servers extra spec as " "False, a share_network_id must not be provided.") raise exception.InvalidInput(reason=msg) share_network = {} try: if share_network_id: share_network = self.db.share_network_get( context, share_network_id) except exception.ShareNetworkNotFound: msg = _("The specified share network does not exist.") raise exception.InvalidInput(reason=msg) if share_network: # Check if share network is active, otherwise raise a BadRequest api_common.check_share_network_is_active(share_network) if (driver_handles_share_servers and not (source_share_group_snapshot_id or share_network_id)): msg = _("When using a share type with the " "driver_handles_share_servers extra spec as " "True, a share_network_id must be provided.") raise exception.InvalidInput(reason=msg) try: share_group_type = self.db.share_group_type_get( context, share_group_type_id) except exception.ShareGroupTypeNotFound: msg = _("The specified share group type %s does not exist.") raise exception.InvalidInput(reason=msg % share_group_type_id) supported_share_types = set( [x['share_type_id'] for x in share_group_type['share_types']]) supported_share_type_objects = [ share_types.get_share_type(context, share_type_id) for share_type_id in supported_share_types ] if not set(share_type_ids or []) <= supported_share_types: msg = _("The specified share types must be a subset of the share " "types supported by the share group type.") raise exception.InvalidInput(reason=msg) # Grab share type AZs for scheduling share_types_of_new_group = (share_type_objects or supported_share_type_objects) stype_azs_of_new_group = [] stypes_unsupported_in_az = [] for stype in share_types_of_new_group: stype_azs = stype.get('extra_specs', {}).get('availability_zones', '') if stype_azs: stype_azs = stype_azs.split(',') stype_azs_of_new_group.extend(stype_azs) if availability_zone and availability_zone not in stype_azs: # If an AZ is requested, it must be supported by the AZs # configured in each of the share types requested stypes_unsupported_in_az.append( (stype['name'], stype['id'])) if stypes_unsupported_in_az: msg = _("Share group cannot be created since the following share " "types are not supported within the availability zone " "'%(az)s': (%(stypes)s)") payload = {'az': availability_zone, 'stypes': ''} for type_name, type_id in set(stypes_unsupported_in_az): if payload['stypes']: payload['stypes'] += ', ' type_name = '%s ' % (type_name or '') payload['stypes'] += type_name + '(ID: %s)' % type_id raise exception.InvalidInput(reason=msg % payload) try: reservations = QUOTAS.reserve(context, share_groups=1) 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']) if 'share_groups' in overs: msg = ("Quota exceeded for '%(s_uid)s' user in '%(s_pid)s' " "project. (%(d_consumed)d of " "%(d_quota)d already consumed).") LOG.warning( msg, { 's_pid': context.project_id, 's_uid': context.user_id, 'd_consumed': _consumed('share_groups'), 'd_quota': quotas['share_groups'], }) raise exception.ShareGroupsLimitExceeded() options = { 'share_group_type_id': share_group_type_id, 'source_share_group_snapshot_id': source_share_group_snapshot_id, 'share_network_id': share_network_id, 'share_server_id': share_server_id, 'availability_zone_id': availability_zone_id, 'name': name, 'description': description, 'user_id': context.user_id, 'project_id': context.project_id, 'status': constants.STATUS_CREATING, 'share_types': share_type_ids or supported_share_types } if original_share_group: options['host'] = original_share_group['host'] share_group = {} try: share_group = self.db.share_group_create(context, options) if share_group_snapshot: members = self.db.share_group_snapshot_members_get_all( context, source_share_group_snapshot_id) for member in members: share_instance = self.db.share_instance_get( context, member['share_instance_id']) share_type = share_types.get_share_type( context, share_instance['share_type_id']) self.share_api.create( context, member['share_proto'], member['size'], None, None, share_group_id=share_group['id'], share_group_snapshot_member=member, share_type=share_type, availability_zone=availability_zone_id, share_network_id=share_network_id) except Exception: with excutils.save_and_reraise_exception(): if share_group: self.db.share_group_destroy(context.elevated(), share_group['id']) QUOTAS.rollback(context, reservations) try: QUOTAS.commit(context, reservations) except Exception: with excutils.save_and_reraise_exception(): QUOTAS.rollback(context, reservations) request_spec = {'share_group_id': share_group['id']} request_spec.update(options) request_spec['availability_zones'] = set(stype_azs_of_new_group) request_spec['share_types'] = share_type_objects request_spec['resource_type'] = share_group_type if share_group_snapshot and original_share_group: self.share_rpcapi.create_share_group(context, share_group, original_share_group['host']) else: self.scheduler_rpcapi.create_share_group( context, share_group_id=share_group['id'], request_spec=request_spec, filter_properties={}) return share_group