1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00

New enos_facts, + module_utils/enos.py. modifying copyright year in rest all (#31696)

* Squashing all commits to one as suggested by John

* Adding Unit test method for the module enos_facts.py

* Pep8 and Ylint issues addressed

* Trying again to remove blank line. Some scripts are required for this.

* Bug Fixing for interfaces

* Editing for over indenting issue

* E203 whitespace before ','

* Update enos.py

Added warnings argument as to check_args method

* Update enos_facts.py

Added warnings to check_args method
This commit is contained in:
Anil Kumar Muraleedharan 2017-11-08 21:25:46 +05:30 committed by John R Barker
parent e5dbf63b65
commit 9d98452032
16 changed files with 1330 additions and 3 deletions

View file

@ -0,0 +1,162 @@
# This code is part of Ansible, but is an independent component.
# This particular file snippet, and this file snippet only, is BSD licensed.
# Modules you write using this snippet, which is embedded dynamically by
# Ansible still belong to the author of the module, and may assign their own
# license to the complete work.
#
# Copyright (C) 2017 Lenovo, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Contains utility methods
# Lenovo Networking
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import env_fallback, return_values
from ansible.module_utils.network_common import to_list, EntityCollection
from ansible.module_utils.connection import Connection, exec_command
_DEVICE_CONFIGS = {}
_CONNECTION = None
enos_provider_spec = {
'host': dict(),
'port': dict(type='int'),
'username': dict(fallback=(env_fallback, ['ANSIBLE_NET_USERNAME'])),
'password': dict(fallback=(env_fallback, ['ANSIBLE_NET_PASSWORD']), no_log=True),
'ssh_keyfile': dict(fallback=(env_fallback, ['ANSIBLE_NET_SSH_KEYFILE']), type='path'),
'authorize': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTHORIZE']), type='bool'),
'auth_pass': dict(fallback=(env_fallback, ['ANSIBLE_NET_AUTH_PASS']), no_log=True),
'timeout': dict(type='int'),
'context': dict(),
'passwords': dict()
}
enos_argument_spec = {
'provider': dict(type='dict', options=enos_provider_spec),
}
command_spec = {
'command': dict(key=True),
'prompt': dict(),
'answer': dict()
}
def get_provider_argspec():
return enos_provider_spec
def check_args(module, warnings):
pass
def get_connection(module):
global _CONNECTION
if _CONNECTION:
return _CONNECTION
_CONNECTION = Connection(module)
context = None
try:
context = module.params['context']
except KeyError:
context = None
if context:
if context == 'system':
command = 'changeto system'
else:
command = 'changeto context %s' % context
_CONNECTION.get(command)
return _CONNECTION
def get_config(module, flags=None):
flags = [] if flags is None else flags
passwords = module.params['passwords']
if passwords:
cmd = 'more system:running-config'
else:
cmd = 'show running-config '
cmd += ' '.join(flags)
cmd = cmd.strip()
try:
return _DEVICE_CONFIGS[cmd]
except KeyError:
conn = get_connection(module)
out = conn.get(cmd)
cfg = to_text(out, errors='surrogate_then_replace').strip()
_DEVICE_CONFIGS[cmd] = cfg
return cfg
def to_commands(module, commands):
assert isinstance(commands, list), 'argument must be of type <list>'
transform = EntityCollection(module, command_spec)
commands = transform(commands)
for index, item in enumerate(commands):
if module.check_mode and not item['command'].startswith('show'):
module.warn('only show commands are supported when using check '
'mode, not executing `%s`' % item['command'])
return commands
def run_commands(module, commands, check_rc=True):
connection = get_connection(module)
commands = to_commands(module, to_list(commands))
responses = list()
for cmd in commands:
out = connection.get(**cmd)
responses.append(to_text(out, errors='surrogate_then_replace'))
return responses
def load_config(module, config):
conn = get_connection(module)
conn.edit_config(config)
def get_defaults_flag(module):
rc, out, err = exec_command(module, 'show running-config ?')
out = to_text(out, errors='surrogate_then_replace')
commands = set()
for line in out.splitlines():
if line:
commands.add(line.strip().split()[0])
if 'all' in commands:
return 'all'
else:
return 'full'

View file

@ -0,0 +1,488 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function
__metaclass__ = type
#
# Copyright (C) 2017 Lenovo, 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/>.
#
# Module to Collect facts from Lenovo Switches running Lenovo ENOS commands
# Lenovo Networking
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: enos_facts
version_added: "2.5"
author: "Anil Kumar Muraleedharan (@amuraleedhar)"
short_description: Collect facts from remote devices running Lenovo ENOS
description:
- Collects a base set of device facts from a remote Lenovo device
running on ENOS. 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.
extends_documentation_fragment: enos
notes:
- Tested against ENOS 8.4.1.68
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 = '''
Tasks: The following are examples of using the module enos_facts.
---
- name: Test Enos Facts
enos_facts:
provider={{ cli }}
vars:
cli:
host: "{{ inventory_hostname }}"
port: 22
username: admin
password: admin
transport: cli
timeout: 30
authorize: True
auth_pass:
---
# Collect all facts from the device
- enos_facts:
gather_subset: all
provider: "{{ cli }}"
# Collect only the config and default facts
- enos_facts:
gather_subset:
- config
provider: "{{ cli }}"
# Do not collect hardware facts
- enos_facts:
gather_subset:
- "!hardware"
provider: "{{ cli }}"
'''
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 Lenovo ENOS device
returned: always
type: str
ansible_net_serialnum:
description: The serial number of the Lenovo ENOS device
returned: always
type: str
ansible_net_version:
description: The ENOS 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
ansible_net_image:
description: Indicates the active image for 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
# 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 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.
This gives information on description, mac address, mtu, speed,
duplex and operstatus
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
from ansible.module_utils.enos import run_commands, enos_argument_spec, check_args
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import iteritems
from ansible.module_utils.six.moves import zip
class FactsBase(object):
COMMANDS = list()
def __init__(self, module):
self.module = module
self.facts = dict()
self.responses = None
self.PERSISTENT_COMMAND_TIMEOUT = 60
def populate(self):
self.responses = run_commands(self.module, self.COMMANDS,
check_rc=False)
def run(self, cmd):
return run_commands(self.module, cmd, check_rc=False)
class Default(FactsBase):
COMMANDS = ['show version', 'show run']
def populate(self):
super(Default, self).populate()
data = self.responses[0]
data_run = self.responses[1]
if data:
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)
if data_run:
self.facts['hostname'] = self.parse_hostname(data_run)
def parse_version(self, data):
match = re.search(r'^Software Version (.*?) ', data, re.M | re.I)
if match:
return match.group(1)
def parse_hostname(self, data_run):
for line in data_run.split('\n'):
line = line.strip()
match = re.match(r'hostname (.*?)', line, re.M | re.I)
if match:
hosts = line.split()
hostname = hosts[1].strip('\"')
return hostname
return "NA"
def parse_model(self, data):
# match = re.search(r'^Cisco (.+) \(revision', data, re.M)
match = re.search(r'^Lenovo RackSwitch (\S+)', data, re.M | re.I)
if match:
return match.group(1)
def parse_image(self, data):
match = re.search(r'(.*) image1(.*)', data, re.M | re.I)
if match:
return "Image1"
else:
return "Image2"
def parse_serialnum(self, data):
# match = re.search(r'board ID (\S+)', data)
match = re.search(r'^Switch Serial No: (\S+)', data, re.M | re.I)
if match:
return match.group(1)
class Hardware(FactsBase):
COMMANDS = [
'show system memory'
]
def populate(self):
super(Hardware, self).populate()
data = self.responses[0]
if data:
self.facts['memtotal_mb'] = self.parse_memtotal(data)
self.facts['memfree_mb'] = self.parse_memfree(data)
def parse_memtotal(self, data):
match = re.search(r'^MemTotal:\s*(.*) kB', data, re.M | re.I)
if match:
return int(match.group(1)) / 1024
def parse_memfree(self, data):
match = re.search(r'^MemFree:\s*(.*) kB', data, re.M | re.I)
if match:
return int(match.group(1)) / 1024
class Config(FactsBase):
COMMANDS = ['show running-config']
def populate(self):
super(Config, self).populate()
data = self.responses[0]
if data:
self.facts['config'] = data
class Interfaces(FactsBase):
COMMANDS = ['show interface status']
def populate(self):
super(Interfaces, self).populate()
self.facts['all_ipv4_addresses'] = list()
self.facts['all_ipv6_addresses'] = list()
data1 = self.run(['show interface status'])
data1 = to_text(data1, errors='surrogate_or_strict').strip()
data1 = data1.replace(r"\n", "\n")
data2 = self.run(['show lldp port'])
data2 = to_text(data2, errors='surrogate_or_strict').strip()
data2 = data2.replace(r"\n", "\n")
lines1 = None
lines2 = None
if data1:
lines1 = self.parse_interfaces(data1)
if data2:
lines2 = self.parse_interfaces(data2)
if lines1 is not None and lines2 is not None:
self.facts['interfaces'] = self.populate_interfaces(lines1, lines2)
data3 = self.run(['show lldp remote-device port'])
data3 = to_text(data3, errors='surrogate_or_strict').strip()
data3 = data3.replace(r"\n", "\n")
lines3 = None
if data3:
lines3 = self.parse_neighbors(data3)
if lines3 is not None:
self.facts['neighbors'] = self.populate_neighbors(lines3)
data4 = self.run(['show interface ip'])
data4 = data4[0].split('\n')
lines4 = None
if data4:
lines4 = self.parse_ipaddresses(data4)
ipv4_interfaces = self.set_ipv4_interfaces(lines4)
self.facts['all_ipv4_addresses'] = ipv4_interfaces
ipv6_interfaces = self.set_ipv6_interfaces(lines4)
self.facts['all_ipv6_addresses'] = ipv6_interfaces
def parse_ipaddresses(self, data4):
parsed = list()
for line in data4:
if len(line) == 0:
continue
else:
line = line.strip()
if len(line) == 0:
continue
match = re.search(r'IP4', line, re.M | re.I)
if match:
key = match.group()
parsed.append(line)
match = re.search(r'IP6', line, re.M | re.I)
if match:
key = match.group()
parsed.append(line)
return parsed
def set_ipv4_interfaces(self, line4):
ipv4_addresses = list()
for line in line4:
ipv4Split = line.split()
if ipv4Split[1] == "IP4":
ipv4_addresses.append(ipv4Split[2])
return ipv4_addresses
def set_ipv6_interfaces(self, line4):
ipv6_addresses = list()
for line in line4:
ipv6Split = line.split()
if ipv6Split[1] == "IP6":
ipv6_addresses.append(ipv6Split[2])
return ipv6_addresses
def populate_neighbors(self, lines3):
neighbors = dict()
for line in lines3:
neighborSplit = line.split("|")
innerData = dict()
innerData['Remote Chassis ID'] = neighborSplit[2].strip()
innerData['Remote Port'] = neighborSplit[3].strip()
sysName = neighborSplit[4].strip()
if sysName is not None:
innerData['Remote System Name'] = neighborSplit[4].strip()
else:
innerData['Remote System Name'] = "NA"
neighbors[neighborSplit[0].strip()] = innerData
return neighbors
def populate_interfaces(self, lines1, lines2):
interfaces = dict()
for line1, line2 in zip(lines1, lines2):
line = line1 + " " + line2
intfSplit = line.split()
innerData = dict()
innerData['description'] = intfSplit[6].strip()
innerData['macaddress'] = intfSplit[8].strip()
innerData['mtu'] = intfSplit[9].strip()
innerData['speed'] = intfSplit[1].strip()
innerData['duplex'] = intfSplit[2].strip()
innerData['operstatus'] = intfSplit[5].strip()
interfaces[intfSplit[0].strip()] = innerData
return interfaces
def parse_neighbors(self, neighbors):
parsed = list()
for line in neighbors.split('\n'):
if len(line) == 0:
continue
else:
line = line.strip()
match = re.match(r'^([0-9]+)', line)
if match:
key = match.group(1)
parsed.append(line)
match = re.match(r'^(MGT+)', line)
if match:
key = match.group(1)
parsed.append(line)
return parsed
def parse_interfaces(self, data):
parsed = list()
for line in data.split('\n'):
if len(line) == 0:
continue
else:
line = line.strip()
match = re.match(r'^([0-9]+)', line)
if match:
key = match.group(1)
parsed.append(line)
match = re.match(r'^(MGT+)', line)
if match:
key = match.group(1)
parsed.append(line)
return parsed
FACT_SUBSETS = dict(
default=Default,
hardware=Hardware,
interfaces=Interfaces,
config=Config,
)
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
PERSISTENT_COMMAND_TIMEOUT = 60
def main():
"""main entry point for module execution
"""
argument_spec = dict(
gather_subset=dict(default=['!config'], type='list')
)
argument_spec.update(enos_argument_spec)
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()
check_args(module, warnings)
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
if __name__ == '__main__':
main()

