def multi_call(*methods, **kwargs): ''' Invoke multiple Netmiko methods at once, and return their output, as list. methods A list of dictionaries with the following keys: - ``name``: the name of the Netmiko method to be executed. - ``args``: list of arguments to be sent to the Netmiko method. - ``kwargs``: dictionary of arguments to be sent to the Netmiko method. kwargs Key-value dictionary with the connection details (when not running under a Proxy Minion). ''' kwargs = clean_kwargs(**kwargs) if 'netmiko.conn' in __proxy__: conn = __proxy__['netmiko.conn']() else: conn, kwargs = _prepare_connection(**kwargs) ret = [] for method in methods: # Explicit unpacking method_name = method['name'] method_args = method.get('args', []) method_kwargs = method.get('kwargs', []) ret.append(getattr(conn, method_name)(*method_args, **method_kwargs)) if 'netmiko.conn' not in __proxy__: conn.disconnect() return ret
def get_connection(**kwargs): ''' Return the connection object to the pyeapi Node. .. warning:: This function returns an unserializable object, hence it is not meant to be used on the CLI. This should mainly be used when invoked from other modules for the low level connection with the network device. kwargs Key-value dictionary with the authentication details. USAGE Example: .. code-block:: python conn = __salt__['pyeapi.get_connection'](host='router1.example.com', username='******', password='******') show_ver = conn.run_commands(['show version', 'show interfaces']) ''' kwargs = clean_kwargs(**kwargs) if 'pyeapi.conn' in __proxy__: return __proxy__['pyeapi.conn']() conn, kwargs = _prepare_connection(**kwargs) return conn
def replace(old_value, new_value, full_match=False, **kwargs): """ Replace string or full line matches in switch's running config. If full_match is set to True, then the whole line will need to be matched as part of the old value. .. code-block:: bash salt '*' nxos.replace 'TESTSTRINGHERE' 'NEWTESTSTRINGHERE' """ if full_match is False: matcher = re.compile("^.*{}.*$".format(re.escape(old_value)), re.MULTILINE) repl = re.compile(re.escape(old_value)) else: matcher = re.compile(old_value, re.MULTILINE) repl = re.compile(old_value) lines = {"old": [], "new": []} for line in matcher.finditer(show_run()): lines["old"].append(line.group(0)) lines["new"].append(repl.sub(new_value, line.group(0))) kwargs = clean_kwargs(**kwargs) if lines["old"]: delete_config(lines["old"], **kwargs) if lines["new"]: config(lines["new"], **kwargs) return lines
def _prepare_connection(**nxos_api_kwargs): """ Prepare the connection with the remote network device, and clean up the key value pairs, removing the args used for the connection init. """ nxos_api_kwargs = clean_kwargs(**nxos_api_kwargs) init_kwargs = {} # Clean up any arguments that are not required for karg, warg in nxos_api_kwargs.items(): if karg in RPC_INIT_KWARGS: init_kwargs[karg] = warg if "host" not in init_kwargs: init_kwargs["host"] = "localhost" if "transport" not in init_kwargs: init_kwargs["transport"] = "https" if "port" not in init_kwargs: init_kwargs["port"] = 80 if init_kwargs["transport"] == "http" else 443 verify = init_kwargs.get("verify", True) if isinstance(verify, bool): init_kwargs["verify_ssl"] = verify else: init_kwargs["ca_bundle"] = verify if "rpc_version" not in init_kwargs: init_kwargs["rpc_version"] = "2.0" if "timeout" not in init_kwargs: init_kwargs["timeout"] = 60 return init_kwargs
def set_password( username, password, encrypted=False, role=None, crypt_salt=None, algorithm="sha256", **kwargs ): """ Set users password on switch. username Username to configure password Password to configure for username encrypted Whether or not to encrypt the password Default: False role Configure role for the username Default: None crypt_salt Configure crypt_salt setting Default: None algorithm Encryption algorithm Default: sha256 save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.set_password admin TestPass salt '*' nxos.set_password admin \\ password='******' \\ encrypted=True """ if algorithm == "blowfish": raise SaltInvocationError("Hash algorithm requested isn't available on nxos") get_user(username, **kwargs) # verify user exists if encrypted is False: hashed_pass = gen_hash( crypt_salt=crypt_salt, password=password, algorithm=algorithm ) else: hashed_pass = password password_line = "username {} password 5 {}".format(username, hashed_pass) if role is not None: password_line += " role {}".format(role) kwargs = clean_kwargs(**kwargs) return config(password_line, **kwargs)
def add_config(lines, **kwargs): """ Add one or more config lines to the NX-OS device running config. lines Configuration lines to add save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.add_config 'snmp-server community TESTSTRINGHERE group network-operator' .. note:: For more than one config added per command, lines should be a list. """ warn_until( "Argon", "'nxos.add_config lines' is deprecated in favor of 'nxos.config commands'", ) kwargs = clean_kwargs(**kwargs) return config(lines, **kwargs)
def get_connection(**kwargs): """ Return the Netmiko connection object. .. warning:: This function returns an unserializable object, hence it is not meant to be used on the CLI. This should mainly be used when invoked from other modules for the low level connection with the network device. kwargs Key-value dictionary with the authentication details. USAGE Example: .. code-block:: python conn = __salt__['netmiko.get_connection'](host='router1.example.com', username='******', password='******') show_if = conn.send_command('show interfaces') conn.disconnect() """ kwargs = clean_kwargs(**kwargs) if "netmiko.conn" in __proxy__: return __proxy__["netmiko.conn"]() conn, kwargs = _prepare_connection(**kwargs) return conn
def __init__(self, **nxos_kwargs): """ Initialize NxapiClient() connection object. By default this connects to the local unix domain socket (UDS). If http(s) is required to connect to a remote device then nxos_kwargs['host'], nxos_kwargs['username'], nxos_kwargs['password'], nxos_kwargs['transport'], nxos_kwargs['port'], parameters must be provided. """ self.nxargs = self._prepare_conn_args(clean_kwargs(**nxos_kwargs)) # Default: Connect to unix domain socket on localhost. if self.nxargs["connect_over_uds"]: if not os.path.exists(self.NXAPI_UDS): raise NxosClientError( "No host specified and no UDS found at {}\n".format( self.NXAPI_UDS)) # Create UHTTPConnection object for NX-API communication over UDS. log.info("Nxapi connection arguments: %s", self.nxargs) log.info("Connecting over unix domain socket") self.connection = UHTTPConnection(self.NXAPI_UDS) else: # Remote connection - Proxy Minion, connect over http(s) log.info("Nxapi connection arguments: %s", self.nxargs) log.info("Connecting over %s", self.nxargs["transport"]) self.connection = salt.utils.http.query
def delete_config(lines, **kwargs): """ Delete one or more config lines to the switch running config. lines Configuration lines to remove. save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.delete_config 'snmp-server community TESTSTRINGHERE group network-operator' .. note:: For more than one config deleted per command, lines should be a list. """ if not isinstance(lines, list): lines = [lines] for i, _ in enumerate(lines): lines[i] = "no " + lines[i] result = None try: kwargs = clean_kwargs(**kwargs) result = config(lines, **kwargs) except CommandExecutionError as e: # Some commands will generate error code 400 if they do not exist # and we try to remove them. These can be ignored. if ast.literal_eval(e.message)["code"] != "400": raise return result
def _prepare_connection(**nxos_api_kwargs): ''' Prepare the connection with the remote network device, and clean up the key value pairs, removing the args used for the connection init. ''' nxos_api_kwargs = clean_kwargs(**nxos_api_kwargs) init_kwargs = {} # Clean up any arguments that are not required for karg, warg in six.iteritems(nxos_api_kwargs): if karg in RPC_INIT_KWARGS: init_kwargs[karg] = warg if 'host' not in init_kwargs: init_kwargs['host'] = 'localhost' if 'transport' not in init_kwargs: init_kwargs['transport'] = 'https' if 'port' not in init_kwargs: init_kwargs['port'] = 80 if init_kwargs['transport'] == 'http' else 443 verify = init_kwargs.get('verify', True) if isinstance(verify, bool): init_kwargs['verify_ssl'] = verify else: init_kwargs['ca_bundle'] = verify if 'rpc_version' not in init_kwargs: init_kwargs['rpc_version'] = '2.0' if 'timeout' not in init_kwargs: init_kwargs['timeout'] = 60 return init_kwargs
def call(method, *args, **kwargs): """ Calls an arbitrary netmiko method. """ kwargs = clean_kwargs(**kwargs) connection_timeout = __context__["netmiko_device"]["connection_timeout"] with connection(connection_timeout) as con: return getattr(con, method)(*args, **kwargs)
def call(method, *args, **kwargs): ''' Calls an arbitrary netmiko method. ''' kwargs = clean_kwargs(**kwargs) if not netmiko_device['always_alive']: connection = ConnectHandler(**netmiko_device['args']) ret = getattr(connection, method)(*args, **kwargs) connection.disconnect() return ret return getattr(netmiko_device['connection'], method)(*args, **kwargs)
def call(method, *args, **kwargs): """ Calls an arbitrary netmiko method. """ kwargs = clean_kwargs(**kwargs) if not netmiko_device["always_alive"]: connection = ConnectHandler(**netmiko_device["args"]) ret = getattr(connection, method)(*args, **kwargs) connection.disconnect() return ret return getattr(netmiko_device["connection"], method)(*args, **kwargs)
def _sendline_ssh(commands, timeout=None, **kwargs): if isinstance(commands, str): commands = [commands] command = " ; ".join(commands) if _ping_ssh() is False: _init_ssh() out, err = DEVICE_DETAILS[_worker_name()].sendline(command) _, out = out.split("\n", 1) out, _, _ = out.rpartition("\n") kwargs = clean_kwargs(**kwargs) _parse_output_for_errors(out, command, **kwargs) return out
def filter(app, endpoint, **kwargs): ''' Get a list of items from NetBox. .. code-block:: bash salt myminion netbox.filter dcim devices status=1 role=router ''' ret = [] nb = _nb_obj(auth_required=True if app in AUTH_ENDPOINTS else False) nb_query = getattr(getattr(nb, app), endpoint).filter(**clean_kwargs(**kwargs)) if nb_query: ret = [_strip_url_field(dict(i)) for i in nb_query] return sorted(ret)
def freeze(name=None, force=False, **kwargs): ''' Save the list of package and repos in a freeze file. As this module is build on top of the pkg module, the user can send extra attributes to the underlying pkg module via kwargs. This function will call ``pkg.list_pkgs`` and ``pkg.list_repos``, and any additional arguments will be passed through to those functions. name Name of the frozen state. Optional. force If true, overwrite the state. Optional. CLI Example: .. code-block:: bash salt '*' freezer.freeze salt '*' freezer.freeze pre_install salt '*' freezer.freeze force=True root=/chroot ''' states_path = _states_path() try: if not os.path.exists(states_path): os.makedirs(states_path) except OSError as e: msg = 'Error when trying to create the freezer storage %s: %s' log.error(msg, states_path, e) raise CommandExecutionError(msg % (states_path, e)) if status(name) and not force: raise CommandExecutionError('The state is already present. Use ' 'force parameter to overwrite.') safe_kwargs = clean_kwargs(**kwargs) pkgs = __salt__['pkg.list_pkgs'](**safe_kwargs) repos = __salt__['pkg.list_repos'](**safe_kwargs) for fname, content in zip(_paths(name), (pkgs, repos)): with fopen(fname, 'w') as fp: json.dump(content, fp) return True
def remove_user(username, **kwargs): """ Remove user from switch. username Username to remove save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.remove_user username=daniel """ user_line = "no username {}".format(username) kwargs = clean_kwargs(**kwargs) return config(user_line, **kwargs)
def _get_endpoint(endpoint, id=None, **kwargs): username, password = _get_auth(kwargs.pop("username", None), kwargs.pop("password", None)) kwargs = clean_kwargs(**kwargs) url = _build_url(endpoint, id=id) ret = {"comment": "", "result": True, "out": None} res = salt.utils.http.query( url, method="GET", decode=True, username=username, password=password, params=kwargs, ) if "error" in res: ret.update({"result": False, "comment": res["error"]}) return ret ret["out"] = res["dict"]["data"] return ret
def call(method, *args, **kwargs): ''' Invoke an arbitrary Netmiko method. method The name of the Netmiko method to invoke. args A list of arguments to send to the method invoked. kwargs Key-value dictionary to send to the method invoked. ''' kwargs = clean_kwargs(**kwargs) if 'netmiko.call' in __proxy__: return __proxy__['netmiko.call'](method, *args, **kwargs) conn, kwargs = _prepare_connection(**kwargs) ret = getattr(conn, method)(*args, **kwargs) conn.disconnect() return ret
def multi_call(*methods, **kwargs): """ Invoke multiple Netmiko methods at once, and return their output, as list. methods A list of dictionaries with the following keys: - ``name``: the name of the Netmiko method to be executed. - ``args``: list of arguments to be sent to the Netmiko method. - ``kwargs``: dictionary of arguments to be sent to the Netmiko method. kwargs Key-value dictionary with the connection details (when not running under a Proxy Minion). CLI Example: .. code-block:: bash salt '*' netmiko.multi_call "{'name': 'enable', 'args': ['sudo su']}" "{'name': 'send_command', 'kwargs': {'command_string': 'whoami'}}" """ kwargs = clean_kwargs(**kwargs) if "netmiko.conn" in __proxy__: conn = __proxy__["netmiko.conn"]() else: conn, kwargs = _prepare_connection(**kwargs) ret = [] for method in methods: # Explicit unpacking method_name = method["name"] method_args = method.get("args", []) method_kwargs = method.get("kwargs", {}) if "netmiko.call" in __proxy__: ret.append(__proxy__["netmiko.call"](method_name, *method_args, **method_kwargs)) else: ret.append( getattr(conn, method_name)(*method_args, **method_kwargs)) if "netmiko.conn" not in __proxy__: conn.disconnect() return ret
def set_role(username, role, **kwargs): """ Assign role to username. username Username for role configuration role Configure role for username save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.set_role username=daniel role=vdc-admin. """ role_line = "username {} role {}".format(username, role) kwargs = clean_kwargs(**kwargs) return config(role_line, **kwargs)
def unset_role(username, role, **kwargs): """ Remove role from username. username Username for role removal role Role to remove save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True .. code-block:: bash salt '*' nxos.unset_role username=daniel role=vdc-admin """ role_line = "no username {} role {}".format(username, role) kwargs = clean_kwargs(**kwargs) return config(role_line, **kwargs)
def get(app, endpoint, id=None, **kwargs): ''' Get a single item from NetBox. To get an item based on ID. .. code-block:: bash salt myminion netbox.get dcim devices id=123 Or using named arguments that correspond with accepted filters on the NetBox endpoint. .. code-block:: bash salt myminion netbox.get dcim devices name=my-router ''' nb = _nb_obj(auth_required=True if app in AUTH_ENDPOINTS else False) if id: return dict(getattr(getattr(nb, app), endpoint).get(id)) else: return dict( getattr(getattr(nb, app), endpoint).get(**clean_kwargs(**kwargs)))
def _get_endpoint(endpoint, id=None, **kwargs): username, password = _get_auth(kwargs.pop('username', None), kwargs.pop('password', None)) kwargs = clean_kwargs(**kwargs) url = _build_url(endpoint, id=id) ret = { 'comment': '', 'result': True, 'out': None } res = salt.utils.http.query(url, method='GET', decode=True, username=username, password=password, params=kwargs) if 'error' in res: ret.update({ 'result': False, 'comment': res['error'] }) return ret ret['out'] = res['dict']['data'] return ret
def restore(name=None, clean=False, **kwargs): ''' Make sure that the system contains the packages and repos from a frozen state. Read the list of packages and repositories from the freeze file, and compare it with the current list of packages and repos. If there is any difference, all the missing packages are repos will be installed, and all the extra packages and repos will be removed. As this module is build on top of the pkg module, the user can send extra attributes to the underlying pkg module via kwargs. This function will call ``pkg.list_repos``, ``pkg.mod_repo``, ``pkg.list_pkgs``, ``pkg.install``, ``pkg.remove`` and ``pkg.del_repo``, and any additional arguments will be passed through to those functions. name Name of the frozen state. Optional. clean If True remove the frozen information YAML from the cache .. versionadded:: 3000 CLI Example: .. code-block:: bash salt '*' freezer.restore salt '*' freezer.restore root=/chroot ''' if not status(name): raise CommandExecutionError('Frozen state not found.') frozen_pkgs = {} frozen_repos = {} for fname, content in zip(_paths(name), (frozen_pkgs, frozen_repos)): with fopen(fname) as fp: content.update(json.load(fp)) # The ordering of removing or adding packages and repos can be # relevant, as maybe some missing package comes from a repo that # is also missing, so it cannot be installed. But can also happend # that a missing package comes from a repo that is present, but # will be removed. # # So the proposed order is; # - Add missing repos # - Add missing packages # - Remove extra packages # - Remove extra repos safe_kwargs = clean_kwargs(**kwargs) # Note that we expect that the information stored in list_XXX # match with the mod_XXX counterpart. If this is not the case the # recovery will be partial. ret = { 'pkgs': { 'add': [], 'remove': [] }, 'repos': { 'add': [], 'remove': [] }, 'comment': [], } _add_missing_repositories(frozen_repos, ret, **safe_kwargs) _add_missing_packages(frozen_pkgs, ret, **safe_kwargs) _remove_extra_packages(frozen_pkgs, ret, **safe_kwargs) _remove_extra_repositories(frozen_repos, ret, **safe_kwargs) # Clean the cached YAML files if clean and not ret['comment']: for fname in _paths(name): os.remove(fname) return ret
def send_config(config_file=None, config_commands=None, template_engine='jinja', commit=False, context=None, defaults=None, saltenv='base', **kwargs): ''' Send configuration commands down the SSH channel. Return the configuration lines sent to the device. The function is flexible to send the configuration from a local or remote file, or simply the commands as list. config_file The source file with the configuration commands to be sent to the device. The file can also be a template that can be rendered using the template engine of choice. This can be specified using the absolute path to the file, or using one of the following URL schemes: - ``salt://``, to fetch the file from the Salt fileserver. - ``http://`` or ``https://`` - ``ftp://`` - ``s3://`` - ``swift://`` config_commands Multiple configuration commands to be sent to the device. .. note:: This argument is ignored when ``config_file`` is specified. template_engine: ``jinja`` The template engine to use when rendering the source file. Default: ``jinja``. To simply fetch the file without attempting to render, set this argument to ``None``. commit: ``False`` Commit the configuration changes before exiting the config mode. This option is by default disabled, as many platforms don't have this capability natively. context Variables to add to the template context. defaults Default values of the context_dict. exit_config_mode: ``True`` Determines whether or not to exit config mode after complete. delay_factor: ``1`` Factor to adjust delays. max_loops: ``150`` Controls wait time in conjunction with delay_factor (default: ``150``). strip_prompt: ``False`` Determines whether or not to strip the prompt (default: ``False``). strip_command: ``False`` Determines whether or not to strip the command (default: ``False``). config_mode_command The command to enter into config mode. CLI Example: .. code-block:: bash salt '*' netmiko.send_config config_commands="['interface GigabitEthernet3', 'no ip address']" salt '*' netmiko.send_config config_commands="['snmp-server location {{ grains.location }}']" salt '*' netmiko.send_config config_file=salt://config.txt salt '*' netmiko.send_config config_file=https://bit.ly/2sgljCB device_type='cisco_ios' ip='1.2.3.4' username='******' ''' if config_file: file_str = __salt__['cp.get_file_str'](config_file, saltenv=saltenv) if file_str is False: raise CommandExecutionError('Source file {} not found'.format(config_file)) elif config_commands: if isinstance(config_commands, (six.string_types, six.text_type)): config_commands = [config_commands] file_str = '\n'.join(config_commands) # unify all the commands in a single file, to render them in a go if template_engine: file_str = __salt__['file.apply_template_on_contents'](file_str, template_engine, context, defaults, saltenv) # whatever the source of the commands would be, split them line by line config_commands = [line for line in file_str.splitlines() if line.strip()] kwargs = clean_kwargs(**kwargs) if 'netmiko.conn' in __proxy__: conn = __proxy__['netmiko.conn']() else: conn, kwargs = _prepare_connection(**kwargs) if commit: kwargs['exit_config_mode'] = False # don't exit config mode after # loading the commands, wait for explicit commit ret = conn.send_config_set(config_commands=config_commands, **kwargs) if commit: ret += conn.commit() return ret
def send_config(config_file=None, config_commands=None, template_engine="jinja", commit=False, context=None, defaults=None, saltenv="base", **kwargs): """ Send configuration commands down the SSH channel. Return the configuration lines sent to the device. The function is flexible to send the configuration from a local or remote file, or simply the commands as list. config_file The source file with the configuration commands to be sent to the device. The file can also be a template that can be rendered using the template engine of choice. This can be specified using the absolute path to the file, or using one of the following URL schemes: - ``salt://``, to fetch the file from the Salt fileserver. - ``http://`` or ``https://`` - ``ftp://`` - ``s3://`` - ``swift://`` config_commands Multiple configuration commands to be sent to the device. .. note:: This argument is ignored when ``config_file`` is specified. template_engine: ``jinja`` The template engine to use when rendering the source file. Default: ``jinja``. To simply fetch the file without attempting to render, set this argument to ``None``. commit: ``False`` Commit the configuration changes before exiting the config mode. This option is by default disabled, as many platforms don't have this capability natively. context Variables to add to the template context. defaults Default values of the context_dict. exit_config_mode: ``True`` Determines whether or not to exit config mode after complete. delay_factor: ``1`` Factor to adjust delays. max_loops: ``150`` Controls wait time in conjunction with delay_factor (default: ``150``). strip_prompt: ``False`` Determines whether or not to strip the prompt (default: ``False``). strip_command: ``False`` Determines whether or not to strip the command (default: ``False``). config_mode_command The command to enter into config mode. CLI Example: .. code-block:: bash salt '*' netmiko.send_config config_commands="['interface GigabitEthernet3', 'no ip address']" salt '*' netmiko.send_config config_commands="['snmp-server location {{ grains.location }}']" salt '*' netmiko.send_config config_file=salt://config.txt salt '*' netmiko.send_config config_file=https://bit.ly/2sgljCB device_type='cisco_ios' ip='1.2.3.4' username='******' """ if config_file: file_str = __salt__["cp.get_file_str"](config_file, saltenv=saltenv) if file_str is False: raise CommandExecutionError( "Source file {} not found".format(config_file)) elif config_commands: if isinstance(config_commands, ((str, ), str)): config_commands = [config_commands] file_str = "\n".join(config_commands) # unify all the commands in a single file, to render them in a go if template_engine: file_str = __salt__["file.apply_template_on_contents"](file_str, template_engine, context, defaults, saltenv) # whatever the source of the commands would be, split them line by line config_commands = [line for line in file_str.splitlines() if line.strip()] kwargs = clean_kwargs(**kwargs) if "netmiko.conn" in __proxy__: if __proxy__["netmiko.conn"]().is_alive(): conn = __proxy__["netmiko.conn"]() else: conn, _ = _prepare_connection(**__proxy__["netmiko.args"]()) else: conn, kwargs = _prepare_connection(**kwargs) if commit: kwargs["exit_config_mode"] = False # don't exit config mode after # loading the commands, wait for explicit commit ret = conn.send_config_set(config_commands=config_commands, **kwargs) if commit: ret += conn.commit() return ret
def call(method, *args, **kwargs): ''' Calls an arbitrary pyeapi method. ''' kwargs = clean_kwargs(**kwargs) return getattr(pyeapi_device['connection'], method)(*args, **kwargs)
def call(method, *args, **kwargs): ''' Invoke an arbitrary pyeapi method. method The name of the pyeapi method to invoke. args A list of arguments to send to the method invoked. kwargs Key-value dictionary to send to the method invoked. transport: ``https`` Specifies the type of connection transport to use. Valid values for the connection are ``socket``, ``http_local``, ``http``, and ``https``. .. note:: This argument does not need to be specified when running in a :mod:`pyeapi <salt.proxy.arista_pyeapi>` Proxy Minion. host: ``localhost`` The IP address or DNS host name of the connection device. .. note:: This argument does not need to be specified when running in a :mod:`pyeapi <salt.proxy.arista_pyeapi>` Proxy Minion. username: ``admin`` The username to pass to the device to authenticate the eAPI connection. .. note:: This argument does not need to be specified when running in a :mod:`pyeapi <salt.proxy.arista_pyeapi>` Proxy Minion. password The password to pass to the device to authenticate the eAPI connection. .. note:: This argument does not need to be specified when running in a :mod:`pyeapi <salt.proxy.arista_pyeapi>` Proxy Minion. port The TCP port of the endpoint for the eAPI connection. If this keyword is not specified, the default value is automatically determined by the transport type (``80`` for ``http``, or ``443`` for ``https``). .. note:: This argument does not need to be specified when running in a :mod:`pyeapi <salt.proxy.arista_pyeapi>` Proxy Minion. enablepwd The enable mode password if required by the destination node. .. note:: This argument does not need to be specified when running in a :mod:`pyeapi <salt.proxy.arista_pyeapi>` Proxy Minion. CLI Example: .. code-block:: bash salt '*' pyeapi.call run_commands "['show version']" ''' kwargs = clean_kwargs(**kwargs) if 'pyeapi.call' in __proxy__: return __proxy__['pyeapi.call'](method, *args, **kwargs) conn, kwargs = _prepare_connection(**kwargs) ret = getattr(conn, method)(*args, **kwargs) return ret
def call(method, *args, **kwargs): """ Calls an arbitrary pyeapi method. """ kwargs = clean_kwargs(**kwargs) return getattr(pyeapi_device["connection"], method)(*args, **kwargs)
def restore(name=None, **kwargs): ''' Make sure that the system contains the packages and repos from a frozen state. Read the list of packages and repositories from the freeze file, and compare it with the current list of packages and repos. If there is any difference, all the missing packages are repos will be installed, and all the extra packages and repos will be removed. As this module is build on top of the pkg module, the user can send extra attributes to the underlying pkg module via kwargs. This function will call ``pkg.list_repos``, ``pkg.mod_repo``, ``pkg.list_pkgs``, ``pkg.install``, ``pkg.remove`` and ``pkg.del_repo``, and any additional arguments will be passed through to those functions. name Name of the frozen state. Optional. CLI Example: .. code-block:: bash salt '*' freezer.restore salt '*' freezer.restore root=/chroot ''' if not status(name): raise CommandExecutionError('Frozen state not found.') frozen_pkgs = {} frozen_repos = {} for name, content in zip(_paths(name), (frozen_pkgs, frozen_repos)): with fopen(name) as fp: content.update(json.load(fp)) # The ordering of removing or adding packages and repos can be # relevant, as maybe some missing package comes from a repo that # is also missing, so it cannot be installed. But can also happend # that a missing package comes from a repo that is present, but # will be removed. # # So the proposed order is; # - Add missing repos # - Add missing packages # - Remove extra packages # - Remove extra repos safe_kwargs = clean_kwargs(**kwargs) # Note that we expect that the information stored in list_XXX # match with the mod_XXX counterpart. If this is not the case the # recovery will be partial. res = { 'pkgs': { 'add': [], 'remove': [] }, 'repos': { 'add': [], 'remove': [] }, 'comment': [], } # Add missing repositories repos = __salt__['pkg.list_repos'](**safe_kwargs) missing_repos = set(frozen_repos) - set(repos) for repo in missing_repos: try: # In Python 2 we cannot do advance destructuring, so we # need to create a temporary dictionary that will merge # all the parameters _tmp_kwargs = frozen_repos[repo].copy() _tmp_kwargs.update(safe_kwargs) __salt__['pkg.mod_repo'](repo, **_tmp_kwargs) res['repos']['add'].append(repo) log.info('Added missing repository %s', repo) except Exception as e: msg = 'Error adding %s repository: %s' log.error(msg, repo, e) res['comment'].append(msg % (repo, e)) # Add missing packages # NOTE: we can remove the `for` using `pkgs`. This will improve # performance, but I want to have a more detalied report of what # packages are installed or failled. pkgs = __salt__['pkg.list_pkgs'](**safe_kwargs) missing_pkgs = set(frozen_pkgs) - set(pkgs) for pkg in missing_pkgs: try: __salt__['pkg.install'](name=pkg, **safe_kwargs) res['pkgs']['add'].append(pkg) log.info('Added missing package %s', pkg) except Exception as e: msg = 'Error adding %s package: %s' log.error(msg, pkg, e) res['comment'].append(msg % (pkg, e)) # Remove extra packages pkgs = __salt__['pkg.list_pkgs'](**safe_kwargs) extra_pkgs = set(pkgs) - set(frozen_pkgs) for pkg in extra_pkgs: try: __salt__['pkg.remove'](name=pkg, **safe_kwargs) res['pkgs']['remove'].append(pkg) log.info('Removed extra package %s', pkg) except Exception as e: msg = 'Error removing %s package: %s' log.error(msg, pkg, e) res['comment'].append(msg % (pkg, e)) # Remove extra repositories repos = __salt__['pkg.list_repos'](**safe_kwargs) extra_repos = set(repos) - set(frozen_repos) for repo in extra_repos: try: __salt__['pkg.del_repo'](repo, **safe_kwargs) res['repos']['remove'].append(repo) log.info('Removed extra repository %s', repo) except Exception as e: msg = 'Error removing %s repository: %s' log.error(msg, repo, e) res['comment'].append(msg % (repo, e)) return res
def config( commands=None, config_file=None, template_engine="jinja", context=None, defaults=None, saltenv="base", **kwargs ): """ Configures the Nexus switch with the specified commands. This method is used to send configuration commands to the switch. It will take either a string or a list and prepend the necessary commands to put the session into config mode. .. warning:: All the commands will be applied directly to the running-config. config_file The source file with the configuration commands to be sent to the device. The file can also be a template that can be rendered using the template engine of choice. This can be specified using the absolute path to the file, or using one of the following URL schemes: - ``salt://``, to fetch the file from the Salt fileserver. - ``http://`` or ``https://`` - ``ftp://`` - ``s3://`` - ``swift://`` commands The commands to send to the switch in config mode. If the commands argument is a string it will be cast to a list. The list of commands will also be prepended with the necessary commands to put the session in config mode. .. note:: This argument is ignored when ``config_file`` is specified. template_engine: ``jinja`` The template engine to use when rendering the source file. Default: ``jinja``. To simply fetch the file without attempting to render, set this argument to ``None``. context Variables to add to the template context. defaults Default values of the context_dict. save_config If False, don't save configuration commands to startup configuration. If True, save configuration to startup configuration. Default: True CLI Example: .. code-block:: bash salt '*' nxos.config commands="['spanning-tree mode mstp']" salt '*' nxos.config config_file=salt://config.txt salt '*' nxos.config config_file=https://bit.ly/2LGLcDy context="{'servers': ['1.2.3.4']}" """ kwargs = clean_kwargs(**kwargs) initial_config = sendline("show running-config", **kwargs) if isinstance(initial_config, list): initial_config = initial_config[0] if config_file: file_str = __salt__["cp.get_file_str"](config_file, saltenv=saltenv) if file_str is False: raise CommandExecutionError("Source file {} not found".format(config_file)) elif commands: if isinstance(commands, str): commands = [commands] file_str = "\n".join(commands) # unify all the commands in a single file, to render them in a go else: raise CommandExecutionError( "Either arg <config_file> or <commands> must be specified" ) if template_engine: file_str = __salt__["file.apply_template_on_contents"]( file_str, template_engine, context, defaults, saltenv ) # whatever the source of the commands would be, split them line by line commands = [line for line in file_str.splitlines() if line.strip()] try: config_result = _configure_device(commands, **kwargs) except socket_error as e: return e.strerror + "\n" + CONNECTION_ERROR_MSG except NxosError as e: return e.strerror + "\n" + CONNECTION_ERROR_MSG config_result = _parse_config_result(config_result) current_config = sendline("show running-config", **kwargs) if isinstance(current_config, list): current_config = current_config[0] diff = difflib.unified_diff( initial_config.splitlines(1)[4:], current_config.splitlines(1)[4:] ) clean_diff = "".join([x.replace("\r", "") for x in diff]) head = "COMMAND_LIST: " cc = config_result[0] cr = config_result[1] return head + cc + "\n" + cr + "\n" + clean_diff