Add platform facts in network facts modules (#51434)

* Add platform facts in network facts modules

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* Add nxos

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* Add vyos

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* Add iosxr

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* Add junos

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* fix pep8

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* update unit test

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* fix vyos_facts unittest

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* fix ios_facts unittest

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* fix iosxr unittests

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* fix CI failure

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* fix junos test

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
This commit is contained in:
Trishna Guha 2019-03-11 10:56:39 +05:30 committed by GitHub
commit a41028244d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 303 additions and 180 deletions

View file

@ -96,6 +96,14 @@ ansible_net_fqdn:
description: The fully qualified domain name of the device
returned: always
type: str
ansible_net_api:
description: The name of the transport
returned: always
type: str
ansible_net_python_version:
description: The Python version Ansible controller is using
returned: always
type: str
# hardware
ansible_net_filesystems:
@ -135,11 +143,13 @@ ansible_net_neighbors:
returned: when interfaces is configured
type: dict
"""
import platform
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import iteritems
from ansible.module_utils.network.eos.eos import run_commands
from ansible.module_utils.network.eos.eos import run_commands, get_capabilities
from ansible.module_utils.network.eos.eos import eos_argument_spec, check_args
@ -159,15 +169,12 @@ class FactsBase(object):
class Default(FactsBase):
SYSTEM_MAP = {
'version': 'version',
'serialNumber': 'serialnum',
'modelName': 'model'
}
COMMANDS = [
'show version | json',
'show hostname | json',
'bash timeout 5 cat /mnt/flash/boot-config'
]
def populate(self):
@ -178,18 +185,25 @@ class Default(FactsBase):
self.facts[value] = data[key]
self.facts.update(self.responses[1])
self.facts.update(self.parse_image())
self.facts.update(self.platform_facts())
def parse_image(self):
data = self.responses[2]
if isinstance(data, dict):
data = data['messages'][0]
match = re.search(r'SWI=(.+)$', data, re.M)
if match:
value = match.group(1)
else:
value = None
return dict(image=value)
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 Hardware(FactsBase):

View file

@ -103,6 +103,14 @@ ansible_net_stacked_serialnums:
description: The serial numbers of each device in the stack
returned: when multiple devices are configured in a stack
type: list
ansible_net_api:
description: The name of the transport
returned: always
type: str
ansible_net_python_version:
description: The Python version Ansible controller is using
returned: always
type: str
# hardware
ansible_net_filesystems:
@ -148,9 +156,10 @@ ansible_net_neighbors:
returned: when interfaces is configured
type: dict
"""
import platform
import re
from ansible.module_utils.network.ios.ios import run_commands
from ansible.module_utils.network.ios.ios import run_commands, get_capabilities
from ansible.module_utils.network.ios.ios import ios_argument_spec, check_args
from ansible.module_utils.network.ios.ios import normalize_interface
from ansible.module_utils.basic import AnsibleModule
@ -180,21 +189,13 @@ class Default(FactsBase):
def populate(self):
super(Default, self).populate()
self.facts.update(self.platform_facts())
data = self.responses[0]
if data:
self.facts['version'] = self.parse_version(data)
self.facts['iostype'] = self.parse_iostype(data)
self.facts['serialnum'] = self.parse_serialnum(data)
self.facts['model'] = self.parse_model(data)
self.facts['image'] = self.parse_image(data)
self.facts['hostname'] = self.parse_hostname(data)
self.parse_stacks(data)
def parse_version(self, data):
match = re.search(r'Version (\S+?)(?:,\s|\s)', data)
if match:
return match.group(1)
def parse_iostype(self, data):
match = re.search(r'\S+(X86_64_LINUX_IOSD-UNIVERSALK9-M)(\S+)', data)
if match:
@ -202,21 +203,6 @@ class Default(FactsBase):
else:
return "IOS"
def parse_hostname(self, data):
match = re.search(r'^(.+) uptime', data, re.M)
if match:
return match.group(1)
def parse_model(self, data):
match = re.search(r'^[Cc]isco (\S+).+bytes of .*memory', data, re.M)
if match:
return match.group(1)
def parse_image(self, data):
match = re.search(r'image file is "(.+)"', data)
if match:
return match.group(1)
def parse_serialnum(self, data):
match = re.search(r'board ID (\S+)', data)
if match:
@ -231,6 +217,24 @@ class Default(FactsBase):
if match:
self.facts['stacked_serialnums'] = match
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 Hardware(FactsBase):

View file

@ -76,6 +76,14 @@ ansible_net_image:
description: The image file the device is running
returned: always
type: str
ansible_net_api:
description: The name of the transport
returned: always
type: str
ansible_net_python_version:
description: The Python version Ansible controller is using
returned: always
type: str
# hardware
ansible_net_filesystems:
@ -115,61 +123,67 @@ ansible_net_neighbors:
returned: when interfaces is configured
type: dict
"""
import platform
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, run_commands
from ansible.module_utils.network.iosxr.iosxr import iosxr_argument_spec, run_commands, get_capabilities
from ansible.module_utils.six import iteritems
from ansible.module_utils.six.moves import zip
class FactsBase(object):
def __init__(self):
self.facts = dict()
self.commands()
COMMANDS = frozenset()
def commands(self):
raise NotImplementedError
def __init__(self, module):
self.module = module
self.facts = dict()
self.responses = None
def populate(self):
self.responses = run_commands(self.module, list(self.COMMANDS), check_rc=False)
class Default(FactsBase):
def commands(self):
return(['show version | utility head -n 20'])
def populate(self):
self.facts.update(self.platform_facts())
def populate(self, results):
self.facts['version'] = self.parse_version(results['show version | utility head -n 20'])
self.facts['image'] = self.parse_image(results['show version | utility head -n 20'])
self.facts['hostname'] = self.parse_hostname(results['show version | utility head -n 20'])
def platform_facts(self):
platform_facts = {}
def parse_version(self, data):
match = re.search(r'Version (\S+)$', data, re.M)
if match:
return match.group(1)
resp = get_capabilities(self.module)
device_info = resp['device_info']
def parse_hostname(self, data):
match = re.search(r'^(.+) uptime', data, re.M)
if match:
return match.group(1)
platform_facts['system'] = device_info['network_os']
def parse_image(self, data):
match = re.search(r'image file is "(.+)"', data)
if match:
return match.group(1)
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 Hardware(FactsBase):
def commands(self):
return(['dir /all', 'show memory summary'])
COMMANDS = [
'dir /all',
'show memory summary'
]
def populate(self, results):
self.facts['filesystems'] = self.parse_filesystems(
results['dir /all'])
def populate(self):
super(Hardware, self).populate()
data = self.responses[0]
self.facts['filesystems'] = self.parse_filesystems(data)
match = re.search(r'Physical Memory: (\d+)M total \((\d+)',
results['show memory summary'])
data = self.responses[1]
match = re.search(r'Physical Memory: (\d+)M total \((\d+)', data)
if match:
self.facts['memtotal_mb'] = match.group(1)
self.facts['memfree_mb'] = match.group(2)
@ -180,33 +194,39 @@ class Hardware(FactsBase):
class Config(FactsBase):
def commands(self):
return(['show running-config'])
COMMANDS = [
'show running-config'
]
def populate(self, results):
self.facts['config'] = results['show running-config']
def populate(self):
super(Config, self).populate()
self.facts['config'] = self.responses[0]
class Interfaces(FactsBase):
def commands(self):
return(['show interfaces', 'show ipv6 interface',
'show lldp', 'show lldp neighbors detail'])
COMMANDS = [
'show interfaces',
'show ipv6 interface',
'show lldp',
'show lldp neighbors detail'
]
def populate(self, results):
def populate(self):
super(Interfaces, self).populate()
self.facts['all_ipv4_addresses'] = list()
self.facts['all_ipv6_addresses'] = list()
interfaces = self.parse_interfaces(results['show interfaces'])
interfaces = self.parse_interfaces(self.responses[0])
self.facts['interfaces'] = self.populate_interfaces(interfaces)
data = results['show ipv6 interface']
data = self.responses[1]
if len(data) > 0:
data = self.parse_interfaces(data)
self.populate_ipv6_interfaces(data)
if 'LLDP is not enabled' not in results['show lldp']:
neighbors = results['show lldp neighbors detail']
if 'LLDP is not enabled' not in self.responses[2]:
neighbors = self.responses[3]
self.facts['neighbors'] = self.parse_neighbors(neighbors)
def populate_interfaces(self, interfaces):
@ -402,17 +422,11 @@ def main():
instances = list()
for key in runable_subsets:
instances.append(FACT_SUBSETS[key]())
instances.append(FACT_SUBSETS[key](module))
try:
for inst in instances:
commands = inst.commands()
responses = run_commands(module, commands)
results = dict(zip(commands, responses))
inst.populate(results)
facts.update(inst.facts)
except Exception:
module.exit_json(out=module.from_json(results))
for inst in instances:
inst.populate()
facts.update(inst.facts)
ansible_facts = dict()
for key, value in iteritems(facts):

View file

@ -86,10 +86,13 @@ 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_connection
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
@ -138,19 +141,30 @@ class FactsBase(object):
class Default(FactsBase):
def populate(self):
reply = self.rpc('get-software-information')
data = reply.find('.//software-information')
self.facts.update({
'hostname': self.get_text(data, 'host-name'),
'version': self.get_text(data, 'junos-version'),
'model': self.get_text(data, 'product-model')
})
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):
@ -318,7 +332,6 @@ def main():
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
get_connection(module)
warnings = list()
gather_subset = module.params['gather_subset']

View file

@ -95,6 +95,18 @@ ansible_net_image:
description: The image file the device is running
returned: always
type: str
ansible_net_api:
description: The name of the transport
returned: always
type: str
ansible_net_license_hostid:
description: The License host id of the device
returned: always
type: str
ansible_net_python_version:
description: The Python version Ansible controller is using
returned: always
type: str
# hardware
ansible_net_filesystems:
@ -170,6 +182,8 @@ vlan_list:
returned: when legacy is configured
type: list
"""
import platform
import re
from ansible.module_utils.network.nxos.nxos import run_commands, get_config
@ -190,6 +204,7 @@ class FactsBase(object):
self.module = module
self.warnings = list()
self.facts = dict()
self.capabilities = get_capabilities(self.module)
def populate(self):
pass
@ -227,39 +242,12 @@ class FactsBase(object):
class Default(FactsBase):
VERSION_MAP_7K = frozenset([
('sys_ver_str', 'version'),
('proc_board_id', 'serialnum'),
('chassis_id', 'model'),
('isan_file_name', 'image'),
('host_name', 'hostname')
])
VERSION_MAP = frozenset([
('kickstart_ver_str', 'version'),
('proc_board_id', 'serialnum'),
('chassis_id', 'model'),
('kick_file_name', 'image'),
('host_name', 'hostname')
])
def populate(self):
data = None
data = self.run('show version', output='json')
data = self.run('show version')
if data:
if isinstance(data, dict):
if data.get('sys_ver_str'):
self.facts.update(self.transform_dict(data, self.VERSION_MAP_7K))
else:
self.facts.update(self.transform_dict(data, self.VERSION_MAP))
else:
self.facts['version'] = self.parse_version(data)
self.facts['serialnum'] = self.parse_serialnum(data)
self.facts['model'] = self.parse_model(data)
self.facts['image'] = self.parse_image(data)
self.facts['hostname'] = self.parse_hostname(data)
self.facts['serialnum'] = self.parse_serialnum(data)
data = self.run('show license host-id')
if data:
@ -279,24 +267,28 @@ class Default(FactsBase):
if match:
return match.group(1)
def parse_model(self, data):
match = re.search(r'Hardware\n\s+cisco\s*(\S+\s+\S+)', data, re.M)
def parse_license_hostid(self, data):
match = re.search(r'License hostid: VDH=(.+)$', data, re.M)
if match:
return match.group(1)
def parse_image(self, data):
match = re.search(r'\s+system image file is:\s*(\S+)', data, re.M)
if match:
return match.group(1)
else:
match = re.search(r'\s+kickstart image file is:\s*(\S+)', data, re.M)
if match:
return match.group(1)
def platform_facts(self):
platform_facts = {}
def parse_hostname(self, data):
match = re.search(r'\s+Device name:\s*(\S+)', data, re.M)
if match:
return match.group(1)
resp = self.capabilities
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
def parse_license_hostid(self, data):
match = re.search(r'License hostid: VDH=(.+)$', data, re.M)
@ -398,7 +390,7 @@ class Interfaces(FactsBase):
])
def ipv6_structure_op_supported(self):
data = get_capabilities(self.module)
data = self.capabilities
if data:
nxos_os_version = data['device_info']['network_os_version']
unsupported_versions = ['I2', 'F1', 'A8']