View file

@ -4,7 +4,7 @@
# Ansible still belong to the author of the module, and may assign their own
# license to the complete work.
#
# Copyright (C) 2018 Lenovo, Inc.
# Copyright (C) 2017 Lenovo, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without

View file

@ -4,7 +4,7 @@
# Ansible still belong to the author of the module, and may assign their own
# license to the complete work.
#
# Copyright (C) 2018 Lenovo, Inc.
# Copyright (C) 2017 Lenovo, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without

View file

@ -4,7 +4,7 @@
# Ansible still belong to the author of the module, and may assign their own
# license to the complete work.
#
# Copyright (C) 2018 Lenovo, Inc.
# Copyright (C) 2017 Lenovo, Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without

View file

@ -0,0 +1,114 @@
# (c) 2016 Red Hat 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/>.
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import json
from ansible.compat.tests import unittest
from ansible.compat.tests.mock import patch
from ansible.module_utils import basic
from ansible.module_utils._text import to_bytes
def set_module_args(args):
args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
basic._ANSIBLE_ARGS = to_bytes(args)
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures')
fixture_data = {}
def load_fixture(name):
path = os.path.join(fixture_path, name)
if path in fixture_data:
return fixture_data[path]
with open(path) as f:
data = f.read()
try:
data = json.loads(data)
except:
pass
fixture_data[path] = data
return data
class AnsibleExitJson(Exception):
pass
class AnsibleFailJson(Exception):
pass
class TestEnosModule(unittest.TestCase):
def execute_module(self, failed=False, changed=False, commands=None, sort=True, defaults=False):
self.load_fixtures(commands)
if failed:
result = self.failed()
self.assertTrue(result['failed'], result)
else:
result = self.changed(changed)
self.assertEqual(result['changed'], changed, result)
if commands is not None:
if sort:
self.assertEqual(sorted(commands), sorted(result['commands']), result['commands'])
else:
self.assertEqual(commands, result['commands'], result['commands'])
return result
def failed(self):
def fail_json(*args, **kwargs):
kwargs['failed'] = True
raise AnsibleFailJson(kwargs)
with patch.object(basic.AnsibleModule, 'fail_json', fail_json):
with self.assertRaises(AnsibleFailJson) as exc:
self.module.main()
result = exc.exception.args[0]
self.assertTrue(result['failed'], result)
return result
def changed(self, changed=False):
def exit_json(*args, **kwargs):
if 'changed' not in kwargs:
kwargs['changed'] = False
raise AnsibleExitJson(kwargs)
with patch.object(basic.AnsibleModule, 'exit_json', exit_json):
with self.assertRaises(AnsibleExitJson) as exc:
self.module.main()
result = exc.exception.args[0]
self.assertEqual(result['changed'], changed, result)
return result
def load_fixtures(self, commands=None):
pass

