Server IP : 15.235.198.142 / Your IP : 216.73.216.125 Web Server : Apache/2.4.58 (Ubuntu) System : Linux ballsack 6.8.0-45-generic #45-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 30 12:02:04 UTC 2024 x86_64 User : www-data ( 33) PHP Version : 8.3.6 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/landscape/lib/ |
Upload File : |
import os.path import sys from logging import getLogger from optparse import OptionParser from configobj import ConfigObj from configobj import ConfigObjError from twisted.python.compat import StringType from landscape.lib import cli def add_cli_options(parser, filename=None): """Add common config-related CLI options to the given arg parser.""" cfgfilehelp = ( "Use config from this file (any command line " "options override settings from the file)." ) if filename is not None: cfgfilehelp += f" (default: {filename!r})" parser.add_option( "-c", "--config", metavar="FILE", default=filename, help=cfgfilehelp, ) class ConfigSpecOptionParser(OptionParser): _config_spec_definitions = {} def __init__(self, unsaved_options=None): OptionParser.__init__(self, unsaved_options) def add_option(self, *args, **kwargs): option = OptionParser.add_option(self, *args, **kwargs) print(dir(option)) print(option.get_opt_string()) return option class BaseConfiguration: """Base class for configuration implementations. @cvar required_options: Optionally, a sequence of key names to require when reading or writing a configuration. @cvar unsaved_options: Optionally, a sequence of key names to never write to the configuration file. This is useful when you want to provide command-line options that should never end up in a configuration file. @cvar default_config_filenames: A sequence of filenames to check when reading or writing a configuration. Default values for supported options are set as in make_parser. """ version = None required_options = () unsaved_options = () default_config_filenames = () default_data_dir = None config_section = None def __init__(self): self._set_options = {} self._command_line_args = [] self._command_line_options = {} self._config_filename = None self._config_file_options = {} self._parser = self.make_parser() self._command_line_defaults = self._parser.defaults.copy() # We don't want them mixed with explicitly given options, # otherwise we can't define the precedence properly. self._parser.defaults.clear() def __getattr__(self, name): """Find and return the value of the given configuration parameter. The following sources will be searched: - The attributes that were explicitly set on this object, - The parameters specified on the command line, - The parameters specified in the configuration file, and - The defaults. If no values are found and the parameter does exist as a possible parameter, C{None} is returned. Otherwise C{AttributeError} is raised. """ for options in [ self._set_options, self._command_line_options, self._config_file_options, self._command_line_defaults, ]: if name in options: value = options[name] break else: if self._parser.has_option("--" + name.replace("_", "-")): value = None else: raise AttributeError(name) if isinstance(value, StringType): option = self._parser.get_option("--" + name.replace("_", "-")) if option is not None: value = option.convert_value(None, value) return value def clone(self): """ Return a new configuration object, with the same settings as this one. """ config = self.__class__() config._set_options = self._set_options.copy() config._command_line_options = self._command_line_options.copy() config._config_filename = self._config_filename config._config_file_options = self._config_file_options.copy() return config def get(self, name, default=None): """Return the value of the C{name} option or C{default}.""" try: return self.__getattr__(name) except AttributeError: return default def __setattr__(self, name, value): """Set a configuration parameter. If the name begins with C{_}, it will only be set on this object and not stored in the configuration file. """ if name.startswith("_"): super().__setattr__(name, value) else: self._set_options[name] = value def reload(self): """Reload options using the configured command line arguments. @see: L{load_command_line} """ self.load(self._command_line_args) def load(self, args, accept_nonexistent_default_config=False): """ Load configuration data from command line arguments and a config file. @param accept_nonexistent_default_config: If True, don't complain if default configuration files aren't found @raise: A SystemExit if the arguments are bad. """ self.load_command_line(args) if self.config: config_filenames = [self.config] allow_missing = False else: config_filenames = self.default_config_filenames allow_missing = accept_nonexistent_default_config # Parse configuration file, if found. for config_filename in config_filenames: if os.path.isfile(config_filename) and os.access( config_filename, os.R_OK, ): self.load_configuration_file(config_filename) break else: if not allow_missing: if len(config_filenames) == 1: message = ( f"error: config file {config_filenames[0]} " "can't be read" ) else: message = "error: no config file could be read" sys.exit(message) self._load_external_options() # Check that all needed options were given. for option in self.required_options: if not getattr(self, option): sys.exit( "error: must specify --{} " "or the '{}' directive in the config file.".format( option.replace("_", "-"), option, ), ) def _load_external_options(self): """Hook for loading options from elsewhere (e.g. for --import).""" def load_command_line(self, args): """Load configuration data from the given command line.""" self._command_line_args = args values = self._parser.parse_args(args)[0] self._command_line_options = vars(values) def load_configuration_file(self, filename): """Load configuration data from the given file name. If any data has already been set on this configuration object, then the old data will take precedence. """ self._config_filename = filename config_obj = self._get_config_object() try: self._config_file_options = config_obj[self.config_section] except KeyError: pass def _get_config_object(self, alternative_config=None): """Create a L{ConfigObj} consistent with our preferences. @param config_source: Optional readable source to read from instead of the default configuration file. """ config_source = alternative_config or self.get_config_filename() # Setting list_values to False prevents ConfigObj from being "smart" # about lists (it now treats them as strings). See bug #1228301 for # more context. # Setting raise_errors to False causes ConfigObj to batch all parsing # errors into one ConfigObjError raised at the end of the parse instead # of raising the first one and then exiting. This also allows us to # recover the good config values in the error handler below. # Setting write_empty_values to True prevents configObj writes # from writing "" as an empty value, which get_plugins interprets as # '""' which search for a plugin named "". See bug #1241821. try: config_obj = ConfigObj( config_source, list_values=False, raise_errors=False, write_empty_values=True, encoding=getattr(self, "encoding", None), ) except ConfigObjError as e: logger = getLogger() logger.warn("ERROR at {}: {}".format(config_source, str(e))) # Good configuration values are recovered here config_obj = e.config return config_obj def write(self): """Write back configuration to the configuration file. Values which match the default option in the parser won't be saved. Options are considered in the following precedence: 1. Manually set options (C{config.option = value}) 2. Options passed in the command line 3. Previously existent options in the configuration file The filename picked for saving configuration options is the one returned by L{get_config_filename}. """ # The filename we'll write to filename = self.get_config_filename() # Make sure we read the old values from the config file so that we # don't remove *unrelated* values. config_obj = self._get_config_object() if self.config_section not in config_obj: config_obj[self.config_section] = {} all_options = self._config_file_options.copy() all_options.update(self._command_line_options) all_options.update(self._set_options) section = config_obj[self.config_section] for name, value in all_options.items(): if name != "config" and name not in self.unsaved_options: if ( value == self._command_line_defaults.get(name) and name not in self._config_file_options and name not in self._command_line_options ): # We don't want to write this value to the config file # as it is default value and as not present in the # config file if name in config_obj[self.config_section]: del config_obj[self.config_section][name] else: section[name] = value config_obj[self.config_section] = section config_obj.filename = filename config_obj.write() def make_parser(self, cfgfile=None, datadir=None): """Parser factory for supported options. @return: An OptionParser preset with options that all programs commonly accept. These include - config - data_path """ parser = OptionParser(version=self.version) cli.add_cli_options(parser, cfgfile, datadir) return parser def get_config_filename(self): """Pick the proper configuration file. The picked filename is: 1. C{self.config}, if defined 2. The last loaded configuration file, if any 3. The first filename in C{self.default_config_filenames} """ if self.config: return self.config if self._config_filename: return self._config_filename if self.default_config_filenames: for potential_config_file in self.default_config_filenames: if os.access(potential_config_file, os.R_OK): return potential_config_file return self.default_config_filenames[0] return None def get_command_line_options(self): """Get currently loaded command line options. @see: L{load_command_line} """ return self._command_line_options def get_bindir(config=None): """Return the directory path where the client binaries are. If the config is None, it doesn't have a "bindir" attribute, or its value is None, then sys.argv[0] is returned. """ try: bindir = config.bindir except AttributeError: # ...also the result if config is None. bindir = None if bindir is None: bindir = os.path.dirname(os.path.abspath(sys.argv[0])) return bindir