def check_string_length(value, name, min_length=0, max_length=None): """Check the length of specified string. :param value: the value of the string :param name: the name of the string :param min_length: the min_length of the string :param max_length: the max_length of the string """ if not isinstance(value, six.string_types): msg = _("%s is not a string or unicode") % name raise exception.InvalidInput(message=msg) if len(value) < min_length: msg = _("%(name)s has a minimum character requirement of " "%(min_length)s.") % { 'name': name, 'min_length': min_length } raise exception.InvalidInput(message=msg) if max_length and len(value) > max_length: msg = _("%(name)s has more than %(max_length)s " "characters.") % { 'name': name, 'max_length': max_length } raise exception.InvalidInput(message=msg)
def create_trigger(self, context, trigger): if trigger.type not in ['time']: msg = (_("Invalid trigger type:%s") % trigger.type) raise exception.InvalidInput(msg) if trigger.properties['format'] not in ['crontab']: msg = (_("Invalid trigger time format type")) raise exception.InvalidInput(msg)
def check_time_format(cls, pattern): if not pattern: msg = (_("The trigger pattern is None")) raise exception.InvalidInput(msg) try: croniter(pattern) except Exception: msg = (_("The trigger pattern(%s) is invalid") % pattern) raise exception.InvalidInput(msg)
def create(self, req, body): """Creates a new restore.""" if not self.is_valid_body(body, 'restore'): raise exc.HTTPUnprocessableEntity() LOG.debug('Create restore request body: %s', body) context = req.environ['smaug.context'] check_policy(context, 'create') restore = body['restore'] LOG.debug('Create restore request : %s', restore) if not restore.get("provider_id"): msg = _("provider_id must be provided when creating " "a restore.") raise exception.InvalidInput(reason=msg) if not restore.get("checkpoint_id"): msg = _("checkpoint_id must be provided when creating " "a restore.") raise exception.InvalidInput(reason=msg) parameters = restore.get("parameters") if not isinstance(parameters, dict): msg = _("parameters must be a dict when creating" " a restore.") raise exception.InvalidInput(reason=msg) restore_properties = { 'project_id': context.project_id, 'provider_id': restore.get('provider_id'), 'checkpoint_id': restore.get('checkpoint_id'), 'restore_target': restore.get('restore_target'), 'parameters': parameters, 'status': 'started', } restoreobj = objects.Restore(context=context, **restore_properties) restoreobj.create() LOG.debug('call restore RPC : restoreobj:%s', restoreobj) # call restore rpc API of protection service result = self.protection_api.restore(context, restoreobj) if result is True: status_update = "success" else: status_update = "failed" # update the status of restore update_dict = {"status": status_update} check_policy(context, 'update', restoreobj) self._restore_update(context, restoreobj.get("id"), update_dict) restoreobj.update(update_dict) retval = self._view_builder.detail(req, restoreobj) return retval
def checkpoints_create(self, req, provider_id, body): """Creates a new checkpoint.""" if not self.is_valid_body(body, 'checkpoint'): raise exc.HTTPUnprocessableEntity() context = req.environ['smaug.context'] LOG.debug('Create checkpoint request ' 'body: %s provider_id:%s', body, provider_id) check_policy(context, 'checkpoint_create') checkpoint = body['checkpoint'] LOG.debug('Create checkpoint request checkpoint: %s', checkpoint) if not provider_id: msg = _("provider_id must be provided when creating " "a checkpoint.") raise exception.InvalidInput(reason=msg) plan_id = checkpoint.get("plan_id") if not plan_id: msg = _("plan_id must be provided when creating " "a checkpoint.") raise exception.InvalidInput(reason=msg) if not uuidutils.is_uuid_like(plan_id): msg = _("Invalid plan id provided.") raise exc.HTTPBadRequest(explanation=msg) plan = objects.Plan.get_by_id(context, plan_id) if not plan: raise exception.PlanNotFound(plan_id=plan_id) checkpoint_properties = { 'project_id': context.project_id, 'status': 'protecting', 'provider_id': provider_id, "protection_plan": { "id": plan.get("id"), "name": plan.get("name"), "resources": plan.get("resources"), } } checkpoint = self.protection_api.protect(context, plan) if checkpoint is not None: checkpoint_properties['id'] = checkpoint.get('checkpoint_id') else: msg = _("Get checkpoint failed.") raise exc.HTTPNotFound(explanation=msg) returnval = self._checkpoint_view_builder.detail( req, checkpoint_properties) return returnval
def _get_all(self, context, marker=None, limit=None, sort_keys=None, sort_dirs=None, filters=None, offset=None): check_policy(context, 'get_all') if filters is None: filters = {} all_tenants = utils.get_bool_param('all_tenants', filters) 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 filters: LOG.debug("Searching by: %s.", six.text_type(filters)) if context.is_admin and all_tenants: # Need to remove all_tenants to pass the filtering below. del filters['all_tenants'] restores = objects.RestoreList.get_all(context, marker, limit, sort_keys=sort_keys, sort_dirs=sort_dirs, filters=filters, offset=offset) else: restores = objects.RestoreList.get_all_by_project( context, context.project_id, marker, limit, sort_keys=sort_keys, sort_dirs=sort_dirs, filters=filters, offset=offset) LOG.info(_LI("Get all restores completed successfully.")) return restores
def validate_plan_resources(self, plan): resources_list = plan["resources"] if (isinstance(resources_list, list)) and (len(resources_list) > 0): for resource in resources_list: if (isinstance(resource, dict) and (len(resource) == 3) and {"id", "type", 'name'}.issubset(resource)): pass else: msg = _("Resource in list must be a dict when creating a " "plan.The keys of resource are id,type and name.") raise exception.InvalidInput(reason=msg) else: msg = _("list resources must be provided when creating " "a plan.") raise exception.InvalidInput(reason=msg)
def unpack_graph(packed_graph): """Return a list of GraphNodes from a PackedGraph Unpacks a PackedGraph, which must have the property: each parent node in the adjacency list appears after its children. """ (nodes, adjacency_list) = packed_graph nodes_dict = dict(nodes) graph_nodes_dict = {} for (parent_sid, children_sids) in adjacency_list: if parent_sid in graph_nodes_dict: raise exception.InvalidInput(reason="PackedGraph adjacency list " "must be topologically ordered") children = [] for child_sid in children_sids: if child_sid not in graph_nodes_dict: graph_nodes_dict[child_sid] = GraphNode( nodes_dict[child_sid], ()) children.append(graph_nodes_dict[child_sid]) del (nodes_dict[child_sid]) graph_nodes_dict[parent_sid] = GraphNode(nodes_dict[parent_sid], tuple(children)) result_nodes = [] for sid in nodes_dict: if sid not in graph_nodes_dict: graph_nodes_dict[sid] = GraphNode(nodes_dict[sid], ()) result_nodes.append(graph_nodes_dict[sid]) return result_nodes
def instances_show(self, req, protectable_type, protectable_id): """Return a instance about the given protectable_type and id.""" context = req.environ['smaug.context'] LOG.info(_LI("Show the instance of a given protectable" " type: %s"), protectable_type) protectable_types = self._get_all(context) if protectable_type not in protectable_types: msg = _("Invalid protectable type provided.") raise exception.InvalidInput(reason=msg) instance = self.protection_api.\ show_protectable_instance(context, protectable_type, protectable_id) if instance is None: raise exception.InvalidProtectableInstance( protectable_id=instance.get('id')) dependents = self.protection_api.\ list_protectable_dependents(context, protectable_id, protectable_type) instance["dependent_resources"] = dependents retval_instance = self._view_builder.detail(req, instance) return retval_instance
def show(self, req, id): """Return data about the given protectable_type.""" context = req.environ['smaug.context'] protectable_type = id LOG.info( _LI("Show the information of a given" " protectable type: %s"), protectable_type) protectable_types = self._get_all(context) if protectable_type not in protectable_types: msg = _("Invalid protectable type provided.") raise exception.InvalidInput(reason=msg) check_policy(context, 'get') try: retval_protectable_type = self.protection_api.\ show_protectable_type(context, protectable_type) except exception.ProtectableTypeNotFound as error: raise exc.HTTPNotFound(explanation=error.msg) LOG.info( _LI("Show the protectable type information" " issued successfully.")) return self._view_builder.show(req, retval_protectable_type)
def _get_trigger_class(self, trigger_type): cls = self._trigger_cls_map.get(trigger_type, None) if not cls: msg = (_("Invalid trigger type:%s") % trigger_type) raise exception.InvalidInput(msg) return cls
def __init__(self, name, loader=None): """Initialize, but do not start the WSGI server. :param name: The name of the WSGI server given to the loader. :param loader: Loads the WSGI application using the given name. :returns: None """ self.name = name self.manager = self._get_manager() self.loader = loader or wsgi_common.Loader() self.app = self.loader.load_app(name) self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0") self.port = getattr(CONF, '%s_listen_port' % name, 0) self.workers = (getattr(CONF, '%s_workers' % name, None) or processutils.get_worker_count()) if self.workers and self.workers < 1: worker_name = '%s_workers' % name msg = (_("%(worker_name)s value of %(workers)d is invalid, " "must be greater than 0.") % {'worker_name': worker_name, 'workers': self.workers}) raise exception.InvalidInput(msg) self.server = wsgi.Server(name, self.app, host=self.host, port=self.port)
def check_trigger_definition(cls, trigger_definition): """Check trigger definition All the time instances of trigger_definition are in UTC, including start_time, end_time """ trigger_format = trigger_definition.get("format", None) pattern = trigger_definition.get("pattern", None) cls.TIME_FORMAT_MANAGER.check_time_format(trigger_format, pattern) interval = int( cls.TIME_FORMAT_MANAGER.get_interval(trigger_format, pattern)) if interval < CONF.min_interval: msg = (_("The interval of two adjacent time points " "is less than %d") % CONF.min_interval) raise exception.InvalidInput(msg) window = trigger_definition.get("window", CONF.window_time) if not isinstance(window, int): try: window = int(window) except Exception: msg = (_("The trigger windows(%s) is not integer") % window) raise exception.InvalidInput(msg) if window <= 0: msg = (_("The trigger windows(%d) must be positive") % window) raise exception.InvalidInput(msg) if (window * 2) > interval: msg = (_("The trigger windows%(window)d must be less " "than %(interval)d") % { "window": window, "interval": interval / 2 }) raise exception.InvalidInput(msg) end_time = trigger_definition.get("end_time", None) end_time = cls._check_and_get_datetime(end_time, "end_time") start_time = trigger_definition.get("start_time", None) start_time = cls._check_and_get_datetime(start_time, "start_time") valid_trigger_property = trigger_definition.copy() valid_trigger_property['start_time'] = start_time valid_trigger_property['end_time'] = end_time return valid_trigger_property
def add_trigger(self, trigger_id, trigger_type, trigger_property): if trigger_id in self._trigger_obj_map: msg = (_("Trigger id %s is exist") % trigger_id) raise exception.InvalidInput(msg) trigger_cls = self._get_trigger_class(trigger_type) trigger = trigger_cls(trigger_id, trigger_property, self._executor) self._trigger_obj_map[trigger_id] = trigger
def _check_and_get_datetime(cls, time, time_name): if not time or isinstance(time, datetime): return time if not isinstance(time, six.string_types): msg = (_("The trigger %(name)s(type = %(vtype)s) is not an " "instance of string") % { "name": time_name, "vtype": type(time) }) raise exception.InvalidInput(msg) try: time = timeutils.parse_strtime(time, fmt='%Y-%m-%d %H:%M:%S') except Exception: msg = (_("The format of trigger %s is not correct") % time_name) raise exception.InvalidInput(msg) return time
def create(self, req, body): """Creates a new plan.""" if not self.is_valid_body(body, 'plan'): raise exc.HTTPUnprocessableEntity() LOG.debug('Create plan request body: %s', body) context = req.environ['smaug.context'] check_policy(context, 'create') plan = body['plan'] LOG.debug('Create plan request plan: %s', plan) if not plan.get("provider_id"): msg = _("provider_id must be provided when creating " "a plan.") raise exception.InvalidInput(reason=msg) if not plan.get("parameters"): msg = _("parameters must be provided when creating " "a plan.") raise exception.InvalidInput(reason=msg) parameters = plan.get("parameters") if not isinstance(parameters, dict): msg = _("parameters must be a dict when creating a plan.") raise exception.InvalidInput(reason=msg) self.validate_name_and_description(plan) self.validate_plan_resources(plan) plan_properties = { 'name': plan.get('name', None), 'provider_id': plan.get('provider_id', None), 'project_id': context.project_id, 'status': 'suspended', 'resources': plan.get('resources', None), 'parameters': parameters, } plan = objects.Plan(context=context, **plan_properties) plan.create() retval = self._view_builder.detail(req, plan) return retval
def register_operation(self, operation_id, **kwargs): if operation_id in self._operation_ids: msg = (_("The operation_id(%s) is exist") % operation_id) raise exception.InvalidInput(msg) if self._greenthread and not self._greenthread.running: raise exception.TriggerIsInvalid(trigger_id=self._id) self._operation_ids.add(operation_id) if self._greenthread is None: self._start_greenthread()
def _get_all(self, context, marker=None, limit=None, sort_keys=None, sort_dirs=None, filters=None, offset=None): check_policy(context, 'get_all') if filters is None: filters = {} 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 filters: LOG.debug("Searching by: %s.", six.text_type(filters)) if context.is_admin: providers = self.protection_api.list_providers(context, marker, limit, sort_keys=sort_keys, sort_dirs=sort_dirs, filters=filters, offset=offset) else: msg = _('user must be an administrator') raise exception.InvalidInput(reason=msg) LOG.info(_LI("Get all providers completed successfully.")) return providers
def _restore_update(self, context, restore_id, fields): try: restore = self._restore_get(context, restore_id) except exception.RestoreNotFound as error: raise exc.HTTPNotFound(explanation=error.msg) if isinstance(restore, objects_base.SmaugObject): restore.update(fields) restore.save() LOG.info(_LI("restore updated successfully.")) else: msg = _("The parameter restore must be a object of " "SmaugObject class.") raise exception.InvalidInput(reason=msg)
def _plan_update(self, context, plan, fields): if plan['status'] != 'suspended': LOG.info( _LI("Unable to update plan, " "because it is in %s state."), plan['status']) msg = _("The plan can be only updated in suspended status.") raise exception.InvalidPlan(reason=msg) # TODO(chenying) replication scene: need call rpc API when # the status of the plan is changed. if isinstance(plan, objects_base.SmaugObject): plan.update(fields) plan.save() LOG.info(_LI("Plan updated successfully."), resource=plan) else: msg = _("The parameter plan must be a object of " "SmaugObject class.") raise exception.InvalidInput(reason=msg)
def instances_index(self, req, protectable_type): """Return data about the given protectable_type.""" context = req.environ['smaug.context'] LOG.info(_LI("Show the instances of a given" " protectable type: %s"), protectable_type) params = req.params.copy() marker, limit, offset = common.get_pagination_params(params) sort_keys, sort_dirs = common.get_sort_params(params) filters = params utils.remove_invalid_filter_options( context, filters, self._get_instance_filter_options()) protectable_types = self._get_all(context) if protectable_type not in protectable_types: msg = _("Invalid protectable type provided.") raise exception.InvalidInput(reason=msg) utils.check_filters(filters) instances = self._instances_get_all(context, protectable_type, marker, limit, sort_keys=sort_keys, sort_dirs=sort_dirs, filters=filters, offset=offset) for instance in instances: protectable_id = instance.get("id") instance["type"] = protectable_type if protectable_id is None: raise exception.InvalidProtectableInstance( protectable_id=protectable_id) dependents = self.protection_api.\ list_protectable_dependents(context, protectable_id, protectable_type) instance["dependent_resources"] = dependents retval_instances = self._view_builder.detail_list(req, instances) return retval_instances
def update_trigger_property(self, trigger_property): valid_trigger_property = self.check_trigger_definition( trigger_property) if valid_trigger_property == self._trigger_property: return start_time = valid_trigger_property["start_time"] if self._next_run_time is None and start_time is None: msg = (_("The start_time should not be None")) raise exception.InvalidInput(msg) if start_time: self._next_run_time = start_time self._window = int(valid_trigger_property.get('window', self._window)) self._trigger_property.update(valid_trigger_property) if len(self._operation_ids) > 0: # Restart greenthread to take the change of trigger property # effect immediately self._restart_greenthread()
def restore(self, context, restore=None): LOG.info(_LI("Starting restore service:restore action")) checkpoint_id = restore["checkpoint_id"] provider_id = restore["provider_id"] provider = self.provider_registry.show_provider(provider_id) try: checkpoint_collection = provider.get_checkpoint_collection() checkpoint = checkpoint_collection.get(checkpoint_id) except Exception: LOG.error(_LE("get checkpoint failed, checkpoint_id:%s"), checkpoint_id) raise exception.InvalidInput( reason="Invalid checkpoint_id or provider_id") if checkpoint.status in [ constants.CHECKPOINT_STATUS_ERROR, constants.CHECKPOINT_STATUS_PROTECTING ]: raise exception.CheckpointNotAvailable(checkpoint_id=checkpoint_id) try: restoration_flow = self.worker.get_restoration_flow( context, constants.OPERATION_RESTORE, checkpoint, provider, restore) except Exception: LOG.exception( _LE("Failed to create restoration flow, checkpoint:%s"), checkpoint_id) raise exception.SmaugException( _("Failed to create restoration flow")) try: self.worker.run_flow(restoration_flow) return True except Exception: LOG.exception(_LE("Failed to run restoration flow")) raise
def __init__(self, name, app, host=None, port=None, pool_size=None, protocol=eventlet.wsgi.HttpProtocol, backlog=128): """Initialize, but do not start, a WSGI server. :param name: Pretty name for logging. :param app: The WSGI application to serve. :param host: IP address to serve the application. :param port: Port number to server the application. :param pool_size: Maximum number of eventlets to spawn concurrently. :returns: None """ # Allow operators to customize http requests max header line size. eventlet.wsgi.MAX_HEADER_LINE = CONF.max_header_line self.client_socket_timeout = CONF.client_socket_timeout or None self.name = name self.app = app self._host = host or "0.0.0.0" self._port = port or 0 self._server = None self._socket = None self._protocol = protocol self.pool_size = pool_size or self.default_pool_size self._pool = eventlet.GreenPool(self.pool_size) self._logger = logging.getLogger("eventlet.wsgi.server") if backlog < 1: raise exception.InvalidInput( reason='The backlog must be more than 1') bind_addr = (host, port) # TODO(dims): eventlet's green dns/socket module does not actually # support IPv6 in getaddrinfo(). We need to get around this in the # future or monitor upstream for a fix try: info = socket.getaddrinfo(bind_addr[0], bind_addr[1], socket.AF_UNSPEC, socket.SOCK_STREAM)[0] family = info[0] bind_addr = info[-1] except Exception: family = socket.AF_INET cert_file = CONF.ssl_cert_file key_file = CONF.ssl_key_file ca_file = CONF.ssl_ca_file self._use_ssl = cert_file or key_file if cert_file and not os.path.exists(cert_file): raise RuntimeError(_("Unable to find cert_file : %s") % cert_file) if ca_file and not os.path.exists(ca_file): raise RuntimeError(_("Unable to find ca_file : %s") % ca_file) if key_file and not os.path.exists(key_file): raise RuntimeError(_("Unable to find key_file : %s") % key_file) if self._use_ssl and (not cert_file or not key_file): raise RuntimeError( _("When running server in SSL mode, you " "must specify both a cert_file and " "key_file option value in your " "configuration file.")) retry_until = time.time() + 30 while not self._socket and time.time() < retry_until: try: self._socket = eventlet.listen(bind_addr, backlog=backlog, family=family) except socket.error as err: if err.args[0] != errno.EADDRINUSE: raise eventlet.sleep(0.1) if not self._socket: raise RuntimeError( _("Could not bind to %(host)s:%(port)s " "after trying for 30 seconds") % { 'host': host, 'port': port }) (self._host, self._port) = self._socket.getsockname()[0:2] LOG.info(_LI("%(name)s listening on %(_host)s:%(_port)s"), { 'name': self.name, '_host': self._host, '_port': self._port })
def _get_timeformat_cls(self, format_type): if format_type not in self._timeformat_cls_map: msg = (_("Invalid trigger time format type:%s") % format_type) raise exception.InvalidInput(msg) return self._timeformat_cls_map[format_type]
def _get_operation_cls(self, operation_type): if operation_type not in self._operation_cls_map: msg = (_("Invalid operation type:%s") % operation_type) raise exception.InvalidInput(msg) return self._operation_cls_map[operation_type]
def process_sort_params(sort_keys, sort_dirs, default_keys=None, default_dir='asc'): """Process the sort parameters to include default keys. Creates a list of sort keys and a list of sort directions. Adds the default keys to the end of the list if they are not already included. When adding the default keys to the sort keys list, the associated direction is: 1) The first element in the 'sort_dirs' list (if specified), else 2) 'default_dir' value (Note that 'asc' is the default value since this is the default in sqlalchemy.utils.paginate_query) :param sort_keys: List of sort keys to include in the processed list :param sort_dirs: List of sort directions to include in the processed list :param default_keys: List of sort keys that need to be included in the processed list, they are added at the end of the list if not already specified. :param default_dir: Sort direction associated with each of the default keys that are not supplied, used when they are added to the processed list :returns: list of sort keys, list of sort directions :raise exception.InvalidInput: If more sort directions than sort keys are specified or if an invalid sort direction is specified """ if default_keys is None: default_keys = ['created_at', 'id'] # Determine direction to use for when adding default keys if sort_dirs and len(sort_dirs): default_dir_value = sort_dirs[0] else: default_dir_value = default_dir # Create list of keys (do not modify the input list) if sort_keys: result_keys = list(sort_keys) else: result_keys = [] # If a list of directions is not provided, use the default sort direction # for all provided keys. if sort_dirs: result_dirs = [] # Verify sort direction for sort_dir in sort_dirs: if sort_dir not in ('asc', 'desc'): msg = _("Unknown sort direction, must be 'desc' or 'asc'.") raise exception.InvalidInput(reason=msg) result_dirs.append(sort_dir) else: result_dirs = [default_dir_value for _sort_key in result_keys] # Ensure that the key and direction length match while len(result_dirs) < len(result_keys): result_dirs.append(default_dir_value) # Unless more direction are specified, which is an error if len(result_dirs) > len(result_keys): msg = _("Sort direction array size exceeds sort key array size.") raise exception.InvalidInput(reason=msg) # Ensure defaults are included for key in default_keys: if key not in result_keys: result_keys.append(key) result_dirs.append(default_dir_value) return result_keys, result_dirs