View file

@ -0,0 +1,9 @@
Interface information:
1: IP4 192.168.49.50 255.255.255.0 192.168.49.255, vlan 1, up
128: IP4 10.241.105.24 255.255.255.0 10.241.105.255, vlan 4095, up
Routed Port Interface Information:
Loopback interface information:

View file

@ -0,0 +1,59 @@
-----------------------------------------------------------------------
Port Speed Duplex Flow Ctrl Link Description
------- ------ -------- --TX-----RX-- ------ -------------
1 1G/10G full no no down 1
2 1G/10G full no no down 2
3 1G/10G full no no down 3
4 1G/10G full no no down 4
5 1G/10G full no no down 5
6 1G/10G full no no down 6
7 1G/10G full no no down 7
8 1G/10G full no no down 8
9 1G/10G full no no down 9
10 1G/10G full no no down 10
11 1G/10G full no no down 11
12 1G/10G full no no down 12
13 1G/10G full no no down 13
14 1G/10G full no no down 14
15 1G/10G full no no down 15
16 1G/10G full no no down 16
17 1G/10G full no no down 17
18 1G/10G full no no down 18
19 1G/10G full no no down 19
20 1G/10G full no no down 20
21 1G/10G full no no down 21
22 1G/10G full no no down 22
23 1G/10G full no no down 23
24 1G/10G full no no down 24
25 1G/10G full no no down 25
26 1G/10G full no no down 26
27 1G/10G full no no down 27
28 1G/10G full no no down 28
29 1G/10G full no no down 29
30 1G/10G full no no down 30
31 1G/10G full no no down 31
32 1G/10G full no no down 32
33 1G/10G full no no down 33
34 1G/10G full no no down 34
35 1G/10G full no no down 35
36 1G/10G full no no down 36
37 1G/10G full no no down 37
38 1000 full no no up 38
39 1G/10G full no no down 39
40 1G/10G full no no down 40
41 1G/10G full no no down 41
42 1G/10G full no no down 42
43 1G/10G full no no down 43
44 1G/10G full no no down 44
45 1G/10G full no no down 45
46 1G/10G full no no down 46
47 1G/10G full no no down 47
48 10000 full no no down 48
49 40000 full no no down 49
50 40000 full no no down 50
51 40000 full no no down 51
52 40000 full no no down 52
53 40000 full no no down 53
54 40000 full no no down 54
MGT 1000 full yes yes up MGT

