def dictupdate(dict1, dict2): ''' Merge two dictionnaries recursively test:: salt '*' mc_utils.dictupdate '{foobar: {toto: tata, toto2: tata2},titi: tutu}' '{bar: toto, foobar: {toto2: arg, toto3: arg2}}' ---------- bar: toto foobar: ---------- toto: tata toto2: arg toto3: arg2 titi: tutu ''' if not isinstance(dict1, dict): raise SaltException( 'mc_utils.dictupdate 1st argument is not a dictionnary!') if not isinstance(dict2, dict): raise SaltException( 'mc_utils.dictupdate 2nd argument is not a dictionnary!') return update_no_list(dict1, dict2)
def set_(environ): ''' Set the salt process environment variables. Accepts a dict 'environ'. Each top-level key of the dict are the names of the environment variables to set. The value to set must be a string. CLI Example: .. code-block:: bash salt '*' environ.set '{"foo": "bar", "baz": "quux"}' ''' ret = {} if not isinstance(environ, dict): raise SaltException('The "environ" argument variable must be a dict') try: for key, val in environ.items(): if not isinstance(val, string_types): raise SaltException( 'The value of "environ" keys must be string type') os.environ[key] = val ret[key] = os.environ[key] except Exception as exc: raise SaltException(exc) return ret
def _build_cmd(**kwargs): """ Build a well-formatted ipvsadm command based on kwargs. """ cmd = "" if "service_address" in kwargs: if kwargs["service_address"]: if "protocol" in kwargs: if kwargs["protocol"] == "tcp": cmd += " -t {}".format(kwargs["service_address"]) elif kwargs["protocol"] == "udp": cmd += " -u {}".format(kwargs["service_address"]) elif kwargs["protocol"] == "fwmark": cmd += " -f {}".format(kwargs["service_address"]) else: raise SaltException( "Error: Only support tcp, udp and fwmark service protocol" ) del kwargs["protocol"] else: raise SaltException("Error: protocol should specified") if "scheduler" in kwargs: if kwargs["scheduler"]: cmd += " -s {}".format(kwargs["scheduler"]) del kwargs["scheduler"] else: raise SaltException("Error: service_address should specified") del kwargs["service_address"] if "server_address" in kwargs: if kwargs["server_address"]: cmd += " -r {}".format(kwargs["server_address"]) if "packet_forward_method" in kwargs and kwargs[ "packet_forward_method"]: if kwargs["packet_forward_method"] == "dr": cmd += " -g" elif kwargs["packet_forward_method"] == "tunnel": cmd += " -i" elif kwargs["packet_forward_method"] == "nat": cmd += " -m" else: raise SaltException( "Error: only support dr, tunnel and nat") del kwargs["packet_forward_method"] if "weight" in kwargs and kwargs["weight"]: cmd += " -w {}".format(kwargs["weight"]) del kwargs["weight"] else: raise SaltException("Error: server_address should specified") del kwargs["server_address"] return cmd
def _build_cmd(**kwargs): ''' Build a well-formatted ipvsadm command based on kwargs. ''' cmd = '' if 'service_address' in kwargs: if kwargs['service_address']: if 'protocol' in kwargs: if kwargs['protocol'] == 'tcp': cmd += ' -t {0}'.format(kwargs['service_address']) elif kwargs['protocol'] == 'udp': cmd += ' -u {0}'.format(kwargs['service_address']) elif kwargs['protocol'] == 'fwmark': cmd += ' -f {0}'.format(kwargs['service_address']) else: raise SaltException( 'Error: Only support tcp, udp and fwmark service protocol' ) del kwargs['protocol'] else: raise SaltException('Error: protocol should specified') if 'scheduler' in kwargs: if kwargs['scheduler']: cmd += ' -s {0}'.format(kwargs['scheduler']) del kwargs['scheduler'] else: raise SaltException('Error: service_address should specified') del kwargs['service_address'] if 'server_address' in kwargs: if kwargs['server_address']: cmd += ' -r {0}'.format(kwargs['server_address']) if 'packet_forward_method' in kwargs and kwargs[ 'packet_forward_method']: if kwargs['packet_forward_method'] == 'dr': cmd += ' -g' elif kwargs['packet_forward_method'] == 'tunnel': cmd += ' -i' elif kwargs['packet_forward_method'] == 'nat': cmd += ' -m' else: raise SaltException( 'Error: only support dr, tunnel and nat') del kwargs['packet_forward_method'] if 'weight' in kwargs and kwargs['weight']: cmd += ' -w {0}'.format(kwargs['weight']) del kwargs['weight'] else: raise SaltException('Error: server_address should specified') del kwargs['server_address'] return cmd
def filter_by(lookup_dict, lookup, traverse, merge=None, default="default", base=None): """ Common code to filter data structures like grains and pillar """ ret = None # Default value would be an empty list if lookup not found val = traverse_dict_and_list(traverse, lookup, []) # Iterate over the list of values to match against patterns in the # lookup_dict keys for each in val if isinstance(val, list) else [val]: for key in lookup_dict: test_key = key if isinstance( key, six.string_types) else six.text_type(key) test_each = (each if isinstance(each, six.string_types) else six.text_type(each)) if fnmatch.fnmatchcase(test_each, test_key): ret = lookup_dict[key] break if ret is not None: break if ret is None: ret = lookup_dict.get(default, None) if base and base in lookup_dict: base_values = lookup_dict[base] if ret is None: ret = base_values elif isinstance(base_values, Mapping): if not isinstance(ret, Mapping): raise SaltException( "filter_by default and look-up values must both be " "dictionaries.") ret = salt.utils.dictupdate.update(copy.deepcopy(base_values), ret) if merge: if not isinstance(merge, Mapping): raise SaltException( "filter_by merge argument must be a dictionary.") if ret is None: ret = merge else: salt.utils.dictupdate.update(ret, copy.deepcopy(merge)) return ret
def get(keys): ''' Get the salt process environment variables. 'keys' can be either a string or a list of strings that will be used as the keys for environment lookup. CLI Example: .. code-block:: bash salt '*' environ.get foo salt '*' environ.get '[foo, baz]' ''' ret = {} key_list = [] if isinstance(keys, string_types): key_list.append(keys) elif isinstance(keys, list): key_list = keys else: raise SaltException( 'The "keys" argument variable must be string or list.') for key in key_list: ret[key] = os.environ[key] return ret
def _get_provider(): ''' Determin which gitfs_provider to use ''' # Don't re-perform all the verification if we already have a verified # provider if 'verified_gitfs_provider' in __opts__: return __opts__['verified_gitfs_provider'] provider = __opts__.get('gitfs_provider', '').lower() if not provider: # Prefer GitPython if it's available and verified if _verify_gitpython(quiet=True): return 'gitpython' elif _verify_pygit2(quiet=True): return 'pygit2' elif _verify_dulwich(quiet=True): return 'dulwich' else: log.error( 'No suitable version of GitPython, pygit2/libgit2, or Dulwich ' 'is installed.' ) else: if provider not in VALID_PROVIDERS: raise SaltException( 'Invalid gitfs_provider {0!r}. Valid choices are: {1}' .format(provider, ', '.join(VALID_PROVIDERS)) ) elif provider == 'gitpython' and _verify_gitpython(): return 'gitpython' elif provider == 'pygit2' and _verify_pygit2(): return 'pygit2' elif provider == 'dulwich' and _verify_dulwich(): return 'dulwich' return ''
def get_property(host=None, admin_username=None, admin_password=None, property=None): ''' .. versionadded:: Fluorine Return specific property host The chassis host. admin_username The username used to access the chassis. admin_password The password used to access the chassis. property: The property which should be get. CLI Example: .. code-block:: bash salt dell dracr.get_property property=System.ServerOS.HostName ''' if property is None: raise SaltException('No property specified!') ret = __execute_ret('get \'{0}\''.format(property), host=host, admin_username=admin_username, admin_password=admin_password) return ret
def content_check(self, result): """ Checks for specific types in the state output. Raises an Exception in case particular rule is broken. :param result: :return: """ if not isinstance(result, dict): err_msg = "Malformed state return. Data must be a dictionary type." elif not isinstance(result.get("changes"), dict): err_msg = "'Changes' should be a dictionary." else: missing = [] for val in ["name", "result", "changes", "comment"]: if val not in result: missing.append(val) if missing: err_msg = "The following keys were not present in the state return: {}.".format( ", ".join(missing)) else: err_msg = None if err_msg: raise SaltException(err_msg) for sub_state in result.get("sub_state_run", ()): self.content_check(sub_state) return result
def __init__(self, *policies): self.policies = [] for pls in policies: if not hasattr(self, pls): raise SaltException('Unknown policy: {0}'.format(pls)) else: self.policies.append(getattr(self, pls))
def content_check(self, result): ''' Checks for specific types in the state output. Raises an Exception in case particular rule is broken. :param result: :return: ''' if not isinstance(result, dict): err_msg = 'Malformed state return. Data must be a dictionary type.' elif not isinstance(result.get('changes'), dict): err_msg = "'Changes' should be a dictionary." else: missing = [] for val in ['name', 'result', 'changes', 'comment']: if val not in result: missing.append(val) if missing: err_msg = 'The following keys were not present in the state return: {0}.'.format( ', '.join(missing)) else: err_msg = None if err_msg: raise SaltException(err_msg) return result
def __init__(self, tgt='', expr_form='glob', saltenv=None, use_cached_grains=True, use_cached_pillar=True, grains_fallback=True, pillar_fallback=True, opts=None): log.debug('New instance of {0} created.'.format( self.__class__.__name__)) if opts is None: log.error('{0}: Missing master opts init arg.'.format( self.__class__.__name__)) raise SaltException('{0}: Missing master opts init arg.'.format( self.__class__.__name__)) else: self.opts = opts self.serial = salt.payload.Serial(self.opts) self.tgt = tgt self.expr_form = expr_form self.saltenv = saltenv self.use_cached_grains = use_cached_grains self.use_cached_pillar = use_cached_pillar self.grains_fallback = grains_fallback self.pillar_fallback = pillar_fallback self.cache = salt.cache.Cache(opts) log.debug( 'Init settings: tgt: \'{0}\', expr_form: \'{1}\', saltenv: \'{2}\', ' 'use_cached_grains: {3}, use_cached_pillar: {4}, ' 'grains_fallback: {5}, pillar_fallback: {6}'.format( tgt, expr_form, saltenv, use_cached_grains, use_cached_pillar, grains_fallback, pillar_fallback))
def __init__(self, tgt='', tgt_type='glob', saltenv=None, use_cached_grains=True, use_cached_pillar=True, grains_fallback=True, pillar_fallback=True, opts=None): log.debug('New instance of %s created.', self.__class__.__name__) if opts is None: log.error('%s: Missing master opts init arg.', self.__class__.__name__) raise SaltException('{0}: Missing master opts init arg.'.format( self.__class__.__name__)) else: self.opts = opts self.serial = salt.payload.Serial(self.opts) self.tgt = tgt self.tgt_type = tgt_type self.saltenv = saltenv self.use_cached_grains = use_cached_grains self.use_cached_pillar = use_cached_pillar self.grains_fallback = grains_fallback self.pillar_fallback = pillar_fallback self.cache = salt.cache.factory(opts) log.debug( 'Init settings: tgt: \'%s\', tgt_type: \'%s\', saltenv: \'%s\', ' 'use_cached_grains: %s, use_cached_pillar: %s, ' 'grains_fallback: %s, pillar_fallback: %s', tgt, tgt_type, saltenv, use_cached_grains, use_cached_pillar, grains_fallback, pillar_fallback)
def attach_file_content(sls, minion, pillar): for state in sls: if state['state'] == 'file': if state[ 'fun'] == 'rename': # the `source` of `file.rename` is not a local file continue if 'source' in state and 'sources' in state: raise SaltException( '`source` and `sources` are mutually exclusive in state with id `{}`.' .format(state['__id__'])) if 'sources' in state: content = [] for source in state['sources']: content.append( get_file_content(source, state, minion, pillar)) state['__file_content'] = ''.join(content) if 'source' in state: if state['fun'] == 'recurse': files = {} prefix = state['source'][ 7:] # remove the salt:// portion of the source url for path in minion.cmd('cp.list_master', prefix=prefix): destination = os.path.join(state['name'], path[len(prefix) + 1:]) files[destination] = get_file_content( 'salt://{}'.format(path), state, minion, pillar) state['__file_content'] = files else: if not isinstance(state['source'], list): state['source'] = [state['source']] for source in state['source']: try: state['__file_content'] = get_file_content( source, state, minion, pillar) break except SaltException: pass else: raise SaltException( 'Unable to locate source for {!r}'.format( state['source'])) return sls
def __init__(self, address): """ Instantiate a new IPv6 address object. Scope is moved to an attribute 'scope'. Args: address: A string or integer representing the IP Additionally, an integer can be passed, so IPv6Address('2001:db8::') == IPv6Address(42540766411282592856903984951653826560) or, more generally IPv6Address(int(IPv6Address('2001:db8::'))) == IPv6Address('2001:db8::') Raises: AddressValueError: If address isn't a valid IPv6 address. :param address: """ # pylint: disable-all if not hasattr(self, "_is_packed_binary"): # This method (below) won't be around for some Python 3 versions # and we need check this differently anyway self._is_packed_binary = lambda p: isinstance(p, bytes) # pylint: enable-all if isinstance(address, string_types) and "%" in address: buff = address.split("%") if len(buff) != 2: raise SaltException( 'Invalid IPv6 address: "{}"'.format(address)) address, self.__scope = buff else: self.__scope = None # For compatibility with python3.9 ipaddress self._scope_id = self.__scope if sys.version_info.major == 2: ipaddress._BaseAddress.__init__(self, address) ipaddress._BaseV6.__init__(self, address) else: # Python 3.4 fix. Versions higher are simply not affected # https://github.com/python/cpython/blob/3.4/Lib/ipaddress.py#L543-L544 self._version = 6 self._max_prefixlen = ipaddress.IPV6LENGTH # Efficient constructor from integer. if isinstance(address, integer_types): self._check_int_address(address) self._ip = address elif self._is_packed_binary(address): self._check_packed_address(address, 16) self._ip = int(binascii.hexlify(address), 16) else: address = str(address) if "/" in address: raise ipaddress.AddressValueError( "Unexpected '/' in {}".format(address)) self._ip = self._ip_int_from_string(address)
def _parse_conf(conf_file=None, in_mem=False, family='ipv4'): ''' If a file is not passed in, and the correct one for this OS is not detected, return False ''' if _conf() and not conf_file and not in_mem: conf_file = _conf(family) rules = '' if conf_file: with salt.utils.fopen(conf_file, 'r') as ifile: rules = ifile.read() elif in_mem: cmd = '{0}-save'.format(_iptables_cmd(family)) rules = __salt__['cmd.run'](cmd) else: raise SaltException('A file was not found to parse') ret = {} table = '' for line in rules.splitlines(): if line.startswith('*'): table = line.replace('*', '') ret[table] = {} elif line.startswith(':'): comps = line.split() chain = comps[0].replace(':', '') ret[table][chain] = {} ret[table][chain]['policy'] = comps[1] counters = comps[2].replace('[', '').replace(']', '') (pcount, bcount) = counters.split(':') ret[table][chain]['packet count'] = pcount ret[table][chain]['byte count'] = bcount ret[table][chain]['rules'] = [] ret[table][chain]['rules_comment'] = {} elif line.startswith('-A'): parser = _parser() parsed_args = [] if sys.version.startswith('2.6'): (opts, args) = parser.parse_args(shlex.split(line)) parsed_args = vars(opts) else: parsed_args = vars(parser.parse_args(shlex.split(line))) ret_args = {} chain = parsed_args['append'] if not sys.version.startswith('2.6'): if isinstance(chain, list): chain = chain[0] for arg in parsed_args: if parsed_args[arg] and arg is not 'append': ret_args[arg] = parsed_args[arg] if parsed_args['comment'] is not None: comment = parsed_args['comment'][0].strip('"') ret[table][chain[0]]['rules_comment'][comment] = ret_args ret[table][chain[0]]['rules'].append(ret_args) return ret
def _connect(host, port): ''' Returns a tuple of (user, host, port) with config, pillar, or default values assigned to missing values. ''' if not host: user = __salt__['config.option']('memcache.host') if not port: port = __salt__['config.option']('memcache.port') if not HAS_MEMCACHE: raise SaltException('Error: python-memcached is not installed.') else: if str(port).isdigit(): conn = memcache.Client(["%s:%s" % (host, port)], debug=0) else: raise SaltException('Error: port must be a number.') return conn
def _get_ccp(config=None, config_path=None, saltenv="base"): """ """ if config_path: config = __salt__["cp.get_file_str"](config_path, saltenv=saltenv) if config is False: raise SaltException("{} is not available".format(config_path)) if isinstance(config, str): config = config.splitlines() ccp = ciscoconfparse.CiscoConfParse(config) return ccp
def filter_by( lookup, tgt_type="compound", minion_id=None, merge=None, merge_lists=False, default="default", ): """ Return the first match in a dictionary of target patterns .. versionadded:: 2014.7.0 CLI Example: .. code-block:: bash salt '*' match.filter_by '{foo*: Foo!, bar*: Bar!}' minion_id=bar03 Pillar Example: .. code-block:: jinja # Filter the data for the current minion into a variable: {% set roles = salt['match.filter_by']({ 'web*': ['app', 'caching'], 'db*': ['db'], }, default='web*') %} # Make the filtered data available to Pillar: roles: {{ roles | yaml() }} """ expr_funcs = dict( inspect.getmembers(sys.modules[__name__], predicate=inspect.isfunction) ) for key in lookup: params = (key, minion_id) if minion_id else (key,) if expr_funcs[tgt_type](*params): if merge: if not isinstance(merge, Mapping): raise SaltException( "filter_by merge argument must be a dictionary." ) if lookup[key] is None: return merge else: salt.utils.dictupdate.update( lookup[key], copy.deepcopy(merge), merge_lists=merge_lists ) return lookup[key] return lookup.get(default, None)
def _get_ccp(config=None, config_path=None, saltenv='base'): ''' ''' if config_path: config = __salt__['cp.get_file_str'](config_path, saltenv=saltenv) if config is False: raise SaltException('{} is not available'.format(config_path)) if isinstance(config, six.string_types): config = config.splitlines() ccp = ciscoconfparse.CiscoConfParse(config) return ccp
def decr(host, port, key, delta=1): ''' decr key CLI Example: .. code-block:: bash salt '*' memcache.decr <host> <port> <key> <delta> ''' conn = _connect(host, port) status = conn.get_stats() if status == []: raise SaltException('Error: memcache server is down or not exists.') else: try: ret = conn.decr(key, delta) except ValueError: raise SaltException('Error: decr key must be a number.') return ret
def _cli_command(commands, method="cli", **kwargs): """ Execute a list of CLI commands. """ if not isinstance(commands, (list, tuple)): commands = [commands] rpc_responses = rpc(commands, method=method, **kwargs) txt_responses = [] for rpc_reponse in rpc_responses: error = rpc_reponse.get("error") if error: cmd = rpc_reponse.get("command") if "data" in error: msg = 'The command "{cmd}" raised the error "{err}".'.format( cmd=cmd, err=error["data"]["msg"]) raise SaltException(msg) else: msg = 'Invalid command: "{cmd}".'.format(cmd=cmd) raise SaltException(msg) txt_responses.append(rpc_reponse["result"]) return txt_responses
def _cli_command(commands, method='cli', **kwargs): ''' Execute a list of CLI commands. ''' if not isinstance(commands, (list, tuple)): commands = [commands] rpc_responses = rpc(commands, method=method, **kwargs) txt_responses = [] for rpc_reponse in rpc_responses: error = rpc_reponse.get('error') if error: cmd = rpc_reponse.get('command') if 'data' in error: msg = 'The command "{cmd}" raised the error "{err}".'.format( cmd=cmd, err=error['data']['msg']) raise SaltException(msg) else: msg = 'Invalid command: "{cmd}".'.format(cmd=cmd) raise SaltException(msg) txt_responses.append(rpc_reponse['result']) return txt_responses
def set_property(host=None, admin_username=None, admin_password=None, property=None, value=None): ''' .. versionadded:: Fluorine Set specific property host The chassis host. admin_username The username used to access the chassis. admin_password The password used to access the chassis. property: The property which should be set. value: The value which should be set to property. CLI Example: .. code-block:: bash salt dell dracr.set_property property=System.ServerOS.HostName value=Pretty-server ''' if property is None: raise SaltException('No property specified!') elif value is None: raise SaltException('No value specified!') ret = __execute_ret('set \'{0}\' \'{1}\''.format(property, value), host=host, admin_username=admin_username, admin_password=admin_password) return ret
def _get_install_path(): ''' Determine the installation path from the relevant registry value. ''' base_key = r'SOFTWARE\Octopus\Tentacle' vname = 'InstallLocation' value = (__salt__['reg.read_value'](_HKEY, base_key, vname)) if value.get('vdata', None): return value['vdata'] message = r"Value data for '{0}' not found in {1}\{2}.".format(vname, _HKEY, base_key) raise SaltException(message)
def run(self, low): ''' Execute the specified function in the specified client by passing the LowData ''' if not 'client' in low: raise SaltException('No client specified') l_fun = getattr(self, low['client']) f_call = salt.utils.format_call(l_fun, low) ret = l_fun(*f_call.get('args', ()), **f_call.get('kwargs', {})) return ret
def get_master_uri(opts): if "master_uri" in opts: return opts["master_uri"] if "master_ip" in opts: return _get_master_uri( opts["master_ip"], opts["master_port"], source_ip=opts.get("source_ip"), source_port=opts.get("source_ret_port"), ) # if we've reached here something is very abnormal raise SaltException( "ReqChannel: missing master_uri/master_ip in self.opts")
def master_uri(self): if "master_uri" in self.opts: return self.opts["master_uri"] # if by chance master_uri is not there.. if "master_ip" in self.opts: return _get_master_uri( self.opts["master_ip"], self.opts["master_port"], source_ip=self.opts.get("source_ip"), source_port=self.opts.get("source_ret_port"), ) # if we've reached here something is very abnormal raise SaltException("ReqChannel: missing master_uri/master_ip in self.opts")
def run(self, low): ''' Execute the specified function in the specified client by passing the lowstate ''' if 'client' not in low: raise SaltException('No client specified') if not ('token' in low or 'eauth' in low) and low['client'] != 'ssh': raise EauthAuthenticationError( 'No authentication credentials given') l_fun = getattr(self, low['client']) f_call = salt.utils.format_call(l_fun, low) return l_fun(*f_call.get('args', ()), **f_call.get('kwargs', {}))
def _parse_config(conf, slot=None): """ Recursively goes through config structure and builds final Apache configuration :param conf: defined config structure :param slot: name of section container if needed """ ret = io.StringIO() if isinstance(conf, str): if slot: print("{} {}".format(slot, conf), file=ret, end="") else: print("{}".format(conf), file=ret, end="") elif isinstance(conf, list): is_section = False for item in conf: if "this" in item: is_section = True slot_this = str(item["this"]) if is_section: print("<{} {}>".format(slot, slot_this), file=ret) for item in conf: for key, val in item.items(): if key != "this": print(_parse_config(val, str(key)), file=ret) print("</{}>".format(slot), file=ret) else: for value in conf: print(_parse_config(value, str(slot)), file=ret) elif isinstance(conf, dict): try: print("<{} {}>".format(slot, conf["this"]), file=ret) except KeyError: raise SaltException( 'Apache section container "<{}>" expects attribute. ' 'Specify it using key "this".'.format(slot)) for key, value in conf.items(): if key != "this": if isinstance(value, str): print("{} {}".format(key, value), file=ret) elif isinstance(value, list): print(_parse_config(value, key), file=ret) elif isinstance(value, dict): print(_parse_config(value, key), file=ret) print("</{}>".format(slot), file=ret) ret.seek(0) return ret.read()