def create_user(user_name, user_id, group_name=None, group_id=None): group_name = group_name or user_name group_id = group_id or user_id try: execute_shell_cmd('groupadd', [], '--gid', group_id, group_name, as_root=True) except exception.ProcessExecutionError as err: if 'already exists' not in err.stderr: raise exception.UnprocessableEntity( 'Failed to add group %s, error: %s' % (group_name, err.stderr)) try: execute_shell_cmd('useradd', [], '--uid', user_id, '--gid', group_id, '-M', user_name, as_root=True) except exception.ProcessExecutionError as err: if 'already exists' not in err.stderr: raise exception.UnprocessableEntity( 'Failed to add user %s, error: %s' % (user_name, err.stderr))
def copy(source, destination, force=False, preserve=False, recursive=True, **kwargs): """Copy a given file or directory to another location. Copy does NOT attempt to preserve ownership, permissions and timestamps unless the 'preserve' option is enabled. :seealso: _execute_shell_cmd for valid optional keyword arguments. :param source: Path to the source location. :type source: string :param destination: Path to the destination location. :type destination: string :param force: If an existing destination file cannot be opened, remove it and try again. :type force: boolean :param preserve: Preserve mode, ownership and timestamps. :type preserve: boolean :param recursive: Copy directories recursively. :type recursive: boolean :raises: :class:`UnprocessableEntity` if source or destination not given. """ if not source: raise exception.UnprocessableEntity(_("Missing source path.")) elif not destination: raise exception.UnprocessableEntity(_("Missing destination path.")) options = (('f', force), ('p', preserve), ('R', recursive)) _execute_shell_cmd('cp', options, source, destination, **kwargs)
def chown(path, user, group, recursive=True, force=False, **kwargs): """Changes the owner and group of a given file. seealso:: _execute_shell_cmd for valid optional keyword arguments. :param path: Path to the modified file. :type path: string :param user: Owner. :type user: string :param group: Group. :type group: string :param recursive: Operate on files and directories recursively. :type recursive: boolean :param force: Suppress most error messages. :type force: boolean :raises: :class:`UnprocessableEntity` if path not given. :raises: :class:`UnprocessableEntity` if owner/group not given. """ if not path: raise exception.UnprocessableEntity( _("Cannot change ownership of a blank file or directory.")) if not user and not group: raise exception.UnprocessableEntity( _("Please specify owner or group, or both.")) owner_group_modifier = _build_user_group_pair(user, group) options = (('f', force), ('R', recursive)) _execute_shell_cmd('chown', options, owner_group_modifier, path, **kwargs)
def change_user_group(user, group, append=True, add_group=True, **kwargs): """Adds a user to groups by using the usermod linux command with -a and -G options. seealso:: _execute_shell_cmd for valid optional keyword arguments. :param user: Username. :type user: string :param group: Group names. :type group: comma separated string :param append: Adds user to a group. :type append: boolean :param add_group: Lists the groups that the user is a member of. While adding a new groups to an existing user with '-G' option alone, will remove all existing groups that user belongs. Therefore, always add the '-a' (append) with '-G' option to add or append new groups. :type add_group: boolean :raises: :class:`UnprocessableEntity` if user or group not given. """ if not user: raise exception.UnprocessableEntity(_("Missing user.")) elif not group: raise exception.UnprocessableEntity(_("Missing group.")) options = (('a', append), ('G', add_group)) _execute_shell_cmd('usermod', options, group, user, **kwargs)
def move(source, destination, force=False, **kwargs): """Move a given file or directory to a new location. Move attempts to preserve the original ownership, permissions and timestamps. :seealso: _execute_shell_cmd for valid optional keyword arguments. :param source: Path to the source location. :type source: string :param destination: Path to the destination location. :type destination: string :param force: Do not prompt before overwriting. :type force: boolean :raises: :class:`UnprocessableEntity` if source or destination not given. """ if not source: raise exception.UnprocessableEntity(_("Missing source path.")) elif not destination: raise exception.UnprocessableEntity(_("Missing destination path.")) options = (('f', force), ) _execute_shell_cmd('mv', options, source, destination, **kwargs)
def add_shard(self): if self.db_info.task_status != ClusterTasks.NONE: current_task = self.db_info.task_status.name msg = _("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") % current_task LOG.error(msg) raise exception.UnprocessableEntity(msg) db_insts = inst_models.DBInstance.find_all(cluster_id=self.id, type='member').all() num_unique_shards = len(set([db_inst.shard_id for db_inst in db_insts])) if num_unique_shards == 0: msg = _("This action cannot be performed on the cluster as no " "reference shard exists.") LOG.error(msg) raise exception.UnprocessableEntity(msg) arbitrary_shard_id = db_insts[0].shard_id members_in_shard = [db_inst for db_inst in db_insts if db_inst.shard_id == arbitrary_shard_id] num_members_per_shard = len(members_in_shard) a_member = inst_models.load_any_instance(self.context, members_in_shard[0].id) deltas = {'instances': num_members_per_shard} volume_size = a_member.volume_size if volume_size: deltas['volumes'] = volume_size * num_members_per_shard check_quotas(self.context.tenant, deltas) new_replica_set_name = "rs" + str(num_unique_shards + 1) new_shard_id = utils.generate_uuid() dsv_manager = (datastore_models.DatastoreVersion. load_by_uuid(db_insts[0].datastore_version_id).manager) manager = task_api.load(self.context, dsv_manager) key = manager.get_key(a_member) member_config = {"id": self.id, "shard_id": new_shard_id, "instance_type": "member", "replica_set_name": new_replica_set_name, "key": key} for i in range(1, num_members_per_shard + 1): instance_name = "%s-%s-%s" % (self.name, new_replica_set_name, str(i)) inst_models.Instance.create(self.context, instance_name, a_member.flavor_id, a_member.datastore_version.image_id, [], [], a_member.datastore, a_member.datastore_version, volume_size, None, availability_zone=None, nics=None, configuration_id=None, cluster_config=member_config) self.update_db(task_status=ClusterTasks.ADDING_SHARD) manager.mongodb_add_shard_cluster( self.id, new_shard_id, new_replica_set_name)
def _validate_configuration(values, datastore_manager=None): rules = configurations.get_validation_rules( datastore_manager=datastore_manager) LOG.info(_("Validating configuration values")) for k, v in values.iteritems(): # get the validation rule dictionary, which will ensure there is a # rule for the given key name. An exception will be thrown if no # valid rule is located. rule = ConfigurationsController._get_item( k, rules['configuration-parameters']) if rule.get('deleted_at'): raise exception.ConfigurationParameterDeleted( parameter_name=rule.get('name'), parameter_deleted_at=rule.get('deleted_at')) # type checking valueType = rule.get('type') if not isinstance(v, ConfigurationsController._find_type( valueType)): output = {"key": k, "type": valueType} msg = _("The value provided for the configuration " "parameter %(key)s is not of type %(type)s.") % output raise exception.UnprocessableEntity(message=msg) # integer min/max checking if isinstance(v, int): try: min_value = int(rule.get('min')) except ValueError: raise exception.TroveError(_( "Invalid or unsupported min value defined in the " "configuration-parameters configuration file. " "Expected integer.")) if v < min_value: output = {"key": k, "min": min_value} message = _("The value for the configuration parameter " "%(key)s is less than the minimum allowed: " "%(min)s") % output raise exception.UnprocessableEntity(message=message) try: max_value = int(rule.get('max')) except ValueError: raise exception.TroveError(_( "Invalid or unsupported max value defined in the " "configuration-parameters configuration file. " "Expected integer.")) if v > max_value: output = {"key": k, "max": max_value} message = _("The value for the configuration parameter " "%(key)s is greater than the maximum " "allowed: %(max)s") % output raise exception.UnprocessableEntity(message=message)
def create_user(self, context, users): if len(users) > 1: raise exception.UnprocessableEntity( _("Only a single user can be created on the instance.")) user_list = self._list_buckets() if user_list: raise exception.UnprocessableEntity( _("There already is a user on this instance: %s") % user_list[0].name) self._create_bucket(models.CouchbaseUser.deserialize_user(users[0]))
def _validate_configuration(values, datastore_version=None): LOG.info(_("Validating configuration values")) for k, v in values.iteritems(): rule = DatastoreConfigurationParameters.load_parameter_by_name( datastore_version.id, k) if not rule or rule.deleted: output = {"key": k} msg = _("The parameter provided for the configuration " "%(key)s is not available.") % output raise exception.UnprocessableEntity(message=msg) # type checking value_type = rule.data_type if not isinstance(v, ConfigurationsController._find_type(value_type)): output = {"key": k, "type": value_type} msg = _("The value provided for the configuration " "parameter %(key)s is not of type %(type)s.") % output raise exception.UnprocessableEntity(message=msg) # integer min/max checking if isinstance(v, (int, long)) and not isinstance(v, bool): try: min_value = int(rule.min_size) except ValueError: raise exception.TroveError( _("Invalid or unsupported min value defined in the " "configuration-parameters configuration file. " "Expected integer.")) if v < min_value: output = {"key": k, "min": min_value} message = _("The value for the configuration parameter " "%(key)s is less than the minimum allowed: " "%(min)s") % output raise exception.UnprocessableEntity(message=message) try: max_value = int(rule.max_size) except ValueError: raise exception.TroveError( _("Invalid or unsupported max value defined in the " "configuration-parameters configuration file. " "Expected integer.")) if v > max_value: output = {"key": k, "max": max_value} message = _("The value for the configuration parameter " "%(key)s is greater than the maximum " "allowed: %(max)s") % output raise exception.UnprocessableEntity(message=message)
def chmod(path, mode, recursive=True, force=False, **kwargs): """Changes the mode of a given file. :seealso: Modes for more information on the representation of modes. :seealso: _execute_shell_cmd for valid optional keyword arguments. :param path: Path to the modified file. :type path: string :param mode: File permissions (modes). The modes will be applied in the following order: reset (=), add (+), remove (-) :type mode: FileMode :param recursive: Operate on files and directories recursively. :type recursive: boolean :param force: Suppress most error messages. :type force: boolean :raises: :class:`UnprocessableEntity` if path not given. :raises: :class:`UnprocessableEntity` if no mode given. """ if path: options = (('f', force), ('R', recursive)) shell_modes = _build_shell_chmod_mode(mode) _execute_shell_cmd('chmod', options, shell_modes, path, **kwargs) else: raise exception.UnprocessableEntity( _("Cannot change mode of a blank file."))
def create_directory(dir_path, user=None, group=None, force=True, **kwargs): """Create a given directory and update its ownership (recursively) to the given user and group if any. seealso:: _execute_shell_cmd for valid optional keyword arguments. :param dir_path: Path to the created directory. :type dir_path: string :param user: Owner. :type user: string :param group: Group. :type group: string :param force: No error if existing, make parent directories as needed. :type force: boolean :raises: :class:`UnprocessableEntity` if dir_path not given. """ if dir_path: _create_directory(dir_path, force, **kwargs) if user or group: chown(dir_path, user, group, **kwargs) else: raise exception.UnprocessableEntity( _("Cannot create a blank directory."))
def read_file(path, codec=IdentityCodec(), as_root=False, decode=True): """ Read a file into a Python data structure digestible by 'write_file'. :param path: Path to the read config file. :type path: string :param codec: A codec used to transform the data. :type codec: StreamCodec :param as_root: Execute as root. :type as_root: boolean :param decode: Should the codec decode the data. :type decode: boolean :returns: A dictionary of key-value pairs. :raises: :class:`UnprocessableEntity` if file doesn't exist. :raises: :class:`UnprocessableEntity` if codec not given. """ if path and exists(path, is_directory=False, as_root=as_root): if as_root: return _read_file_as_root(path, codec, decode=decode) with open(path, 'r') as fp: if decode: return codec.deserialize(fp.read()) return codec.serialize(fp.read()) raise exception.UnprocessableEntity(_("File does not exist: %s") % path)
def write_file(path, data, codec=IdentityCodec(), as_root=False, encode=True): """Write data into file using a given codec. Overwrite any existing contents. The written file can be read back into its original form by 'read_file'. :param path Path to the written config file. :type path string :param data: An object representing the file contents. :type data: object :param codec: A codec used to transform the data. :type codec: StreamCodec :param as_root: Execute as root. :type as_root: boolean :param encode: Should the codec encode the data. :type encode: boolean :raises: :class:`UnprocessableEntity` if path not given. """ if path: if as_root: _write_file_as_root(path, data, codec, encode=encode) else: with open(path, 'w') as fp: if encode: fp.write(codec.serialize(data)) else: fp.write(codec.deserialize(data)) fp.flush() else: raise exception.UnprocessableEntity(_("Invalid path: %s") % path)
def _delete_resources(): backup = cls.get_by_id(context, backup_id) if backup.is_running: msg = _("Backup %s cannot be deleted because it is running.") raise exception.UnprocessableEntity(msg % backup_id) cls.verify_swift_auth_token(context) api.API(context).delete_backup(backup_id)
def load_instance(cls, context, id, needs_server=False): db_info = get_db_info(context, id) if not needs_server: # TODO(tim.simpson): When we have notifications this won't be # necessary and instead we'll just use the server_status field from # the instance table. load_simple_instance_server_status(context, db_info) server = None else: try: server = load_server(context, db_info.id, db_info.compute_instance_id) #TODO(tim.simpson): Remove this hack when we have notifications! db_info.server_status = server.status db_info.addresses = server.addresses except exception.ComputeInstanceNotFound: LOG.error(_LE("Could not load compute instance %s."), db_info.compute_instance_id) raise exception.UnprocessableEntity("Instance %s is not ready." % id) service_status = InstanceServiceStatus.find_by(instance_id=id) LOG.debug("Instance %(instance_id)s service status is %(service_status)s.", { 'instance_id': id, 'service_status': service_status.status }) return cls(context, db_info, server, service_status)
def read_file(path, codec=IdentityCodec(), as_root=False): """ Read a file into a Python data structure digestible by 'write_file'. :param path Path to the read config file. :type path string :param codec: A codec used to deserialize the data. :type codec: StreamCodec :returns: A dictionary of key-value pairs. :param as_root: Execute as root. :type as_root: boolean :raises: :class:`UnprocessableEntity` if file doesn't exist. :raises: :class:`UnprocessableEntity` if codec not given. """ if path and os.path.exists(path): if as_root: return _read_file_as_root(path, codec) with open(path, 'r') as fp: return codec.deserialize(fp.read()) raise exception.UnprocessableEntity(_("File does not exist: %s") % path)
def config_set(self, name, value): response = self.execute( '%s %s' % (self.__config_cmd_name, 'SET'), name, value) if not self._is_ok_response(response): raise exception.UnprocessableEntity( _("Could not set configuration property '%(name)s' to " "'%(value)s'.") % {'name': name, 'value': value})
def _prep_resize(self): """Get information about the cluster's current state.""" if self.db_info.task_status != ClusterTasks.NONE: current_task = self.db_info.task_status.name log_fmt = ("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") exc_fmt = _("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") LOG.error(log_fmt, current_task) raise exception.UnprocessableEntity(exc_fmt % current_task) def _instances_of_type(instance_type): return [ db_inst for db_inst in self.db_instances if db_inst.type == instance_type ] self.config_svrs = _instances_of_type('config_server') self.query_routers = _instances_of_type('query_router') self.members = _instances_of_type('member') self.shard_ids = set([member.shard_id for member in self.members]) self.arbitrary_query_router = inst_models.load_any_instance( self.context, self.query_routers[0].id) self.manager = task_api.load(self.context, self.datastore_version.manager)
def validate_cluster_available(self, valid_states=[ClusterTasks.NONE]): if self.db_info.task_status not in valid_states: msg = (_("This action cannot be performed on the cluster while " "the current cluster task is '%s'.") % self.db_info.task_status.name) LOG.error(msg) raise exception.UnprocessableEntity(msg)
def execute(self, query, identifiers=None, data_values=None, timeout=None): """ Execute a query with a given sequence or dict of data values to bind. If a sequence is used, '%s' should be used the placeholder for each argument. If a dict is used, '%(name)s' style placeholders must be used. Only data values should be supplied this way. Other items, such as keyspaces, table names, and column names should be set ahead of time. Use the '{}' style placeholders and 'identifiers' parameter for those. Raise an exception if the operation exceeds the given timeout (sec). There is no timeout if set to None. Return a set of rows or an empty list if None. """ if self.__is_active(): try: rows = self.__session.execute(self.__bind(query, identifiers), data_values, timeout) return rows or [] except OperationTimedOut: LOG.error(_("Query execution timed out.")) raise LOG.debug("Cannot perform this operation on a closed connection.") raise exception.UnprocessableEntity()
def validate_can_perform_action(self): """ Raises exception if an instance action cannot currently be performed. """ # cases where action cannot be performed if self.db_info.server_status != 'ACTIVE': status = self.db_info.server_status elif (self.db_info.task_status != InstanceTasks.NONE and self.db_info.task_status != InstanceTasks.RESTART_REQUIRED): status = self.db_info.task_status elif not self.datastore_status.status.action_is_allowed: status = self.status elif Backup.running(self.id): status = InstanceStatus.BACKUP else: # action can be performed return msg = (_("Instance %(instance_id)s is not currently available for an " "action to be performed (status was %(action_status)s).") % { 'instance_id': self.id, 'action_status': status }) LOG.error(msg) raise exception.UnprocessableEntity(msg)
def _build_shell_chmod_mode(mode): """ Build a shell representation of given mode. :seealso: Modes for more information on the representation of modes. :param mode: File permissions (modes). :type mode: FileModes :raises: :class:`UnprocessableEntity` if no mode given. :returns: Following string for any non-empty modes: '=<reset mode>,+<add mode>,-<remove mode>' """ # Handle methods passed in as constant fields. if inspect.ismethod(mode): mode = mode() if mode and mode.has_any(): text_modes = (('=', mode.get_reset_mode()), ('+', mode.get_add_mode()), ('-', mode.get_remove_mode())) return ','.join([ '{0:s}{1:03o}'.format(item[0], item[1]) for item in text_modes if item[1] ]) else: raise exception.UnprocessableEntity(_("No file mode specified."))
def load_and_verify(context, instance_id): # Load InstanceServiceStatus to verify if its running instance = base_models.Instance.load(context, instance_id) if not instance.is_datastore_running: raise exception.UnprocessableEntity("Instance %s is not ready." % instance.id) else: return instance
def update_attributes(self, context, username, hostname, user_attrs): new_name = user_attrs.get('name') if new_name: raise exception.UnprocessableEntity(_("Users cannot be renamed.")) new_password = user_attrs.get('password') if new_password: user = models.CouchbaseUser(username, password=new_password) self._edit_bucket(user)
def _delete_resources(): if self.is_building: raise exception.UnprocessableEntity("Instance %s is not ready." % self.id) LOG.debug(_(" ... deleting compute id = %s") % self.db_info.compute_instance_id) LOG.debug(_(" ... setting status to DELETING.")) self.update_db(task_status=InstanceTasks.DELETING) task_api.API(self.context).delete_instance(self.id)
def get_validation_rules(datastore_manager='mysql'): try: config_location = ("%s/validation-rules.json" % datastore_manager) template = ENV.get_template(config_location) return json.loads(template.render()) except Exception: msg = "This operation is not supported for this datastore at this time" LOG.exception(msg) raise exception.UnprocessableEntity(message=msg)
def max_num_overrides(self, value): """ Maximum number of configuration overrides that can be attached to this instance. """ if value and value < 0: raise exception.UnprocessableEntity( _("The maximum number of attached Configuration Groups " "cannot be negative.")) self._max_num_overrides = value
def instance_root_index(self, req, tenant_id, instance_id): LOG.info(_LI("Getting root enabled for instance '%s'.") % instance_id) LOG.info(_LI("req : '%s'\n\n") % req) context = req.environ[wsgi.CONTEXT_KEY] try: is_root_enabled = models.ClusterRoot.load(context, instance_id) except exception.UnprocessableEntity: raise exception.UnprocessableEntity( _("Cluster %s is not ready.") % instance_id) return wsgi.Result(views.RootEnabledView(is_root_enabled).data(), 200)
def load_and_verify(context, instance_id, enabled_datastore=['mysql', 'mariadb']): """Check instance datastore. Some API operations are only supported for some specific datastores. """ instance = base_models.Instance.load(context, instance_id) if instance.datastore_version.manager not in enabled_datastore: raise exception.UnprocessableEntity( "Operation not supported for datastore " f"{instance.datastore_version.manager}.") if not instance.is_datastore_running: raise exception.UnprocessableEntity( "Instance %s is not ready, status: %s." % (instance.id, instance.datastore_status.status)) return instance
def _execute_stmt(self, statement, identifiers, data_values, fetch): if statement: with psycopg2.connect(**self._connection_args) as connection: connection.autocommit = self._autocommit with connection.cursor() as cursor: cursor.execute(self._bind(statement, identifiers), data_values) if fetch: return cursor.fetchall() else: raise exception.UnprocessableEntity( _("Invalid SQL statement: %s") % statement)