View file

@ -0,0 +1,60 @@
LLDP Port Info
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Port MAC address MTU PortEnabled AdminStatus RxChange TrapNotify
======= ================= ==== =========== =========== ======== ==========
1 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
2 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
3 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
4 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
5 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
6 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
7 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
8 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
9 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
10 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
11 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
12 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
13 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
14 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
15 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
16 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
17 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
18 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
19 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
20 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
21 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
22 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
23 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
24 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
25 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
26 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
27 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
28 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
29 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
30 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
31 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
32 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
33 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
34 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
35 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
36 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
37 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
38 a8:97:dc:dd:e2:00 9216 enabled tx_rx no disabled
39 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
40 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
41 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
42 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
43 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
44 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
45 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
46 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
47 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
48 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
49 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
50 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
51 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
52 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
53 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
54 a8:97:dc:dd:e2:00 9216 disabled tx_rx no disabled
MGT a8:97:dc:dd:e2:fe 9216 enabled tx_rx no disabled

View file

@ -0,0 +1,12 @@
LLDP Remote Devices Information
Legend(possible values in DMAC column) :
NB - Nearest Bridge - 01-80-C2-00-00-0E
NnTB - Nearest non-TPMR Bridge - 01-80-C2-00-00-03
NCB - Nearest Customer Bridge - 01-80-C2-00-00-00
Total number of current entries: 1
LocalPort | Index | Remote Chassis ID | Remote Port | Remote System Name | DMAC
----------|-------|---------------------------|----------------------|-------------------------------|---------
MGT | 1 | 74 26 ac 3d 3c c0 | Gi3/18 | INDIA-LAB-1-C4506E-A.labs.l...| NB