View file

@ -95,12 +95,22 @@ ansible_net_gather_subset:
description: The list of subsets gathered by the module
returned: always
type: list
ansible_net_api:
description: The name of the transport
returned: always
type: str
ansible_net_python_version:
description: The Python version Ansible controller is using
returned: always
type: str
"""
import platform
import re
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import iteritems
from ansible.module_utils.network.vyos.vyos import run_commands
from ansible.module_utils.network.vyos.vyos import run_commands, get_capabilities
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
@ -121,34 +131,37 @@ class Default(FactsBase):
COMMANDS = [
'show version',
'show host name',
]
def populate(self):
super(Default, self).populate()
data = self.responses[0]
self.facts['version'] = self.parse_version(data)
self.facts['serialnum'] = self.parse_serialnum(data)
self.facts['model'] = self.parse_model(data)
self.facts['hostname'] = self.responses[1]
def parse_version(self, data):
match = re.search(r'Version:\s*(.*)', data)
if match:
return match.group(1)
def parse_model(self, data):
match = re.search(r'HW model:\s*(\S+)', data)
if match:
return match.group(1)
self.facts.update(self.platform_facts())
def parse_serialnum(self, data):
match = re.search(r'HW S/N:\s+(\S+)', data)
if match:
return match.group(1)
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):