mirror of https://github.com/ansible/ansible.git
network/exos: add exos_facts module (#43210)
Add exos_facts module. Known limitations at this time include: - Interface MTU is not reported. - Only primary interface IP is reported. Add basic unit tests for the exos_facts module. An EXOS CLI prompt can be prefixed with '! ' (shutting down), '* ' (running configuration does not match saved configuration), and can include various status tokens within parentheses after these prefixes. Update prompt regex to accept valid CLI prompts.pull/43828/head
parent
e24c036057
commit
bd4d68c785
@ -0,0 +1,450 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# (c) 2018 Extreme Networks Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'}
|
||||
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: exos_facts
|
||||
version_added: "2.7"
|
||||
author: "Lance Richardson (@hlrichardson)"
|
||||
short_description: Collect facts from devices running Extreme EXOS
|
||||
description:
|
||||
- Collects a base set of device facts from a remote device that
|
||||
is running EXOS. This module prepends all of the base network
|
||||
fact keys with C(ansible_net_<fact>). The facts module will
|
||||
always collect a base set of facts from the device and can
|
||||
enable or disable collection of additional facts.
|
||||
notes:
|
||||
- Tested against EXOS 22.5.1.7
|
||||
options:
|
||||
gather_subset:
|
||||
description:
|
||||
- When supplied, this argument will restrict the facts collected
|
||||
to a given subset. Possible values for this argument include
|
||||
all, hardware, config, and interfaces. Can specify a list of
|
||||
values to include a larger subset. Values can also be used
|
||||
with an initial C(M(!)) to specify that a specific subset should
|
||||
not be collected.
|
||||
required: false
|
||||
default: ['!config']
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: collect all facts from the device
|
||||
exos_facts:
|
||||
gather_subset: all
|
||||
|
||||
- name: collect only the config and default facts
|
||||
exos_facts:
|
||||
gather_subset: config
|
||||
|
||||
- name: do not collect hardware facts
|
||||
exos_facts:
|
||||
gather_subset: "!hardware"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
ansible_net_gather_subset:
|
||||
description: The list of fact subsets collected from the device
|
||||
returned: always
|
||||
type: list
|
||||
|
||||
# default
|
||||
ansible_net_model:
|
||||
description: The model name returned from the device
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_serialnum:
|
||||
description: The serial number of the remote device
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_version:
|
||||
description: The operating system version running on the remote device
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_hostname:
|
||||
description: The configured hostname of the device
|
||||
returned: always
|
||||
type: string
|
||||
|
||||
# hardware
|
||||
ansible_net_memfree_mb:
|
||||
description: The available free memory on the remote device in Mb
|
||||
returned: when hardware is configured
|
||||
type: int
|
||||
ansible_net_memtotal_mb:
|
||||
description: The total memory on the remote device in Mb
|
||||
returned: when hardware is configured
|
||||
type: int
|
||||
|
||||
# config
|
||||
ansible_net_config:
|
||||
description: The current active config from the device
|
||||
returned: when config is configured
|
||||
type: str
|
||||
|
||||
# interfaces
|
||||
ansible_net_all_ipv4_addresses:
|
||||
description: All IPv4 addresses configured on the device
|
||||
returned: when interfaces is configured
|
||||
type: list
|
||||
ansible_net_all_ipv6_addresses:
|
||||
description: All Primary IPv6 addresses configured on the device
|
||||
returned: when interfaces is configured
|
||||
type: list
|
||||
ansible_net_interfaces:
|
||||
description: A hash of all interfaces running on the system
|
||||
returned: when interfaces is configured
|
||||
type: dict
|
||||
ansible_net_neighbors:
|
||||
description: The list of LLDP neighbors from the remote device
|
||||
returned: when interfaces is configured
|
||||
type: dict
|
||||
"""
|
||||
import re
|
||||
import json
|
||||
|
||||
from ansible.module_utils.network.exos.exos import run_commands
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
|
||||
|
||||
class FactsBase(object):
|
||||
|
||||
COMMANDS = list()
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.facts = dict()
|
||||
self.responses = None
|
||||
|
||||
def populate(self):
|
||||
self.responses = run_commands(self.module, self.COMMANDS)
|
||||
|
||||
def run(self, cmd):
|
||||
return run_commands(self.module, cmd)
|
||||
|
||||
|
||||
class Default(FactsBase):
|
||||
|
||||
COMMANDS = [
|
||||
'show version',
|
||||
'show switch'
|
||||
]
|
||||
|
||||
def populate(self):
|
||||
super(Default, self).populate()
|
||||
data = self.responses[0]
|
||||
if data:
|
||||
self.facts['version'] = self.parse_version(data)
|
||||
self.facts['serialnum'] = self.parse_serialnum(data)
|
||||
|
||||
data = self.responses[1]
|
||||
if data:
|
||||
self.facts['model'] = self.parse_model(data)
|
||||
self.facts['hostname'] = self.parse_hostname(data)
|
||||
|
||||
def parse_version(self, data):
|
||||
match = re.search(r'Image\s+: ExtremeXOS version (\S+)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_model(self, data):
|
||||
match = re.search(r'System Type:\s+(.*$)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_hostname(self, data):
|
||||
match = re.search(r'SysName:\s+(\S+)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_serialnum(self, data):
|
||||
match = re.search(r'Switch\s+: \S+ (\S+)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
# For stack, return serial number of the first switch in the stack.
|
||||
match = re.search(r'Slot-\d+\s+: \S+ (\S+)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
# Handle unique formatting for VM
|
||||
match = re.search(r'Switch\s+: PN:\S+\s+SN:(\S+)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
|
||||
class Hardware(FactsBase):
|
||||
|
||||
COMMANDS = [
|
||||
'show memory'
|
||||
]
|
||||
|
||||
def populate(self):
|
||||
super(Hardware, self).populate()
|
||||
data = self.responses[0]
|
||||
if data:
|
||||
self.facts['memtotal_mb'] = int(round(int(self.parse_memtotal(data)) / 1024, 0))
|
||||
self.facts['memfree_mb'] = int(round(int(self.parse_memfree(data)) / 1024, 0))
|
||||
|
||||
def parse_memtotal(self, data):
|
||||
match = re.search(r' Total DRAM \(KB\): (\d+)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
# Handle unique formatting for VM
|
||||
match = re.search(r' Total \s+\(KB\): (\d+)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_memfree(self, data):
|
||||
match = re.search(r' Free\s+\(KB\): (\d+)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
|
||||
class Config(FactsBase):
|
||||
|
||||
COMMANDS = ['show configuration detail']
|
||||
|
||||
def populate(self):
|
||||
super(Config, self).populate()
|
||||
data = self.responses[0]
|
||||
if data:
|
||||
self.facts['config'] = data
|
||||
|
||||
|
||||
class Interfaces(FactsBase):
|
||||
|
||||
COMMANDS = [
|
||||
'show switch',
|
||||
'run script cli2json.py show port config',
|
||||
'run script cli2json.py show port description',
|
||||
'run script cli2json.py show vlan detail',
|
||||
'run script cli2json.py show lldp neighbors'
|
||||
]
|
||||
|
||||
def populate(self):
|
||||
super(Interfaces, self).populate()
|
||||
|
||||
self.facts['all_ipv4_addresses'] = list()
|
||||
self.facts['all_ipv6_addresses'] = list()
|
||||
|
||||
data = self.responses[0]
|
||||
if data:
|
||||
sysmac = self.parse_sysmac(data)
|
||||
|
||||
data = json.loads(self.responses[1])
|
||||
if data:
|
||||
self.facts['interfaces'] = self.populate_interfaces(data, sysmac)
|
||||
|
||||
data = json.loads(self.responses[2])
|
||||
if data:
|
||||
self.populate_interface_descriptions(data)
|
||||
|
||||
data = json.loads(self.responses[3])
|
||||
if data:
|
||||
self.populate_vlan_interfaces(data, sysmac)
|
||||
|
||||
data = json.loads(self.responses[4])
|
||||
if data:
|
||||
self.facts['neighbors'] = self.parse_neighbors(data)
|
||||
|
||||
def parse_sysmac(self, data):
|
||||
match = re.search(r'System MAC:\s+(\S+)', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def populate_interfaces(self, interfaces, sysmac):
|
||||
facts = dict()
|
||||
for elem in interfaces:
|
||||
intf = dict()
|
||||
|
||||
if 'show_ports_config' not in elem:
|
||||
continue
|
||||
|
||||
key = str(elem['show_ports_config']['port'])
|
||||
|
||||
if elem['show_ports_config']['linkState'] == 2:
|
||||
# Link state is "not present", don't include
|
||||
continue
|
||||
|
||||
intf['type'] = 'Ethernet'
|
||||
intf['macaddress'] = sysmac
|
||||
intf['bandwidth_configured'] = str(elem['show_ports_config']['speedCfg'])
|
||||
intf['bandwidth'] = str(elem['show_ports_config']['speedActual'])
|
||||
intf['duplex_configured'] = elem['show_ports_config']['duplexCfg']
|
||||
intf['duplex'] = elem['show_ports_config']['duplexActual']
|
||||
if elem['show_ports_config']['linkState'] == 1:
|
||||
intf['lineprotocol'] = 'up'
|
||||
else:
|
||||
intf['lineprotocol'] = 'down'
|
||||
if elem['show_ports_config']['portState'] == 1:
|
||||
intf['operstatus'] = 'up'
|
||||
else:
|
||||
intf['operstatus'] = 'admin down'
|
||||
|
||||
facts[key] = intf
|
||||
return facts
|
||||
|
||||
def populate_interface_descriptions(self, data):
|
||||
facts = dict()
|
||||
for elem in data:
|
||||
if 'show_ports_description' not in elem:
|
||||
continue
|
||||
key = str(elem['show_ports_description']['port'])
|
||||
|
||||
if 'descriptionString' in elem['show_ports_description']:
|
||||
desc = elem['show_ports_description']['descriptionString']
|
||||
self.facts['interfaces'][key]['description'] = desc
|
||||
|
||||
def populate_vlan_interfaces(self, data, sysmac):
|
||||
for elem in data:
|
||||
if 'vlanProc' in elem:
|
||||
key = elem['vlanProc']['name1']
|
||||
if key not in self.facts['interfaces']:
|
||||
intf = dict()
|
||||
intf['type'] = 'VLAN'
|
||||
intf['macaddress'] = sysmac
|
||||
self.facts['interfaces'][key] = intf
|
||||
|
||||
if elem['vlanProc']['ipAddress'] != '0.0.0.0':
|
||||
self.facts['interfaces'][key]['ipv4'] = list()
|
||||
addr = elem['vlanProc']['ipAddress']
|
||||
subnet = elem['vlanProc']['maskForDisplay']
|
||||
ipv4 = dict(address=addr, subnet=subnet)
|
||||
self.add_ip_address(addr, 'ipv4')
|
||||
self.facts['interfaces'][key]['ipv4'].append(ipv4)
|
||||
|
||||
if 'rtifIpv6Address' in elem:
|
||||
key = elem['rtifIpv6Address']['rtifName']
|
||||
if key not in self.facts['interfaces']:
|
||||
intf = dict()
|
||||
intf['type'] = 'VLAN'
|
||||
intf['macaddress'] = sysmac
|
||||
self.facts['interfaces'][key] = intf
|
||||
self.facts['interfaces'][key]['ipv6'] = list()
|
||||
addr, subnet = elem['rtifIpv6Address']['ipv6_address_mask'].split('/')
|
||||
ipv6 = dict(address=addr, subnet=subnet)
|
||||
self.add_ip_address(addr, 'ipv6')
|
||||
self.facts['interfaces'][key]['ipv6'].append(ipv6)
|
||||
|
||||
def add_ip_address(self, address, family):
|
||||
if family == 'ipv4':
|
||||
if address not in self.facts['all_ipv4_addresses']:
|
||||
self.facts['all_ipv4_addresses'].append(address)
|
||||
else:
|
||||
if address not in self.facts['all_ipv6_addresses']:
|
||||
self.facts['all_ipv6_addresses'].append(address)
|
||||
|
||||
def parse_neighbors(self, data):
|
||||
facts = dict()
|
||||
for elem in data:
|
||||
if 'lldpPortNbrInfoShort' not in elem:
|
||||
continue
|
||||
intf = str(elem['lldpPortNbrInfoShort']['port'])
|
||||
if intf not in facts:
|
||||
facts[intf] = list()
|
||||
fact = dict()
|
||||
fact['host'] = elem['lldpPortNbrInfoShort']['nbrSysName']
|
||||
fact['port'] = str(elem['lldpPortNbrInfoShort']['nbrPortID'])
|
||||
facts[intf].append(fact)
|
||||
return facts
|
||||
|
||||
FACT_SUBSETS = dict(
|
||||
default=Default,
|
||||
hardware=Hardware,
|
||||
interfaces=Interfaces,
|
||||
config=Config)
|
||||
|
||||
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
|
||||
|
||||
|
||||
def main():
|
||||
"""main entry point for module execution
|
||||
"""
|
||||
argument_spec = dict(
|
||||
gather_subset=dict(default=["!config"], type='list')
|
||||
)
|
||||
|
||||
module = AnsibleModule(argument_spec=argument_spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
gather_subset = module.params['gather_subset']
|
||||
|
||||
runable_subsets = set()
|
||||
exclude_subsets = set()
|
||||
|
||||
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='Bad 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')
|
||||
|
||||
facts = dict()
|
||||
facts['gather_subset'] = list(runable_subsets)
|
||||
|
||||
instances = list()
|
||||
for key in runable_subsets:
|
||||
instances.append(FACT_SUBSETS[key](module))
|
||||
|
||||
for inst in instances:
|
||||
inst.populate()
|
||||
facts.update(inst.facts)
|
||||
|
||||
ansible_facts = dict()
|
||||
for key, value in iteritems(facts):
|
||||
key = 'ansible_net_%s' % key
|
||||
ansible_facts[key] = value
|
||||
|
||||
warnings = list()
|
||||
|
||||
module.exit_json(ansible_facts=ansible_facts)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -0,0 +1 @@
|
||||
[{"CLIoutput": "\n Neighbor Neighbor Neighbor\nPort Chassis ID Port ID TTL Age System Name\n===============================================================================\n1 00:02:02:02:02:02 1 120 26 EXOS-VM\n2 00:02:02:02:02:02 2 120 25 EXOS-VM\n3 00:02:02:02:02:02 3 120 25 EXOS-VM\n===============================================================================\nNOTE: The Chassis ID and/or Port ID might be truncated to fit the screen.\n\n"}, {"lldpPortNbrInfoShort": {"age": 26, "lastUpdate": 8412, "nbrChassisID": "00:02:02:02:02:02", "nbrChassisIdType": 4, "nbrIndex": 1, "nbrPortDescr": "Not-Advertised", "nbrPortID": 1, "nbrPortIdType": 5, "nbrSysDescr": "ExtremeXOS (EXOS-VM) version 30.1.0.27 xos_30.1 by lrichardson on Mon Apr 30 13:38:10 EDT 2018", "nbrSysName": "EXOS-VM", "nbrsOnThisPort": 1, "port": 1, "ttl": 120}, "status": "MORE"}, {"lldpPortNbrInfoShort": {"age": 25, "lastUpdate": 8412, "nbrChassisID": "00:02:02:02:02:02", "nbrChassisIdType": 4, "nbrIndex": 1, "nbrPortDescr": "Not-Advertised", "nbrPortID": 2, "nbrPortIdType": 5, "nbrSysDescr": "ExtremeXOS (EXOS-VM) version 30.1.0.27 xos_30.1 by lrichardson on Mon Apr 30 13:38:10 EDT 2018", "nbrSysName": "EXOS-VM", "nbrsOnThisPort": 1, "port": 2, "ttl": 120}, "status": "MORE"}, {"lldpPortNbrInfoShort": {"age": 25, "lastUpdate": 8417, "nbrChassisID": "00:02:02:02:02:02", "nbrChassisIdType": 4, "nbrIndex": 1, "nbrPortDescr": "Not-Advertised", "nbrPortID": 3, "nbrPortIdType": 5, "nbrSysDescr": "ExtremeXOS (EXOS-VM) version 30.1.0.27 xos_30.1 by lrichardson on Mon Apr 30 13:38:10 EDT 2018", "nbrSysName": "EXOS-VM", "nbrsOnThisPort": 1, "port": 3, "ttl": 120}, "status": "MORE"}, {"status": "SUCCESS"}]
|
@ -0,0 +1 @@
|
||||
[{"CLIoutput": "Port Configuration\nPort Virtual Port Link Auto Speed Duplex Flow Load Media\n router State State Neg Cfg Actual Cfg Actual Cntrl Master Pri Red\n================================================================================\n1 VR-Default E R OFF 25000 FULL NONE \n2 VR-Default E R OFF 25000 FULL NONE \n3 VR-Default E R OFF 25000 FULL NONE \n4 VR-Default E R OFF 25000 FULL NONE \n================================================================================\n> indicates Port Display Name truncated past 8 characters\nLink State: A-Active, R-Ready, NP-Port Not Present, L-Loopback\nPort State: D-Disabled, E-Enabled, L-License Disabled\nMedia: !-Unsupported, $-Unlicensed\nMedia Red: * - use \"show port info detail\" for redundant media type\nFlow Cntrl: Shows link partner's abilities. NONE if Auto Neg is OFF\n"}, {"show_ports_config": {"duplexActual": null, "duplexCfg": "FULL", "flowControl": null, "isAutoNegOn": 0, "licenseDisable": 0, "linkState": 0, "port": 1, "portList": "1-4", "portState": 1, "primaryMedia": " NONE", "speedActual": null, "speedCfg": 25000, "vrName": "VR-Default"}, "status": "MORE"}, {"show_ports_config": {"duplexActual": null, "duplexCfg": "FULL", "flowControl": null, "isAutoNegOn": 0, "licenseDisable": 0, "linkState": 0, "port": 2, "portList": "1-4", "portState": 1, "primaryMedia": " NONE", "speedActual": null, "speedCfg": 25000, "vrName": "VR-Default"}, "status": "MORE"}, {"show_ports_config": {"duplexActual": null, "duplexCfg": "FULL", "flowControl": null, "isAutoNegOn": 0, "licenseDisable": 0, "linkState": 0, "port": 3, "portList": "1-4", "portState": 1, "primaryMedia": " NONE", "speedActual": null, "speedCfg": 25000, "vrName": "VR-Default"}, "status": "MORE"}, {"show_ports_config": {"duplexActual": null, "duplexCfg": "FULL", "flowControl": null, "isAutoNegOn": 0, "licenseDisable": 0, "linkState": 0, "port": 4, "portList": "1-4", "portState": 1, "primaryMedia": " NONE", "speedActual": null, "speedCfg": 25000, "vrName": "VR-Default"}, "status": "SUCCESS"}]
|
@ -0,0 +1,2 @@
|
||||
[{"CLIoutput": "Port Display String Description String \n===== ==================== ==================================================\n1 Firewall\n2 \n3 Database Server\n4 \n===== ==================== ==================================================\n"}, {"show_ports_description": {"descriptionString": "Firewall", "port": 1, "portList": "1-4"}, "status": "MORE"}, {"show_ports_description": {"port": 2, "portList": "1-4"}, "status": "MORE"}, {"show_ports_description": {"descriptionString": "Database Server", "port": 3, "portList": "1-4"}, "status": "MORE"}, {"show_ports_description": {"port": 4, "portList": "1-4"}, "status": "SUCCESS"}]
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,95 @@
|
||||
|
||||
System Memory Information
|
||||
-------------------------
|
||||
Total DRAM (KB): 8388608
|
||||
System (KB): 357088
|
||||
User (KB): 558460
|
||||
Free (KB): 7473060
|
||||
|
||||
Memory Utilization Statistics
|
||||
-----------------------------
|
||||
|
||||
Process Name Memory (KB)
|
||||
-----------------------------
|
||||
aaa 2212
|
||||
acl 1637
|
||||
bfd 1158
|
||||
bgp 10031
|
||||
brm 822
|
||||
cfgmgr 2466
|
||||
cli 16169
|
||||
devmgr 884
|
||||
dirser 463
|
||||
dosprotect 570
|
||||
dot1ag 1370
|
||||
eaps 1359
|
||||
edp 1260
|
||||
elrp 1250
|
||||
elsm 917
|
||||
ems 3196
|
||||
epm 1646
|
||||
erps 1282
|
||||
esrp 1101
|
||||
ethoam 858
|
||||
etmon 7865
|
||||
exacl 0
|
||||
exdhcpsnoop 0
|
||||
exdos 0
|
||||
exfib 0
|
||||
exnasnoop 0
|
||||
exosmc 0
|
||||
exosq 0
|
||||
expolicy 0
|
||||
exsflow 0
|
||||
exsnoop 0
|
||||
exsshd 1522
|
||||
exvlan 0
|
||||
fdb 1990
|
||||
hal 141451
|
||||
hclag 899
|
||||
idMgr 3448
|
||||
ipSecurity 1042
|
||||
ipfix 956
|
||||
isis 1403
|
||||
ismb 0
|
||||
lacp 1306
|
||||
lldp 1724
|
||||
mcmgr 2183
|
||||
mpls 0
|
||||
mrp 1482
|
||||
msdp 915
|
||||
netLogin 1641
|
||||
netTools 4336
|
||||
nettx 0
|
||||
nodealias 1847
|
||||
nodemgr 501
|
||||
ntp 812
|
||||
openflow 0
|
||||
ospf 1455
|
||||
ospfv3 5130
|
||||
otm 1095
|
||||
ovsdb 8206
|
||||
pim 2100
|
||||
polMgr 479
|
||||
policy 45998
|
||||
pwmib 458
|
||||
rip 1000
|
||||
ripng 739
|
||||
rtmgr 2679
|
||||
snmpMaster 2798
|
||||
snmpSubagent 5728
|
||||
stp 2020
|
||||
techSupport 681
|
||||
telnetd 890
|
||||
tftpd 336
|
||||
throw 5262
|
||||
thttpd 8944
|
||||
twamp 471
|
||||
upm 859
|
||||
vlan 3215
|
||||
vmt 1599
|
||||
vpex 1771
|
||||
vrrp 1185
|
||||
vsm 1486
|
||||
xmlc 1013
|
||||
xmld 3468
|
@ -0,0 +1,33 @@
|
||||
|
||||
SysName: X870-32c
|
||||
SysLocation:
|
||||
SysContact: support@extremenetworks.com, +1 888 257 3000
|
||||
System MAC: 00:04:96:9A:B4:F7
|
||||
System Type: X870-32c
|
||||
|
||||
SysHealth check: Enabled (Normal)
|
||||
Recovery Mode: All
|
||||
System Watchdog: Enabled
|
||||
|
||||
Current Time: Wed Jul 18 12:44:49 2018
|
||||
Timezone: [Auto DST Disabled] GMT Offset: 0 minutes, name is UTC.
|
||||
Boot Time: Tue Jul 17 12:49:58 2018
|
||||
Boot Count: 4970
|
||||
Next Reboot: None scheduled
|
||||
System UpTime: 23 hours 54 minutes 50 seconds
|
||||
|
||||
Current State: OPERATIONAL
|
||||
Image Selected: secondary
|
||||
Image Booted: secondary
|
||||
Primary ver: 30.1.0.37
|
||||
Secondary ver: 22.5.1.7
|
||||
|
||||
Config Selected: primary.cfg
|
||||
Config Booted: primary.cfg
|
||||
Config Automatic: NONE (Disabled)
|
||||
|
||||
primary.cfg Created by ExtremeXOS version 22.6.0.11
|
||||
983139 bytes saved on Wed Jun 6 16:59:49 2018
|
||||
|
||||
LAA MAC: Locally Administered MAC Address Disabled
|
||||
|
@ -0,0 +1,111 @@
|
||||
#
|
||||
# (c) 2018 Extreme Networks Inc.
|
||||
#
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
import json
|
||||
|
||||
from ansible.compat.tests.mock import patch
|
||||
from ansible.modules.network.exos import exos_facts
|
||||
from units.modules.utils import set_module_args
|
||||
from .exos_module import TestExosModule, load_fixture
|
||||
|
||||
|
||||
class TestExosFactsModule(TestExosModule):
|
||||
|
||||
module = exos_facts
|
||||
|
||||
def setUp(self):
|
||||
super(TestExosFactsModule, self).setUp()
|
||||
|
||||
self.mock_run_commands = patch('ansible.modules.network.exos.exos_facts.run_commands')
|
||||
self.run_commands = self.mock_run_commands.start()
|
||||
|
||||
def tearDown(self):
|
||||
super(TestExosFactsModule, self).tearDown()
|
||||
self.mock_run_commands.stop()
|
||||
|
||||
def load_fixtures(self, commands=None):
|
||||
|
||||
def load_from_file(*args, **kwargs):
|
||||
module, commands = args
|
||||
output = list()
|
||||
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
|
||||
|
||||
for command in commands:
|
||||
filename = str(command).replace(' ', '_')
|
||||
filename = os.path.join(fixture_path, filename)
|
||||
with open(filename) as f:
|
||||
data = f.read()
|
||||
output.append(data)
|
||||
return output
|
||||
|
||||
self.run_commands.side_effect = load_from_file
|
||||
|
||||
def test_exos_facts_default(self):
|
||||
set_module_args(dict(gather_subset='default'))
|
||||
result = self.execute_module()
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_model'], 'X870-32c'
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_serialnum'], '1604G-00175'
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_version'], '22.5.1.7'
|
||||
)
|
||||
|
||||
def test_exos_facts_hardware(self):
|
||||
set_module_args(dict(gather_subset='hardware'))
|
||||
result = self.execute_module()
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_memfree_mb'], 7298
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_memtotal_mb'], 8192
|
||||
)
|
||||
|
||||
def test_exos_facts_interfaces(self):
|
||||
set_module_args(dict(gather_subset='interfaces'))
|
||||
result = self.execute_module()
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_interfaces']['1']['bandwidth_configured'], '25000'
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_interfaces']['3']['description'], 'Database Server'
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_interfaces']['3']['type'], 'Ethernet'
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_interfaces']['vlan1']['ipv4'][0]['address'], '10.0.1.1'
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_interfaces']['vlan3']['ipv6'][0]['address'], 'fe80::202:b3ff:fe1e:8329'
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_all_ipv4_addresses'], ['10.0.1.1', '192.168.1.1']
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_all_ipv6_addresses'], ['fe80::202:b3ff:fe1e:8329']
|
||||
)
|
||||
self.assertEqual(
|
||||
result['ansible_facts']['ansible_net_interfaces']['vlan3']['type'], 'VLAN'
|
||||
)
|
Loading…
Reference in New Issue