View file

@ -0,0 +1,59 @@
Current configuration:
!
version "8.4.3.12"
switch-type "Lenovo RackSwitch G8272"
iscli-new
!
!
access https enable
snmp-server location "Location:,Room:,Rack:Rack 3,LRU:40"
snmp-server read-community "public"
snmp-server trap-source 128
!
!
!
no system dhcp
no system default-ip mgt
hostname "test1"
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!interface ip 1
! addr <default>
! enable
!
interface ip 128
ip address 10.241.105.24 255.255.255.0
enable
exit
!
ip gateway 4 address 10.241.105.1
ip gateway 4 enable
!
!
!
!
!
!
router bgp
as 100
!
!
end

View file

@ -0,0 +1,59 @@
Current configuration:
!
version "8.4.3.12"
switch-type "Lenovo RackSwitch G8272"
iscli-new
!
!
access https enable
snmp-server location "Location:,Room:,Rack:Rack 3,LRU:40"
snmp-server read-community "public"
snmp-server trap-source 128
!
!
!
no system dhcp
no system default-ip mgt
hostname "test1"
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!
!interface ip 1
! addr <default>
! enable
!
interface ip 128
ip address 10.241.105.24 255.255.255.0
enable
exit
!
ip gateway 4 address 10.241.105.1
ip gateway 4 enable
!
!
!
!
!
!
router bgp
as 100
!
!
end

