mirror of https://github.com/ansible/ansible.git
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 methodpull/32697/head
parent
e5dbf63b65
commit
9d98452032
@ -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'
|
@ -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()
|
@ -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
|
@ -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:
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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)
|
Loading…
Reference in New Issue