salt/grains/napalm.py
# -*- coding: utf-8 -*-
'''
NAPALM Grains
=============
:codeauthor: Mircea Ulinic <ping@mirceaulinic.net>
:maturity: new
:depends: napalm
:platform: unix
Dependencies
------------
- :mod:`NAPALM proxy module <salt.proxies.napalm>`
.. versionadded:: 2016.11.0
'''
from __future__ import absolute_import, print_function, unicode_literals
import logging
log = logging.getLogger(__name__)
# Salt lib
import salt.utils.dns
import salt.utils.napalm
# ----------------------------------------------------------------------------------------------------------------------
# grains properties
# ----------------------------------------------------------------------------------------------------------------------
__virtualname__ = 'napalm'
__proxyenabled__ = ['napalm']
# ----------------------------------------------------------------------------------------------------------------------
# global variables
# ----------------------------------------------------------------------------------------------------------------------
GRAINS_CACHE = {}
DEVICE_CACHE = {}
_FORBIDDEN_OPT_ARGS = [
'secret', # used by IOS to enter in enable mode
'enable_password' # used by EOS
]
# ----------------------------------------------------------------------------------------------------------------------
# property functions
# ----------------------------------------------------------------------------------------------------------------------
def __virtual__():
'''
NAPALM library must be installed for this module to work and run in a (proxy) minion.
'''
return salt.utils.napalm.virtual(__opts__, __virtualname__, __file__)
# ----------------------------------------------------------------------------------------------------------------------
# helpers
# ----------------------------------------------------------------------------------------------------------------------
def _retrieve_grains_cache(proxy=None):
'''
Retrieves the grains from the network device if not cached already.
'''
global GRAINS_CACHE
if not GRAINS_CACHE:
if proxy and salt.utils.napalm.is_proxy(__opts__):
# if proxy var passed and is NAPALM-type proxy minion
GRAINS_CACHE = proxy['napalm.get_grains']()
elif not proxy and salt.utils.napalm.is_minion(__opts__):
# if proxy var not passed and is running in a straight minion
GRAINS_CACHE = salt.utils.napalm.call(
DEVICE_CACHE,
'get_facts',
**{}
)
return GRAINS_CACHE
def _retrieve_device_cache(proxy=None):
'''
Loads the network device details if not cached already.
'''
global DEVICE_CACHE
if not DEVICE_CACHE:
if proxy and salt.utils.napalm.is_proxy(__opts__):
# if proxy var passed and is NAPALM-type proxy minion
if 'napalm.get_device' in proxy:
DEVICE_CACHE = proxy['napalm.get_device']()
elif not proxy and salt.utils.napalm.is_minion(__opts__):
# if proxy var not passed and is running in a straight minion
DEVICE_CACHE = salt.utils.napalm.get_device(__opts__)
return DEVICE_CACHE
def _get_grain(name, proxy=None):
'''
Retrieves the grain value from the cached dictionary.
'''
grains = _retrieve_grains_cache(proxy=proxy)
if grains.get('result', False) and grains.get('out', {}):
return grains.get('out').get(name)
def _get_device_grain(name, proxy=None):
'''
Retrieves device-specific grains.
'''
device = _retrieve_device_cache(proxy=proxy)
return device.get(name.upper())
# ----------------------------------------------------------------------------------------------------------------------
# actual grains
# ----------------------------------------------------------------------------------------------------------------------
def getos(proxy=None):
'''
Returns the Operating System name running on the network device.
Example: junos, iosxr, eos, ios etc.
CLI Example - select all network devices running JunOS:
.. code-block:: bash
salt -G 'os:junos' test.ping
'''
return {'os': _get_device_grain('driver_name', proxy=proxy)}
def version(proxy=None):
'''
Returns the OS version.
Example: 13.3R6.5, 6.0.2 etc.
CLI Example - select all network devices running JunOS 13.3R6.5 and return the model:
.. code-block:: bash
salt -G 'os:junos and version:13.3R6.5' grains.get model
Output:
.. code-block:: yaml
edge01.bjm01:
MX2000
edge01.sjc01:
MX960
edge01.mrs01:
MX480
edge01.muc01:
MX240
'''
return {'version': _get_grain('os_version', proxy=proxy)}
def model(proxy=None):
'''
Returns the network device chassis model.
Example: MX480, ASR-9904-AC etc.
CLI Example - select all Juniper MX480 routers and execute traceroute to 8.8.8.8:
.. code-block:: bash
salt -G 'model:MX480' net.traceroute 8.8.8.8
'''
return {'model': _get_grain('model', proxy=proxy)}
def serial(proxy=None):
'''
Returns the chassis serial number.
Example: FOX1234W00F
CLI Example - select all devices whose serial number begins with `FOX` and display the serial number value:
.. code-block:: bash
salt -G 'serial:FOX*' grains.get serial
Output:
.. code-block:: yaml
edge01.icn01:
FOXW00F001
edge01.del01:
FOXW00F002
edge01.yyz01:
FOXW00F003
edge01.mrs01:
FOXW00F004
'''
return {'serial': _get_grain('serial_number', proxy=proxy)}
def vendor(proxy=None):
'''
Returns the network device vendor.
Example: juniper, cisco, arista etc.
CLI Example - select all devices produced by Cisco and shutdown:
.. code-block:: bash
salt -G 'vendor:cisco' net.cli "shut"
'''
return {'vendor': _get_grain('vendor', proxy=proxy)}
def uptime(proxy=None):
'''
Returns the uptime in seconds.
CLI Example - select all devices started/restarted within the last hour:
.. code-block:: bash
salt -G 'uptime<3600' test.ping
'''
return {'uptime': _get_grain('uptime', proxy=proxy)}
def interfaces(proxy=None):
'''
Returns the complete interfaces list of the network device.
Example: ['lc-0/0/0', 'pfe-0/0/0', 'xe-1/3/0', 'lo0', 'irb', 'demux0', 'fxp0']
CLI Example - select all devices that have a certain interface, e.g.: xe-1/1/1:
.. code-block:: bash
salt -G 'interfaces:xe-1/1/1' test.ping
Output:
.. code-block:: yaml
edge01.yyz01:
True
edge01.maa01:
True
edge01.syd01:
True
edge01.del01:
True
edge01.dus01:
True
edge01.kix01:
True
'''
return {'interfaces': _get_grain('interface_list', proxy=proxy)}
def username(proxy=None):
'''
Return the username.
.. versionadded:: 2017.7.0
CLI Example - select all devices using `foobar` as username for connection:
.. code-block:: bash
salt -G 'username:foobar' test.ping
Output:
.. code-block::yaml
device1:
True
device2:
True
'''
if proxy and salt.utils.napalm.is_proxy(__opts__):
# only if proxy will override the username
# otherwise will use the default Salt grains
return {'username': _get_device_grain('username', proxy=proxy)}
def hostname(proxy=None):
'''
Return the hostname as configured on the network device.
CLI Example:
.. code-block:: bash
salt 'device*' grains.get hostname
Output:
.. code-block:: yaml
device1:
edge01.yyz01
device2:
edge01.bjm01
device3:
edge01.flw01
'''
return {'hostname': _get_grain('hostname', proxy=proxy)}
def host(proxy=None):
'''
This grain is set by the NAPALM grain module
only when running in a proxy minion.
When Salt is installed directly on the network device,
thus running a regular minion, the ``host`` grain
provides the physical hostname of the network device,
as it would be on an ordinary minion server.
When running in a proxy minion, ``host`` points to the
value configured in the pillar: :mod:`NAPALM proxy module <salt.proxy.napalm>`.
.. note::
The diference between ``host`` and ``hostname`` is that
``host`` provides the physical location - either domain name or IP address,
while ``hostname`` provides the hostname as configured on the device.
They are not necessarily the same.
.. versionadded:: 2017.7.0
CLI Example:
.. code-block:: bash
salt 'device*' grains.get host
Output:
.. code-block:: yaml
device1:
ip-172-31-13-136.us-east-2.compute.internal
device2:
ip-172-31-11-193.us-east-2.compute.internal
device3:
ip-172-31-2-181.us-east-2.compute.internal
'''
if proxy and salt.utils.napalm.is_proxy(__opts__):
# this grain is set only when running in a proxy minion
# otherwise will use the default Salt grains
return {'host': _get_device_grain('hostname', proxy=proxy)}
def host_dns(proxy=None):
'''
Return the DNS information of the host.
This grain is a dictionary having two keys:
- ``A``
- ``AAAA``
.. note::
This grain is disabled by default, as the proxy startup may be slower
when the lookup fails.
The user can enable it using the ``napalm_host_dns_grain`` option (in
the pillar or proxy configuration file):
.. code-block:: yaml
napalm_host_dns_grain: true
.. versionadded:: 2017.7.0
CLI Example:
.. code-block:: bash
salt 'device*' grains.get host_dns
Output:
.. code-block:: yaml
device1:
A:
- 172.31.9.153
AAAA:
- fd52:188c:c068::1
device2:
A:
- 172.31.46.249
AAAA:
- fdca:3b17:31ab::17
device3:
A:
- 172.31.8.167
AAAA:
- fd0f:9fd6:5fab::1
'''
if not __opts__.get('napalm_host_dns_grain', False):
return
device_host = host(proxy=proxy)
if device_host:
device_host_value = device_host['host']
host_dns_ret = {
'host_dns': {
'A': [],
'AAAA': []
}
}
dns_a = salt.utils.dns.lookup(device_host_value, 'A')
if dns_a:
host_dns_ret['host_dns']['A'] = dns_a
dns_aaaa = salt.utils.dns.lookup(device_host_value, 'AAAA')
if dns_aaaa:
host_dns_ret['host_dns']['AAAA'] = dns_aaaa
return host_dns_ret
def optional_args(proxy=None):
'''
Return the connection optional args.
.. note::
Sensible data will not be returned.
.. versionadded:: 2017.7.0
CLI Example - select all devices connecting via port 1234:
.. code-block:: bash
salt -G 'optional_args:port:1234' test.ping
Output:
.. code-block:: yaml
device1:
True
device2:
True
'''
opt_args = _get_device_grain('optional_args', proxy=proxy) or {}
if opt_args and _FORBIDDEN_OPT_ARGS:
for arg in _FORBIDDEN_OPT_ARGS:
opt_args.pop(arg, None)
return {'optional_args': opt_args}