View file

@ -0,0 +1,166 @@
------------------------------------------------------------------
Memory utilization:
MemTotal: 4088580 kB
MemFree: 3464304 kB
MemAvailable: 3586864 kB
Buffers: 2016 kB
Cached: 173236 kB
SwapCached: 0 kB
Active: 504316 kB
Inactive: 38056 kB
Active(anon): 376332 kB
Inactive(anon): 27460 kB
Active(file): 127984 kB
Inactive(file): 10596 kB
Unevictable: 0 kB
Mlocked: 0 kB
HighTotal: 3407860 kB
HighFree: 2952904 kB
LowTotal: 680720 kB
LowFree: 511400 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 0 kB
Writeback: 0 kB
AnonPages: 367120 kB
Mapped: 20664 kB
Shmem: 36672 kB
Slab: 8240 kB
SReclaimable: 3492 kB
SUnreclaim: 4748 kB
KernelStack: 760 kB
PageTables: 1592 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 2044288 kB
Committed_AS: 1128364 kB
VmallocTotal: 241652 kB
VmallocUsed: 17116 kB
VmallocChunk: 223920 kB
HugePages_Total: 0
HugePages_Free: 0
HugePagesPercentage used 15
Memory tracing: enabled
Extended Memory tracing: disabled
High-water monitoring: enabled
Memory high-water: 20 percent (at 1818 seconds from boot)
Memory stats:
allocs: 16484474
frees: 16481108
current: 3378
alloc fails: 0
STEM thread memory stats:
thid name allocs frees diff current * largest
0 INIT 2655 933 1722 69381079 31982881
1 STEM 0 0 0 0 0
2 STP 13 6 7 41165721 16673868
3 MFDB 1 0 1 6 6
4 TND 41745 42134 -389 847441 336
5 CONS 3867 3866 1 26622356 6291456
6 TNET 3806775 3809157 -2382 1032303745 12582912
7 TNET 126519 127060 -541 269598653 12582912
8 TNET 1 0 1 6131 6131
9 TNET 1 0 1 6131 6131
10 TNET 1 0 1 6131 6131
11 TNET 1 0 1 6131 6131
12 LOG 441 441 0 61437 272
13 TRAP 16911 16911 0 1416745 544
14 NTP 0 0 0 0 0
15 RMON 0 0 0 0 0
18 IP 40 7 33 26152 4248
19 RIP 0 0 0 0 0
20 AGR 24643 23177 1466 8949189 6131
21 EPI 0 0 0 0 0
22 PORT 56 0 56 60032 16384
23 BGP 0 10 -10 0 0
27 MGMT 335 162 173 48883648 524436
28 SCAN 0 0 0 0 0
29 OSPF 0 20 -20 0 0
30 VRRP 1 0 1 16 16
31 SNMP 4670978 4670164 814 2315549793 12582912
32 SNMP 1108 1068 40 208175203 12582912
34 SSHD 800286 796910 3376 271976834 2017
36 DT1X 0 0 0 0 0
37 NCFD 1 0 1 6131 6131
38 NCFD 1 0 1 6131 6131
39 NCFD 1 0 1 6131 6131
40 NCFD 1 0 1 6131 6131
41 SWR 0 0 0 0 0
42 SWRH 0 0 0 0 0
43 OBS 0 0 0 0 0
44 TEAM 0 0 0 0 0
45 I2C 0 0 0 0 0
46 LACP 72 0 72 1152 16
47 SFP 0 0 0 0 0
48 SWKY 0 0 0 0 0
49 HLNK 0 0 0 0 0
50 LLDP 5794454 5794373 81 598072737 14336
51 IPV6 0 0 0 0 0
52 RTM6 0 0 0 0 0
53 PNG6 0 0 0 0 0
55 OSP3 0 0 0 0 0
56 VMAC 0 0 0 0 0
57 MEMM 0 0 0 0 0
58 UDLD 0 0 0 0 0
59 FCOE 0 0 0 0 0
60 SFLO 0 0 0 0 0
61 PROX 0 0 0 0 0
62 OAM 0 0 0 0 0
63 PIM 0 0 0 0 0
64 DCBX 1 1 0 126 126
65 NBOO 1 0 1 6131 6131
66 VLAG 0 0 0 0 0
67 MLD6 0 0 0 0 0
68 DHCP 0 0 0 0 0
69 ETMR 0 0 0 0 0
70 IKE2 0 0 0 0 0
71 ACLG 1 0 1 5120 5120
72 HWRT 0 0 0 0 0
73 OFLO 17 0 17 32244 8048
74 SFM 0 0 0 0 0
75 UPTM 0 0 0 0 0
76 VSDB 0 2 -2 0 0
77 ECPT 3 0 3 168532 168000
78 ECPR 0 0 0 0 0
79 VDPT 5 0 5 5260 1460
80 VFDB 0 1 -1 0 0
81 PTP 0 0 0 0 0
82 PBR 0 0 0 0 0
83 HIST 0 0 0 0 0
84 SLP 699757 701435 -1678 254297215 262140
85 UFP 217 73 144 12908 132
86 CDCP 0 0 0 0 0
87 IGMP 0 0 0 0 0
88 ICMP 0 0 0 0 0
89 HCM 0 0 0 0 0
90 CFCF 0 0 0 0 0
91 FDF@ 0 0 0 0 0
92 NAT 0 0 0 0 0
93 OCM1 11 0 11 44 4
94 OCM2 0 0 0 0 0
95 OFDT 0 0 0 0 0
96 OSFM 5 0 5 2636 1024
97 OBSC 0 0 0 0 0
98 STPM 0 0 0 0 0
99 ARP 0 0 0 0 0
100 VXLN 0 0 0 0 0
101 OVSD 0 0 0 0 0
102 OVSC 0 0 0 0 0
103 VTEP 0 0 0 0 0
104 BFD 18 0 18 440 44
105 STPR 0 0 0 0 0
106 VMFD 0 0 0 0 0
107 NORM 0 0 0 0 0
108 DONE 494136 493788 348 280129530 6291456
Total 16485149 16481768 3381 1132837544
Non-STEM allocs 0
Non-STEM frees 2
Overhead 1780

