junos_facts refactor to work with resources modules (#58566)
* junos_facts refactor to work with resources modules * Refactor junos_facts module to work with network resource module. * Fix unit test failures * Fix review comments
This commit is contained in:
parent
6c5de9e6eb
commit
1e3034b96d
13 changed files with 396 additions and 307 deletions
|
@ -23,7 +23,7 @@ class FactsBase(object):
|
|||
self._connection = get_resource_connection(module)
|
||||
|
||||
self.ansible_facts = {'ansible_network_resources': {}}
|
||||
self.ansible_facts['ansible_gather_network_resources'] = list()
|
||||
self.ansible_facts['ansible_net_gather_network_resources'] = list()
|
||||
self.ansible_facts['ansible_net_gather_subset'] = list()
|
||||
|
||||
if not self._gather_subset:
|
||||
|
@ -65,7 +65,8 @@ class FactsBase(object):
|
|||
exclude = False
|
||||
|
||||
if subset not in valid_subsets:
|
||||
self._module.fail_json(msg='Bad subset')
|
||||
self._module.fail_json(msg='Subset must be one of [%s], got %s' %
|
||||
(', '.join(sorted([item for item in valid_subsets])), subset))
|
||||
|
||||
if exclude:
|
||||
exclude_subsets.add(subset)
|
||||
|
@ -75,7 +76,6 @@ class FactsBase(object):
|
|||
if not runable_subsets:
|
||||
runable_subsets.update(valid_subsets)
|
||||
runable_subsets.difference_update(exclude_subsets)
|
||||
|
||||
return runable_subsets
|
||||
|
||||
def get_network_resources_facts(self, net_res_choices, facts_resource_obj_map, resource_facts_type=None, data=None):
|
||||
|
@ -95,7 +95,7 @@ class FactsBase(object):
|
|||
|
||||
restorun_subsets = self.gen_runable(resource_facts_type, frozenset(net_res_choices))
|
||||
if restorun_subsets:
|
||||
self.ansible_facts['gather_network_resources'] = list(restorun_subsets)
|
||||
self.ansible_facts['ansible_net_gather_network_resources'] = list(restorun_subsets)
|
||||
instances = list()
|
||||
for key in restorun_subsets:
|
||||
fact_cls_obj = facts_resource_obj_map.get(key)
|
||||
|
@ -112,9 +112,10 @@ class FactsBase(object):
|
|||
legacy_facts_type = self._gather_subset
|
||||
|
||||
runable_subsets = self.gen_runable(legacy_facts_type, frozenset(fact_legacy_obj_map.keys()))
|
||||
runable_subsets.add('default')
|
||||
if runable_subsets:
|
||||
facts = dict()
|
||||
facts['gather_subset'] = list(runable_subsets)
|
||||
facts['ansible_net_gather_subset'] = list(runable_subsets)
|
||||
|
||||
instances = list()
|
||||
for key in runable_subsets:
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The arg spec for the junos facts module.
|
||||
"""
|
||||
CHOICES = [
|
||||
'all',
|
||||
]
|
||||
|
||||
|
||||
class FactsArgs(object):
|
||||
""" The arg spec for the junos facts module
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
pass
|
||||
|
||||
argument_spec = {
|
||||
'gather_subset': dict(default=['!config'], type='list'),
|
||||
'config_format': dict(default='text', choices=['xml', 'text', 'set', 'json']),
|
||||
'gather_network_resources': dict(choices=CHOICES, type='list'),
|
||||
}
|
0
lib/ansible/module_utils/network/junos/facts/__init__.py
Normal file
0
lib/ansible/module_utils/network/junos/facts/__init__.py
Normal file
64
lib/ansible/module_utils/network/junos/facts/facts.py
Normal file
64
lib/ansible/module_utils/network/junos/facts/facts.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The facts class for junos
|
||||
this file validates each subset of facts and selectively
|
||||
calls the appropriate facts gathering function
|
||||
"""
|
||||
|
||||
from ansible.module_utils.network.junos.argspec.facts.facts import FactsArgs
|
||||
from ansible.module_utils.network.common.facts.facts import FactsBase
|
||||
from ansible.module_utils.network.junos.facts.legacy.base import Default, Hardware, Config, Interfaces, OFacts, HAS_PYEZ
|
||||
|
||||
FACT_LEGACY_SUBSETS = dict(
|
||||
default=Default,
|
||||
hardware=Hardware,
|
||||
config=Config,
|
||||
interfaces=Interfaces,
|
||||
)
|
||||
FACT_RESOURCE_SUBSETS = dict(
|
||||
)
|
||||
|
||||
|
||||
class Facts(FactsBase):
|
||||
""" The fact class for junos
|
||||
"""
|
||||
|
||||
VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys())
|
||||
VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys())
|
||||
|
||||
def __init__(self, module):
|
||||
super(Facts, self).__init__(module)
|
||||
|
||||
def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None):
|
||||
""" Collect the facts for junos
|
||||
:param legacy_facts_type: List of legacy facts types
|
||||
:param resource_facts_type: List of resource fact types
|
||||
:param data: previously collected conf
|
||||
:rtype: dict
|
||||
:return: the facts gathered
|
||||
"""
|
||||
netres_choices = FactsArgs.argument_spec['gather_network_resources'].get('choices', [])
|
||||
if self.VALID_RESOURCE_SUBSETS:
|
||||
self.get_network_resources_facts(netres_choices, FACT_RESOURCE_SUBSETS, resource_facts_type, data)
|
||||
|
||||
if not legacy_facts_type:
|
||||
legacy_facts_type = self._gather_subset
|
||||
# fetch old style facts only when explicitly mentioned in gather_subset option
|
||||
if 'ofacts' in legacy_facts_type:
|
||||
if HAS_PYEZ:
|
||||
self.ansible_facts.update(OFacts(self._module).populate())
|
||||
else:
|
||||
self._warnings.extend([
|
||||
'junos-eznc is required to gather old style facts but does not appear to be installed. '
|
||||
'It can be installed using `pip install junos-eznc`'])
|
||||
self.ansible_facts['ansible_net_gather_subset'].append('ofacts')
|
||||
legacy_facts_type.remove('ofacts')
|
||||
|
||||
if self.VALID_LEGACY_GATHER_SUBSETS:
|
||||
self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type)
|
||||
|
||||
return self.ansible_facts, self._warnings
|
226
lib/ansible/module_utils/network/junos/facts/legacy/base.py
Normal file
226
lib/ansible/module_utils/network/junos/facts/legacy/base.py
Normal file
|
@ -0,0 +1,226 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
"""
|
||||
The junos interfaces fact class
|
||||
It is in this file the configuration is collected from the device
|
||||
for a given resource, parsed, and the facts tree is populated
|
||||
based on the configuration.
|
||||
"""
|
||||
import platform
|
||||
|
||||
from ansible.module_utils.network.common.netconf import exec_rpc
|
||||
from ansible.module_utils.network.junos.junos import get_param, tostring
|
||||
from ansible.module_utils.network.junos.junos import get_configuration, get_capabilities
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
try:
|
||||
from jnpr.junos import Device
|
||||
from jnpr.junos.exception import ConnectError
|
||||
HAS_PYEZ = True
|
||||
except ImportError:
|
||||
HAS_PYEZ = False
|
||||
|
||||
|
||||
class FactsBase(object):
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.facts = dict()
|
||||
self.warnings = []
|
||||
|
||||
def populate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def cli(self, command):
|
||||
reply = command(self.module, command)
|
||||
output = reply.find('.//output')
|
||||
if not output:
|
||||
self.module.fail_json(msg='failed to retrieve facts for command %s' % command)
|
||||
return str(output.text).strip()
|
||||
|
||||
def rpc(self, rpc):
|
||||
return exec_rpc(self.module, tostring(Element(rpc)))
|
||||
|
||||
def get_text(self, ele, tag):
|
||||
try:
|
||||
return str(ele.find(tag).text).strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
class Default(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
self.facts.update(self.platform_facts())
|
||||
|
||||
reply = self.rpc('get-chassis-inventory')
|
||||
data = reply.find('.//chassis-inventory/chassis')
|
||||
self.facts['serialnum'] = self.get_text(data, 'serial-number')
|
||||
|
||||
def platform_facts(self):
|
||||
platform_facts = {}
|
||||
|
||||
resp = get_capabilities(self.module)
|
||||
device_info = resp['device_info']
|
||||
|
||||
platform_facts['system'] = device_info['network_os']
|
||||
|
||||
for item in ('model', 'image', 'version', 'platform', 'hostname'):
|
||||
val = device_info.get('network_os_%s' % item)
|
||||
if val:
|
||||
platform_facts[item] = val
|
||||
|
||||
platform_facts['api'] = resp['network_api']
|
||||
platform_facts['python_version'] = platform.python_version()
|
||||
|
||||
return platform_facts
|
||||
|
||||
|
||||
class Config(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
config_format = self.module.params['config_format']
|
||||
reply = get_configuration(self.module, format=config_format)
|
||||
|
||||
if config_format == 'xml':
|
||||
config = tostring(reply.find('configuration')).strip()
|
||||
|
||||
elif config_format == 'text':
|
||||
config = self.get_text(reply, 'configuration-text')
|
||||
|
||||
elif config_format == 'json':
|
||||
config = self.module.from_json(reply.text.strip())
|
||||
|
||||
elif config_format == 'set':
|
||||
config = self.get_text(reply, 'configuration-set')
|
||||
|
||||
self.facts['config'] = config
|
||||
|
||||
|
||||
class Hardware(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
|
||||
reply = self.rpc('get-system-memory-information')
|
||||
data = reply.find('.//system-memory-information/system-memory-summary-information')
|
||||
|
||||
self.facts.update({
|
||||
'memfree_mb': int(self.get_text(data, 'system-memory-free')),
|
||||
'memtotal_mb': int(self.get_text(data, 'system-memory-total'))
|
||||
})
|
||||
|
||||
reply = self.rpc('get-system-storage')
|
||||
data = reply.find('.//system-storage-information')
|
||||
|
||||
filesystems = list()
|
||||
for obj in data:
|
||||
filesystems.append(self.get_text(obj, 'filesystem-name'))
|
||||
self.facts['filesystems'] = filesystems
|
||||
|
||||
reply = self.rpc('get-route-engine-information')
|
||||
data = reply.find('.//route-engine-information')
|
||||
|
||||
routing_engines = dict()
|
||||
for obj in data:
|
||||
slot = self.get_text(obj, 'slot')
|
||||
routing_engines.update({slot: {}})
|
||||
routing_engines[slot].update({'slot': slot})
|
||||
for child in obj:
|
||||
if child.text != "\n":
|
||||
routing_engines[slot].update({child.tag.replace("-", "_"): child.text})
|
||||
|
||||
self.facts['routing_engines'] = routing_engines
|
||||
|
||||
if len(data) > 1:
|
||||
self.facts['has_2RE'] = True
|
||||
else:
|
||||
self.facts['has_2RE'] = False
|
||||
|
||||
reply = self.rpc('get-chassis-inventory')
|
||||
data = reply.findall('.//chassis-module')
|
||||
|
||||
modules = list()
|
||||
for obj in data:
|
||||
mod = dict()
|
||||
for child in obj:
|
||||
if child.text != "\n":
|
||||
mod.update({child.tag.replace("-", "_"): child.text})
|
||||
modules.append(mod)
|
||||
|
||||
self.facts['modules'] = modules
|
||||
|
||||
|
||||
class Interfaces(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
ele = Element('get-interface-information')
|
||||
SubElement(ele, 'detail')
|
||||
reply = exec_rpc(self.module, tostring(ele))
|
||||
|
||||
interfaces = {}
|
||||
|
||||
for item in reply[0]:
|
||||
name = self.get_text(item, 'name')
|
||||
obj = {
|
||||
'oper-status': self.get_text(item, 'oper-status'),
|
||||
'admin-status': self.get_text(item, 'admin-status'),
|
||||
'speed': self.get_text(item, 'speed'),
|
||||
'macaddress': self.get_text(item, 'hardware-physical-address'),
|
||||
'mtu': self.get_text(item, 'mtu'),
|
||||
'type': self.get_text(item, 'if-type'),
|
||||
}
|
||||
|
||||
interfaces[name] = obj
|
||||
|
||||
self.facts['interfaces'] = interfaces
|
||||
|
||||
|
||||
class OFacts(FactsBase):
|
||||
def _connect(self, module):
|
||||
host = get_param(module, 'host')
|
||||
|
||||
kwargs = {
|
||||
'port': get_param(module, 'port') or 830,
|
||||
'user': get_param(module, 'username')
|
||||
}
|
||||
|
||||
if get_param(module, 'password'):
|
||||
kwargs['passwd'] = get_param(module, 'password')
|
||||
|
||||
if get_param(module, 'ssh_keyfile'):
|
||||
kwargs['ssh_private_key_file'] = get_param(module, 'ssh_keyfile')
|
||||
|
||||
kwargs['gather_facts'] = False
|
||||
try:
|
||||
device = Device(host, **kwargs)
|
||||
device.open()
|
||||
device.timeout = get_param(module, 'timeout') or 10
|
||||
except ConnectError as exc:
|
||||
module.fail_json('unable to connect to %s: %s' % (host, to_native(exc)))
|
||||
|
||||
return device
|
||||
|
||||
def populate(self):
|
||||
|
||||
device = self._connect(self.module)
|
||||
facts = dict(device.facts)
|
||||
|
||||
if '2RE' in facts:
|
||||
facts['has_2RE'] = facts['2RE']
|
||||
del facts['2RE']
|
||||
|
||||
facts['version_info'] = dict(facts['version_info'])
|
||||
if 'junos_info' in facts:
|
||||
for key, value in facts['junos_info'].items():
|
||||
if 'object' in value:
|
||||
value['object'] = dict(value['object'])
|
||||
|
||||
return facts
|
0
lib/ansible/module_utils/network/junos/utils/__init__.py
Normal file
0
lib/ansible/module_utils/network/junos/utils/__init__.py
Normal file
41
lib/ansible/module_utils/network/junos/utils/utils.py
Normal file
41
lib/ansible/module_utils/network/junos/utils/utils.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
#
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2019 Red Hat
|
||||
# GNU General Public License v3.0+
|
||||
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
# utils
|
||||
from ansible.module_utils._text import to_text
|
||||
|
||||
try:
|
||||
from ncclient.xml_ import to_ele, to_xml, new_ele, sub_ele
|
||||
HAS_NCCLIENT = True
|
||||
except (ImportError, AttributeError):
|
||||
HAS_NCCLIENT = False
|
||||
|
||||
|
||||
def build_root_xml_node(tag):
|
||||
return new_ele(tag)
|
||||
|
||||
|
||||
def build_child_xml_node(parent, tag, text=None, attrib=None):
|
||||
element = sub_ele(parent, tag)
|
||||
if text:
|
||||
element.text = to_text(text)
|
||||
if attrib:
|
||||
element.attrib.update(attrib)
|
||||
return element
|
||||
|
||||
|
||||
def _handle_field_replace(root, field, have, want, tag=None):
|
||||
tag = field if not tag else tag
|
||||
want_value = want.get(field) if want else None
|
||||
have_value = have.get(field) if have else None
|
||||
if have_value:
|
||||
if want_value:
|
||||
if want_value != have_value:
|
||||
build_child_xml_node(root, tag, want_value)
|
||||
else:
|
||||
build_child_xml_node(root, tag, None, {'delete': 'delete'})
|
||||
elif want_value:
|
||||
build_child_xml_node(root, tag, want_value)
|
|
@ -42,7 +42,7 @@ options:
|
|||
junos-eznc library to be installed on control node and the device login credentials
|
||||
must be given in C(provider) option.
|
||||
required: false
|
||||
default: ['!config', '!ofacts']
|
||||
default: ['!config']
|
||||
version_added: "2.3"
|
||||
config_format:
|
||||
description:
|
||||
|
@ -51,11 +51,21 @@ options:
|
|||
only when C(config) value is present in I(gather_subset).
|
||||
The I(config_format) should be supported by the junos version running on
|
||||
device. This value is not applicable while fetching old style facts that is
|
||||
when C(ofacts) value is present in value if I(gather_subset) value.
|
||||
when C(ofacts) value is present in value if I(gather_subset) value. This option
|
||||
is valid only for C(gather_subset) values.
|
||||
required: false
|
||||
default: 'text'
|
||||
choices: ['xml', 'text', 'set', 'json']
|
||||
version_added: "2.3"
|
||||
gather_network_resources:
|
||||
description:
|
||||
- When supplied, this argument will restrict the facts collected
|
||||
to a given subset. Possible values for this argument include
|
||||
all and the resources like interfaces, vlans etc.
|
||||
Can specify a list of values to include a larger subset.
|
||||
choices: ['all']
|
||||
required: false
|
||||
version_added: "2.9"
|
||||
requirements:
|
||||
- ncclient (>=v0.5.2)
|
||||
notes:
|
||||
|
@ -78,6 +88,11 @@ EXAMPLES = """
|
|||
- name: collect default set of facts and configuration
|
||||
junos_facts:
|
||||
gather_subset: config
|
||||
|
||||
- name: Gather legacy and resource facts
|
||||
junos_facts:
|
||||
gather_subset: all
|
||||
gather_network_resources: all
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
|
@ -86,316 +101,28 @@ ansible_facts:
|
|||
returned: always
|
||||
type: dict
|
||||
"""
|
||||
|
||||
import platform
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.network.common.netconf import exec_rpc
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec, get_param, tostring
|
||||
from ansible.module_utils.network.junos.junos import get_configuration, get_capabilities
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
|
||||
try:
|
||||
from lxml.etree import Element, SubElement
|
||||
except ImportError:
|
||||
from xml.etree.ElementTree import Element, SubElement
|
||||
|
||||
try:
|
||||
from jnpr.junos import Device
|
||||
from jnpr.junos.exception import ConnectError
|
||||
HAS_PYEZ = True
|
||||
except ImportError:
|
||||
HAS_PYEZ = False
|
||||
|
||||
USE_PERSISTENT_CONNECTION = True
|
||||
|
||||
|
||||
class FactsBase(object):
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.facts = dict()
|
||||
|
||||
def populate(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def cli(self, command):
|
||||
reply = command(self.module, command)
|
||||
output = reply.find('.//output')
|
||||
if not output:
|
||||
self.module.fail_json(msg='failed to retrieve facts for command %s' % command)
|
||||
return str(output.text).strip()
|
||||
|
||||
def rpc(self, rpc):
|
||||
return exec_rpc(self.module, tostring(Element(rpc)))
|
||||
|
||||
def get_text(self, ele, tag):
|
||||
try:
|
||||
return str(ele.find(tag).text).strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
class Default(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
self.facts.update(self.platform_facts())
|
||||
|
||||
reply = self.rpc('get-chassis-inventory')
|
||||
data = reply.find('.//chassis-inventory/chassis')
|
||||
self.facts['serialnum'] = self.get_text(data, 'serial-number')
|
||||
|
||||
def platform_facts(self):
|
||||
platform_facts = {}
|
||||
|
||||
resp = get_capabilities(self.module)
|
||||
device_info = resp['device_info']
|
||||
|
||||
platform_facts['system'] = device_info['network_os']
|
||||
|
||||
for item in ('model', 'image', 'version', 'platform', 'hostname'):
|
||||
val = device_info.get('network_os_%s' % item)
|
||||
if val:
|
||||
platform_facts[item] = val
|
||||
|
||||
platform_facts['api'] = resp['network_api']
|
||||
platform_facts['python_version'] = platform.python_version()
|
||||
|
||||
return platform_facts
|
||||
|
||||
|
||||
class Config(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
config_format = self.module.params['config_format']
|
||||
reply = get_configuration(self.module, format=config_format)
|
||||
|
||||
if config_format == 'xml':
|
||||
config = tostring(reply.find('configuration')).strip()
|
||||
|
||||
elif config_format == 'text':
|
||||
config = self.get_text(reply, 'configuration-text')
|
||||
|
||||
elif config_format == 'json':
|
||||
config = self.module.from_json(reply.text.strip())
|
||||
|
||||
elif config_format == 'set':
|
||||
config = self.get_text(reply, 'configuration-set')
|
||||
|
||||
self.facts['config'] = config
|
||||
|
||||
|
||||
class Hardware(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
|
||||
reply = self.rpc('get-system-memory-information')
|
||||
data = reply.find('.//system-memory-information/system-memory-summary-information')
|
||||
|
||||
self.facts.update({
|
||||
'memfree_mb': int(self.get_text(data, 'system-memory-free')),
|
||||
'memtotal_mb': int(self.get_text(data, 'system-memory-total'))
|
||||
})
|
||||
|
||||
reply = self.rpc('get-system-storage')
|
||||
data = reply.find('.//system-storage-information')
|
||||
|
||||
filesystems = list()
|
||||
for obj in data:
|
||||
filesystems.append(self.get_text(obj, 'filesystem-name'))
|
||||
self.facts['filesystems'] = filesystems
|
||||
|
||||
reply = self.rpc('get-route-engine-information')
|
||||
data = reply.find('.//route-engine-information')
|
||||
|
||||
routing_engines = dict()
|
||||
for obj in data:
|
||||
slot = self.get_text(obj, 'slot')
|
||||
routing_engines.update({slot: {}})
|
||||
routing_engines[slot].update({'slot': slot})
|
||||
for child in obj:
|
||||
if child.text != "\n":
|
||||
routing_engines[slot].update({child.tag.replace("-", "_"): child.text})
|
||||
|
||||
self.facts['routing_engines'] = routing_engines
|
||||
|
||||
if len(data) > 1:
|
||||
self.facts['has_2RE'] = True
|
||||
else:
|
||||
self.facts['has_2RE'] = False
|
||||
|
||||
reply = self.rpc('get-chassis-inventory')
|
||||
data = reply.findall('.//chassis-module')
|
||||
|
||||
modules = list()
|
||||
for obj in data:
|
||||
mod = dict()
|
||||
for child in obj:
|
||||
if child.text != "\n":
|
||||
mod.update({child.tag.replace("-", "_"): child.text})
|
||||
modules.append(mod)
|
||||
|
||||
self.facts['modules'] = modules
|
||||
|
||||
|
||||
class Interfaces(FactsBase):
|
||||
|
||||
def populate(self):
|
||||
ele = Element('get-interface-information')
|
||||
SubElement(ele, 'detail')
|
||||
reply = exec_rpc(self.module, tostring(ele))
|
||||
|
||||
interfaces = {}
|
||||
|
||||
for item in reply[0]:
|
||||
name = self.get_text(item, 'name')
|
||||
obj = {
|
||||
'oper-status': self.get_text(item, 'oper-status'),
|
||||
'admin-status': self.get_text(item, 'admin-status'),
|
||||
'speed': self.get_text(item, 'speed'),
|
||||
'macaddress': self.get_text(item, 'hardware-physical-address'),
|
||||
'mtu': self.get_text(item, 'mtu'),
|
||||
'type': self.get_text(item, 'if-type'),
|
||||
}
|
||||
|
||||
interfaces[name] = obj
|
||||
|
||||
self.facts['interfaces'] = interfaces
|
||||
|
||||
|
||||
class OFacts(FactsBase):
|
||||
def _connect(self, module):
|
||||
host = get_param(module, 'host')
|
||||
|
||||
kwargs = {
|
||||
'port': get_param(module, 'port') or 830,
|
||||
'user': get_param(module, 'username')
|
||||
}
|
||||
|
||||
if get_param(module, 'password'):
|
||||
kwargs['passwd'] = get_param(module, 'password')
|
||||
|
||||
if get_param(module, 'ssh_keyfile'):
|
||||
kwargs['ssh_private_key_file'] = get_param(module, 'ssh_keyfile')
|
||||
|
||||
kwargs['gather_facts'] = False
|
||||
try:
|
||||
device = Device(host, **kwargs)
|
||||
device.open()
|
||||
device.timeout = get_param(module, 'timeout') or 10
|
||||
except ConnectError as exc:
|
||||
module.fail_json('unable to connect to %s: %s' % (host, to_native(exc)))
|
||||
|
||||
return device
|
||||
|
||||
def populate(self):
|
||||
|
||||
device = self._connect(self.module)
|
||||
facts = dict(device.facts)
|
||||
|
||||
if '2RE' in facts:
|
||||
facts['has_2RE'] = facts['2RE']
|
||||
del facts['2RE']
|
||||
|
||||
facts['version_info'] = dict(facts['version_info'])
|
||||
if 'junos_info' in facts:
|
||||
for key, value in facts['junos_info'].items():
|
||||
if 'object' in value:
|
||||
value['object'] = dict(value['object'])
|
||||
|
||||
return facts
|
||||
|
||||
|
||||
FACT_SUBSETS = dict(
|
||||
default=Default,
|
||||
hardware=Hardware,
|
||||
config=Config,
|
||||
interfaces=Interfaces,
|
||||
ofacts=OFacts
|
||||
)
|
||||
|
||||
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
|
||||
from ansible.module_utils.network.junos.argspec.facts.facts import FactsArgs
|
||||
from ansible.module_utils.network.junos.facts.facts import Facts
|
||||
from ansible.module_utils.network.junos.junos import junos_argument_spec
|
||||
|
||||
|
||||
def main():
|
||||
""" Main entry point for AnsibleModule
|
||||
"""
|
||||
argument_spec = dict(
|
||||
gather_subset=dict(default=['!config', '!ofacts'], type='list'),
|
||||
config_format=dict(default='text', choices=['xml', 'text', 'set', 'json']),
|
||||
)
|
||||
|
||||
argument_spec = FactsArgs.argument_spec
|
||||
argument_spec.update(junos_argument_spec)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
gather_subset = module.params['gather_subset']
|
||||
warnings = ['default value for `gather_subset` '
|
||||
'will be changed to `min` from `!config` v2.11 onwards']
|
||||
|
||||
runable_subsets = set()
|
||||
exclude_subsets = set()
|
||||
result = Facts(module).get_facts()
|
||||
|
||||
for subset in gather_subset:
|
||||
if subset == 'all':
|
||||
runable_subsets.update(VALID_SUBSETS)
|
||||
continue
|
||||
|
||||
if subset.startswith('!'):
|
||||
subset = subset[1:]
|
||||
if subset == 'all':
|
||||
exclude_subsets.update(VALID_SUBSETS)
|
||||
continue
|
||||
exclude = True
|
||||
else:
|
||||
exclude = False
|
||||
|
||||
if subset not in VALID_SUBSETS:
|
||||
module.fail_json(msg='Subset must be one of [%s], got %s' %
|
||||
(', '.join(sorted([subset for subset in
|
||||
VALID_SUBSETS])), subset))
|
||||
|
||||
if exclude:
|
||||
exclude_subsets.add(subset)
|
||||
else:
|
||||
runable_subsets.add(subset)
|
||||
|
||||
if not runable_subsets:
|
||||
runable_subsets.update(VALID_SUBSETS)
|
||||
|
||||
runable_subsets.difference_update(exclude_subsets)
|
||||
runable_subsets.add('default')
|
||||
|
||||
# handle fetching old style facts separately
|
||||
runable_subsets.discard('ofacts')
|
||||
|
||||
facts = dict()
|
||||
facts['gather_subset'] = list(runable_subsets)
|
||||
|
||||
instances = list()
|
||||
ansible_facts = dict()
|
||||
|
||||
# fetch old style facts only when explicitly mentioned in gather_subset option
|
||||
if 'ofacts' in gather_subset:
|
||||
if HAS_PYEZ:
|
||||
ansible_facts.update(OFacts(module).populate())
|
||||
else:
|
||||
warnings += ['junos-eznc is required to gather old style facts but does not appear to be installed. '
|
||||
'It can be installed using `pip install junos-eznc`']
|
||||
facts['gather_subset'].append('ofacts')
|
||||
|
||||
for key in runable_subsets:
|
||||
instances.append(FACT_SUBSETS[key](module))
|
||||
|
||||
for inst in instances:
|
||||
inst.populate()
|
||||
facts.update(inst.facts)
|
||||
|
||||
for key, value in iteritems(facts):
|
||||
key = 'ansible_net_%s' % key
|
||||
ansible_facts[key] = value
|
||||
ansible_facts, additional_warnings = result
|
||||
warnings.extend(additional_warnings)
|
||||
|
||||
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
|
||||
|
||||
|
|
|
@ -46,20 +46,24 @@ class TestJunosCommandModule(TestJunosModule):
|
|||
def setUp(self):
|
||||
super(TestJunosCommandModule, self).setUp()
|
||||
|
||||
self.mock_get_config = patch('ansible.modules.network.junos.junos_facts.get_configuration')
|
||||
self.mock_get_config = patch('ansible.module_utils.network.junos.facts.legacy.base.get_configuration')
|
||||
self.get_config = self.mock_get_config.start()
|
||||
|
||||
self.mock_netconf = patch('ansible.module_utils.network.junos.junos.NetconfConnection')
|
||||
self.netconf_conn = self.mock_netconf.start()
|
||||
|
||||
self.mock_exec_rpc = patch('ansible.modules.network.junos.junos_facts.exec_rpc')
|
||||
self.mock_exec_rpc = patch('ansible.module_utils.network.junos.facts.legacy.base.exec_rpc')
|
||||
self.exec_rpc = self.mock_exec_rpc.start()
|
||||
|
||||
self.mock_netconf_rpc = patch('ansible.module_utils.network.common.netconf.NetconfConnection')
|
||||
self.netconf_rpc = self.mock_netconf_rpc.start()
|
||||
|
||||
self.mock_get_capabilities = patch('ansible.modules.network.junos.junos_facts.get_capabilities')
|
||||
self.mock_get_resource_connection = patch('ansible.module_utils.network.common.facts.facts.get_resource_connection')
|
||||
self.get_resource_connection = self.mock_get_resource_connection.start()
|
||||
|
||||
self.mock_get_capabilities = patch('ansible.module_utils.network.junos.facts.legacy.base.get_capabilities')
|
||||
self.get_capabilities = self.mock_get_capabilities.start()
|
||||
|
||||
self.get_capabilities.return_value = {
|
||||
'device_info': {
|
||||
'network_os': 'junos',
|
||||
|
@ -76,6 +80,7 @@ class TestJunosCommandModule(TestJunosModule):
|
|||
self.mock_exec_rpc.stop()
|
||||
self.mock_netconf_rpc.stop()
|
||||
self.mock_get_capabilities.stop()
|
||||
self.mock_get_resource_connection.stop()
|
||||
|
||||
def load_fixtures(self, commands=None, format='text', changed=False):
|
||||
def load_from_file(*args, **kwargs):
|
||||
|
|
Loading…
Reference in a new issue