Skip to content

Commit

Permalink
added nr.snmp execution module function, bump to version 0.14.0
Browse files Browse the repository at this point in the history
  • Loading branch information
dmulyalin committed Aug 14, 2022
1 parent bdca0f0 commit 74dd4c0
Show file tree
Hide file tree
Showing 12 changed files with 730 additions and 72 deletions.
168 changes: 127 additions & 41 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "salt_nornir"
version = "0.13.2"
version = "0.14.0"
description = "Salt-Nornir Proxy Minion SaltStack Modules"
authors = ["Denis Mulyalin <[email protected]>"]
maintainers = ["Denis Mulyalin <[email protected]>"]
Expand All @@ -25,7 +25,7 @@ classifiers = [
python = ">=3.7,<4.0"
nornir = "3.3.0"
pydantic = "1.9.1"
nornir_salt = "0.13.*"
nornir_salt = "0.14.*"
psutil = ">=5.8.*,<=5.9.*"

# optional dependencies for extras definition
Expand All @@ -43,6 +43,7 @@ paramiko = { version = "2.11.0", optional = true }
pyats = { version = "22.1", markers = "sys_platform != 'win32'", optional = true, extras = ['full'] }
pygnmi = { version = "0.8.4", optional = true }
pyyaml = { version = "6.0", optional = true }
puresnmp = { version = "2.0.0", optional = true, extras = ['crypto'] }
requests = { version = "2.27.1", optional = true }
scrapli = { version = "2022.1.30", optional = true }
scrapli-community = { version = "2022.1.30", optional = true }
Expand Down Expand Up @@ -113,6 +114,7 @@ prodmax = [
"pyats",
"pygnmi",
"pyyaml",
"puresnmp",
"requests",
"rich",
"scrapli",
Expand Down
97 changes: 95 additions & 2 deletions salt_nornir/modules/nornir_proxy_execution_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@
* - `worker`_
- Worker to use for task, supported values ``all`` or number from ``1`` to ``nornir_workers`` Proxy Minion parameter of default value 3
add_detals
++++++++++
add_details
+++++++++++
Controls Nornir-Salt
`ResultSerializer function <https://nornir-salt.readthedocs.io/en/latest/Functions/ResultSerializer.html#resultserializer>`_
Expand Down Expand Up @@ -893,6 +893,8 @@
+-----------------+---------------------------------------------------+--------------------+
| `nr.nornir`_ | Function to call Nornir Utility Functions | |
+-----------------+---------------------------------------------------+--------------------+
| `nr.snmp`_ | Function to manage devices over SNMP | puresnmp |
+-----------------+---------------------------------------------------+--------------------+
| `nr.task`_ | Function to run any Nornir task plugin | |
+-----------------+---------------------------------------------------+--------------------+
| `nr.test`_ | Function to test show commands output | netmiko (default), |
Expand Down Expand Up @@ -961,6 +963,11 @@
.. autofunction:: salt_nornir.modules.nornir_proxy_execution_module.nornir_fun
nr.snmp
+++++++
.. autofunction:: salt_nornir.modules.nornir_proxy_execution_module.snmp
nr.task
+++++++
Expand Down Expand Up @@ -1001,6 +1008,7 @@
model_exec_nr_nornir_fun,
model_exec_nr_gnmi,
model_exec_nr_do_action,
model_exec_nr_snmp,
)

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -2709,3 +2717,88 @@ def gnmi(call, *args, **kwargs):
return __proxy__["nornir.execute_job"](
task_fun=task_fun, kwargs=kwargs, identity=_form_identity(kwargs, "gnmi")
)


@ValidateFuncArgs(model_exec_nr_snmp)
def snmp(call, *args, **kwargs):
"""
Function to interact with devices using SNMP protocol utilizing one of supported plugins.
:param call: (str) (str) connection object method to call or name of one of extra methods
:param plugin: (str) Name of SNMP plugin to use - puresnmp (default)
:param method_name: (str) name of method to provide doc string for, used only by ``help`` call
:param kwargs: (dict) any additional keyword arguments to use with call method
:return: method call results
Available SNMP plugin names:
* ``puresnmp`` - ``nornir-salt`` built-in plugin that uses
`puresnmp library <https://github.com/exhuma/puresnmp>`_ to interact with devices.
Sample usage of ``puresnmp`` plugin, ``plugin="puresnmp"``::
salt nrp1 nr.snmp bulkget scalar_oids='["1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.1.2.0"]' repeating_oids='["1.3.6.1.2.1.3.1"]'
salt nrp1 nr.snmp bulktable oid="1.3.6.1.2.1.2.2.1"
salt nrp1 nr.snmp bulkwalk oids='["1.3.6.1.2.1.1.1", "1.3.6.1.2.1.1.5"]'
salt nrp1 nr.snmp get oid="1.3.6.1.2.1.1.1.0" FB="*"
salt nrp1 nr.snmp getnext oid="1.3.6.1.2.1.1.1.0"
salt nrp1 nr.snmp multiget oids='["1.3.6.1.2.1.1.1.0", "1.3.6.1.2.1.1.2.0"]'
salt nrp1 nr.snmp multiset mappings='{"1.3.6.1.2.1.1.4.0": "new contact value", "1.3.6.1.2.1.1.6.0": "new location"}'
salt nrp1 nr.snmp multiwalk oids='["1.3.6.1.2.1.1.1", "1.3.6.1.2.1.1.5"]'
salt nrp1 nr.snmp set oid="1.3.6.1.2.1.1.4.0" value="new contact value"
salt nrp1 nr.snmp table oid="1.3.6.1.2.1.2.2.1"
salt nrp1 nr.snmp walk oid="1.3.6.1.2.1.1"
By default ``oid`` and ``oids`` arguments rendered and can be sourced from
host inventory::
salt nrp1 nr.snmp get oid="{{ host.oid.get_os }}"
salt nrp1 nr.snmp multiget oids='["{{ host.oid.get_os }}", "{{ host.oid.get_hostname }}"]'
Where ``host.oid.get_os`` sourced from Nornir inventory::
hosts:
ceos1:
groups: [lab]
groups:
lab:
data:
oid:
get_os: "1.3.6.1.2.1.1.1.0"
get_hostname: "1.3.6.1.2.1.1.5.0"
**Extra Call Methods**
* ``dir`` - returns methods supported by plugin connection object::
salt nrp1 nr.snmp dir plugin=puresnmp
* ``help`` - returns ``method_name`` doc string::
salt nrp1 nr.snmp help method_name=set
Sample Python API usage from Salt-Master::
import salt.client
client = salt.client.LocalClient()
task_result = client.cmd(
tgt="nrp1",
fun="nr.snmp",
arg=["get"],
kwarg={"oid": "1.3.6.1.2.1.1.1.0"},
)
"""
plugin = kwargs.pop("plugin", "puresnmp")
kwargs["call"] = call
kwargs["render"] = ["oid", "oids"]

# decide on task plugin to use
if plugin.lower() == "puresnmp":
task_fun = "nornir_salt.plugins.tasks.puresnmp_call"
kwargs["connection_name"] = "puresnmp"

# run task
return __proxy__["nornir.execute_job"](
task_fun=task_fun, kwargs=kwargs, identity=_form_identity(kwargs, "snmp")
)
51 changes: 47 additions & 4 deletions salt_nornir/pydantic_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@ class Config:
@root_validator(pre=True)
def check_commands_given(cls, values):
assert (
values.get("args") or
values.get("commands") or
values.get("filename") or
values.get("config")
values.get("args")
or values.get("commands")
or values.get("filename")
or values.get("config")
), "No CLI commands, filename or config provided"
return values

Expand Down Expand Up @@ -459,6 +459,48 @@ def check_params_given(cls, values):
return values


class NrSNMPPlugins(str, Enum):
puresnmp = "puresnmp"


class model_exec_nr_snmp(ModelExecCommonArgs):
"""Model for salt_nornir.modules.nornir_proxy_execution_module.snmp function arguments"""

call: StrictStr
oid: Optional[StrictStr]
oids: Optional[List[StrictStr]]
mappings: Optional[Dict[StrictStr, Any]]
value: Optional[Union[StrictStr]]
plugin: Optional[NrSNMPPlugins]
bulk_size: Optional[StrictInt]
scalar_oids: Optional[List[StrictStr]]
repeating_oids: Optional[List[StrictStr]]
method_name: Optional[StrictStr]

class Config:
arbitrary_types_allowed = True
extra = "allow"

@root_validator(pre=True)
def check_params_given(cls, values):
call = values.get("call")
if not call:
raise CommandExecutionError("No 'call' argument provided")
if call == "help" and "method_name" not in values:
raise CommandExecutionError("'help' requires 'method_name' argument")
# check plugin specific arguments presence
if values.get("plugin", "puresnmp") == "puresnmp":
if call == "multiset":
assert (
"mappings" in values
), "Method 'multiset' requires 'mappings' argument"
elif call in ["get", "getnext", "walk", "table", "bulktable", "set"]:
assert "oid" in values, f"Method '{call}' requires 'oid' argument"
elif call in ["multiget", "multiwalk", "bulkwalk"]:
assert "oids" in values, f"Method '{call}' requires 'oids' argument"
return values


class StateWorkflowOptions(BaseModel):
fail_if_any_host_fail_any_step: Optional[List[StrictStr]]
fail_if_any_host_fail_all_step: Optional[List[StrictStr]]
Expand Down Expand Up @@ -724,6 +766,7 @@ class SaltNornirExecutionFunctions(BaseModel):
diff: model_exec_nr_diff
nornir_fun: model_exec_nr_nornir_fun
gnmi: model_exec_nr_gnmi
snmp: model_exec_nr_snmp


class SaltNornirStateFunctions(BaseModel):
Expand Down
17 changes: 9 additions & 8 deletions salt_nornir/runners/nornir_proxy_runner_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,31 +1041,32 @@ def diagram(*args, **kwargs):
Function to retrieve output from devices and produce diagram using N2G library.
This function depends on N2G, TTP and TTP-Templates libraries to obtain list of
per-platfrom commands to retrieve from devices, parse output and build diagram.
per-platform commands to retrieve from devices, parse output and build diagram.
Alternativly, instead of getting show commands output from devices, ``nr.diagram``
Alternatively, instead of getting show commands output from devices, ``nr.diagram``
can retrieve previously saved show commands output using ``nr.file read`` execution
module function if ``filegroup`` name provided.
:param data_plugin: (str) data plugin name to use to process output from devices
:param diagram_plugin: (str) N2G diagram plugin name - ``yed``, ``drawio``, ``v3d``
:param outfile: (str) OS path to save diagram file, default is
``./Output/{data plugin name}_{curent time}.{diagram plugin extension}``
:param save_data: (bool, str) if True, saves commands otput results retrieve from devices
in "Data" folder next to diagram file, if ``save_dat``a is a string, it must be an OS path
:param outfile: (str) OS path to save diagram file, default
is ``./Output/{data plugin name}_{current time}.{diagram plugin extension}``
:param save_data: (bool, str) if True, saves commands output results retrieve from devices
in "Data" folder next to diagram file, if ``save_dat`` a is a string, it must be an OS path
to folder where to save devices output. This is useful during troubleshooting to be able
to check what output devices return.
:param cli: (dict) arguments for ``nr.cli`` execution module function to get devices output
:param filegroup: (str) ``filegroup`` argument value for ``nr.file read`` function to
retrieve previously saved devices show commands output
:param last: (int) ``last`` argument value for ``nr.file read`` function, default value is 1
:param Fx: (str) Nornir filter functions to filter list of devices (hosts) to get output from
:param tgt: (str) SaltStack Nornir Proxy Minions to target, targets all of them by default - ``proxy:proxytype:nornir``
:param tgt: (str) SaltStack Nornir Proxy Minions to target, targets all of them by
default - ``proxy:proxytype:nornir``
:param tgt_type: (str) SaltStack targeting type to use, default is ``pillar``
:param job_retry: (int) how many times to retry if no results returned from all minions, default 0
:param job_timeout: (int) seconds to wait for results from minions before retry, default 300s
:param progress: progress display type to use - bars, raw, log, if False, no progress displayed
:param **kwargs: any additional arguments to use with
:param kwargs: any additional arguments to use with
`N2G data plugins <https://n2g.readthedocs.io/en/latest/data_plugins/index.html>`_
N2G ``data_plugin`` names and details:
Expand Down
12 changes: 9 additions & 3 deletions salt_nornir/states/nornir_proxy_state_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ def _run_workflow_step(
result = __salt__[step["function"]](
identity=_form_identity("workflow.{}.{}".format(state_name, step["name"])),
*step.get("args", []),
**step["kwargs"]
**step["kwargs"],
)
log.debug("state:nr.workflow: step '{}'; result:\n {}".format(step, result))

Expand Down Expand Up @@ -853,12 +853,18 @@ def workflow(*args, **kwargs):
# clean up cached data
if hcache:
_ = __salt__["nr.nornir"](
"clear_hcache", cache_keys=steps_names, identity=_form_identity("workflow"), FL=all_hosts
"clear_hcache",
cache_keys=steps_names,
identity=_form_identity("workflow"),
FL=all_hosts,
)
log.info("state:nr.workflow: cleaned steps' hcache")
if dcache:
_ = __salt__["nr.nornir"](
"clear_dcache", cache_keys=steps_names, identity=_form_identity("workflow"), FL=all_hosts
"clear_dcache",
cache_keys=steps_names,
identity=_form_identity("workflow"),
FL=all_hosts,
)
log.info("state:nr.workflow: cleaned steps' dcache")

Expand Down
2 changes: 1 addition & 1 deletion test/Dockerfile.rocky.py39.master
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM rockylinux:8.5

# install python and other libs
RUN dnf install -y python3.9 nano tree
RUN dnf install -y python3.9 nano tree gcc python39-devel

# do python libs and salt installation
RUN python3 -m pip install pip setuptools wheel poetry --upgrade
Expand Down
2 changes: 1 addition & 1 deletion test/Dockerfile.rocky.py39.minion
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM rockylinux:8.5

# install misc libs
RUN dnf install -y python3.9 nano tree openssh-server openssh-clients
RUN dnf install -y python3.9 nano tree openssh-server openssh-clients gcc python39-devel

# do python libs installation
RUN python3 -m pip install pip setuptools wheel --upgrade
Expand Down
7 changes: 7 additions & 0 deletions test/ceos1.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,11 @@ no ip routing
ntp server 1.1.1.10
ntp server 1.1.1.11
!
snmp-server community public rw
snmp-server view snmpv3 1 included
snmp-server view snmpv3 system included
snmp-server view snmpv3 iso included
snmp-server group snmpview v3 priv write snmpv3
snmp-server user snmpv3_user snmpview v3 auth md5 auth_pass priv des priv_pass
!
end
7 changes: 7 additions & 0 deletions test/ceos2.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,11 @@ no ip routing
!
ntp server 1.1.1.10
!
snmp-server community public rw
snmp-server view snmpv3 1 included
snmp-server view snmpv3 system included
snmp-server view snmpv3 iso included
snmp-server group snmpview v3 priv write snmpv3
snmp-server user snmpv3_user snmpview v3 auth md5 auth_pass priv des priv_pass
!
end
Loading

0 comments on commit 74dd4c0

Please sign in to comment.