View file

@ -0,0 +1,60 @@
System Information at 11:37:06 Fri Oct 27, 2017
Time zone: No timezone configured
Daylight Savings Time Status: Disabled
Lenovo RackSwitch G8272
Switch has been up for 30 days, 10 hours, 43 minutes and 14 seconds.
Last boot: 00:53:32 Wed Sep 27, 2017 (power cycle)
MAC address: a8:97:dc:dd:e2:00 IP (If 1) address: 192.168.49.50
Management Port MAC Address: a8:97:dc:dd:e2:fe
Management Port IP Address (if 128): 10.241.105.24
Hardware Revision: 0
Board Revision:
Hardware Part No: 00CJ066
Old Hardware Part No: 2MV4CR01W
Switch Serial No: Y052MV4CR01W
Manufacturing date: 14/51
MTM Value: 7159-HCV
Old MTM Value:
ESN: MM01086
WARNING: This is UNRELEASED SOFTWARE for LAB TESTING ONLY.
DO NOT USE IN A PRODUCTION NETWORK.
Software Version 8.4.3.12 (FLASH image1), active configuration.
Boot kernel version 8.4.3.12
USB Boot: disabled
Temperature CPU Local : 31 C
Temperature Ambient : 32 C
Temperature Hot Spot : 44 C
Temperature Asic Max : 63 C
System Warning at 85 C / Shutdown at 95 C / Set Point is 70 C
Fan 1 Module 1: 4054rpm 60pwm(23%) Front-To-Back
Fan 2 Module 1: 4404rpm 60pwm(23%) Front-To-Back
Fan 3 Module 2: 4112rpm 60pwm(23%) Front-To-Back
Fan 4 Module 2: 4372rpm 60pwm(23%) Front-To-Back
Fan 5 Module 3: 4072rpm 60pwm(23%) Front-To-Back
Fan 6 Module 3: 4306rpm 60pwm(23%) Front-To-Back
Fan 7 Module 4: 4134rpm 60pwm(23%) Front-To-Back
Fan 8 Module 4: 4326rpm 60pwm(23%) Front-To-Back
System Fan Airflow: Front-To-Back
Power Supply 1: Front-To-Back [DPS-460KB C]
Power Supply 2: Front-To-Back [DPS-460KB C]
Power Faults: PS1-Pwr
Fan Faults: None
Service Faults: Too-Few-PS

View file

@ -0,0 +1,79 @@
# (c) 2016 Red Hat 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/>.
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
from ansible.compat.tests.mock import patch
from .enos_module import TestEnosModule, load_fixture, set_module_args
from ansible.modules.network.enos import enos_facts
class TestEnosFacts(TestEnosModule):
module = enos_facts
def setUp(self):
self.mock_run_commands = patch(
'ansible.modules.network.enos.enos_facts.run_commands')
self.run_commands = self.mock_run_commands.start()
def tearDown(self):
self.mock_run_commands.stop()
def load_fixtures(self, commands=None):
def load_from_file(*args, **kwargs):
module, commands = args
output = list()
for item in commands:
try:
obj = json.loads(item)
command = obj['command']
except ValueError:
command = item
filename = str(command).replace(' ', '_')
filename = filename.replace('/', '7')
output.append(load_fixture(filename))
return output
self.run_commands.side_effect = load_from_file
def test_enos_facts_gather_subset_default(self):
set_module_args(dict())
result = self.execute_module()
ansible_facts = result['ansible_facts']
self.assertIn('hardware', ansible_facts['ansible_net_gather_subset'])
self.assertIn('default', ansible_facts['ansible_net_gather_subset'])
self.assertIn('interfaces', ansible_facts['ansible_net_gather_subset'])
self.assertEquals('test1', ansible_facts['ansible_net_hostname'])
self.assertIn('MGT', ansible_facts['ansible_net_interfaces'].keys())
self.assertEquals(3992.75390625, ansible_facts['ansible_net_memtotal_mb'])
self.assertEquals(3383.109375, ansible_facts['ansible_net_memfree_mb'])
def test_enos_facts_gather_subset_config(self):
set_module_args({'gather_subset': 'config'})
result = self.execute_module()
ansible_facts = result['ansible_facts']
self.assertIn('default', ansible_facts['ansible_net_gather_subset'])
self.assertIn('config', ansible_facts['ansible_net_gather_subset'])
self.assertEquals('test1', ansible_facts['ansible_net_hostname'])
self.assertIn('ansible_net_config', ansible_facts)