edgeswitch_facts (#47618)

This commit is contained in:
f-bor 2018-11-02 14:20:11 +01:00 committed by Ganesh Nalawade
parent e8d6645c54
commit 35b97a2fa7
13 changed files with 890 additions and 0 deletions

View file

@ -0,0 +1,167 @@
# 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.
#
# (c) 2018 Red Hat Inc.
#
# 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.
#
import json
import re
from copy import deepcopy
from ansible.module_utils._text import to_text
from ansible.module_utils.network.common.utils import to_list, ComplexList
from ansible.module_utils.connection import Connection, ConnectionError
from ansible.module_utils.network.common.utils import remove_default_spec
_DEVICE_CONFIGS = {}
def build_aggregate_spec(element_spec, required, *extra_spec):
aggregate_spec = deepcopy(element_spec)
for elt in required:
aggregate_spec[elt] = dict(required=True)
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec)
)
argument_spec.update(element_spec)
argument_spec.update(*extra_spec)
return argument_spec
def map_params_to_obj(module):
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
d = item.copy()
obj.append(d)
else:
obj.append(module.params)
return obj
def get_connection(module):
if hasattr(module, '_edgeswitch_connection'):
return module._edgeswitch_connection
capabilities = get_capabilities(module)
network_api = capabilities.get('network_api')
if network_api == 'cliconf':
module._edgeswitch_connection = Connection(module._socket_path)
else:
module.fail_json(msg='Invalid connection type %s' % network_api)
return module._edgeswitch_connection
def get_capabilities(module):
if hasattr(module, '_edgeswitch_capabilities'):
return module._edgeswitch_capabilities
try:
capabilities = Connection(module._socket_path).get_capabilities()
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
module._edgeswitch_capabilities = json.loads(capabilities)
return module._edgeswitch_capabilities
def get_defaults_flag(module):
connection = get_connection(module)
try:
out = connection.get_defaults_flag()
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
return to_text(out, errors='surrogate_then_replace').strip()
def get_config(module, flags=None):
flag_str = ' '.join(to_list(flags))
try:
return _DEVICE_CONFIGS[flag_str]
except KeyError:
connection = get_connection(module)
try:
out = connection.get_config(flags=flags)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
cfg = to_text(out, errors='surrogate_then_replace').strip()
_DEVICE_CONFIGS[flag_str] = cfg
return cfg
def get_interfaces_config(module):
config = get_config(module)
lines = config.split('\n')
interfaces = {}
interface = None
for line in lines:
if line == 'exit':
if interface:
interfaces[interface[0]] = interface
interface = None
elif interface:
interface.append(line)
else:
match = re.match(r'^interface (.*)$', line)
if match:
interface = list()
interface.append(line)
return interfaces
def to_commands(module, commands):
spec = {
'command': dict(key=True),
'prompt': dict(),
'answer': dict()
}
transform = ComplexList(spec, module)
return transform(commands)
def run_commands(module, commands, check_rc=True):
connection = get_connection(module)
try:
return connection.run_commands(commands=commands, check_rc=check_rc)
except ConnectionError as exc:
module.fail_json(msg=to_text(exc))
def load_config(module, commands):
connection = get_connection(module)
try:
resp = connection.edit_config(commands)
return resp.get('response')
except ConnectionError as exc:
module.fail_json(msg=to_text(exc))

View file

@ -0,0 +1,271 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2018 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = """
---
module: edgeswitch_facts
version_added: "2.8"
author: "Frederic Bor (@f-bor)"
short_description: Collect facts from remote devices running Edgeswitch
description:
- Collects a base set of device facts from a remote device that
is running Ubiquiti Edgeswitch. 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 Edgeswitch 1.7.4
options:
gather_subset:
description:
- When supplied, this argument will restrict the facts collected
to a given subset. Possible values for this argument include
all, 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 = """
# Collect all facts from the device
- edgeswitch_facts:
gather_subset: all
# Collect only the config and default facts
- edgeswitch_facts:
gather_subset:
- config
"""
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: string
ansible_net_serialnum:
description: The serial number of the remote device
returned: always
type: string
ansible_net_version:
description: The operating system version running on the remote device
returned: always
type: string
ansible_net_hostname:
description: The configured hostname of the device
returned: always
type: string
# config
ansible_net_config:
description: The current active config from the device
returned: when config is configured
type: string
# interfaces
ansible_net_interfaces:
description: A hash of all interfaces running on the system
returned: when interfaces is configured
type: dict
"""
import re
from ansible.module_utils.network.edgeswitch.edgeswitch 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, commands=self.COMMANDS, check_rc=False)
def run(self, cmd):
return run_commands(self.module, commands=cmd, check_rc=False)
class Default(FactsBase):
COMMANDS = ['show version', 'show sysinfo']
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)
self.facts['model'] = self.parse_model(data)
self.facts['hostname'] = self.parse_hostname(self.responses[1])
def parse_version(self, data):
match = re.search(r'Software Version\.+ (.*)', data)
if match:
return match.group(1)
def parse_hostname(self, data):
match = re.search(r'System Name\.+ (.*)', data)
if match:
return match.group(1)
def parse_model(self, data):
match = re.search(r'Machine Model\.+ (.*)', data)
if match:
return match.group(1)
def parse_serialnum(self, data):
match = re.search(r'Serial Number\.+ (.*)', data)
if match:
return match.group(1)
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 interfaces description',
'show interfaces status all'
]
def populate(self):
super(Interfaces, self).populate()
interfaces = {}
data = self.responses[0]
self.parse_interfaces_description(data, interfaces)
data = self.responses[1]
self.parse_interfaces_status(data, interfaces)
self.facts['interfaces'] = interfaces
def parse_interfaces_description(self, data, interfaces):
for line in data.split('\n'):
match = re.match(r'(\d\/\d+)\s+(\w+)\s+(\w+)', line)
if match:
name = match.group(1)
interface = {}
interface['operstatus'] = match.group(2)
interface['lineprotocol'] = match.group(3)
interface['description'] = line[30:]
interfaces[name] = interface
def parse_interfaces_status(self, data, interfaces):
for line in data.split('\n'):
match = re.match(r'(\d\/\d+)', line)
if match:
name = match.group(1)
interface = interfaces[name]
interface['physicalstatus'] = line[61:71].strip()
interface['mediatype'] = line[73:91].strip()
FACT_SUBSETS = dict(
default=Default,
config=Config,
interfaces=Interfaces,
)
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
module.exit_json(ansible_facts=ansible_facts)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,134 @@
#
# (c) 2018 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/>.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import re
import time
import json
from itertools import chain
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_text
from ansible.module_utils.network.common.config import dumps
from ansible.module_utils.network.common.utils import to_list
from ansible.plugins.cliconf import CliconfBase, enable_mode
from ansible.module_utils.common._collections_compat import Mapping
class Cliconf(CliconfBase):
def get_device_info(self):
device_info = {}
device_info['network_os'] = 'edgeswitch'
reply = self.get(command='show version')
data = to_text(reply, errors='surrogate_or_strict').strip()
match = re.search(r'Software Version\.+ (.*)', data)
if match:
device_info['network_os_version'] = match.group(1).strip(',')
match = re.search(r'^Machine Model\.+ (.*)', data, re.M)
if match:
device_info['network_os_model'] = match.group(1)
match = re.search(r'System Name\.+ (.*)', data, re.M)
if match:
device_info['network_os_hostname'] = match.group(1)
return device_info
@enable_mode
def get_config(self, source='running', flags=None):
if source not in ('running', 'startup'):
raise ValueError("fetching configuration from %s is not supported" % source)
if source == 'running':
cmd = 'show running-config '
else:
cmd = 'show startup-config '
if flags:
cmd += ' '.join(to_list(flags))
cmd = cmd.strip()
return self.send_command(cmd)
@enable_mode
def edit_config(self, commands):
resp = {}
results = []
requests = []
self.send_command('configure')
for line in to_list(commands):
if not isinstance(line, Mapping):
line = {'command': line}
cmd = line['command']
if cmd != 'end' and cmd[0] != '!':
results.append(self.send_command(**line))
requests.append(cmd)
self.send_command('end')
resp['request'] = requests
resp['response'] = results
return resp
def get(self, command=None, prompt=None, answer=None, sendonly=False, output=None, check_all=False):
if not command:
raise ValueError('must provide value of command to execute')
if output:
raise ValueError("'output' value %s is not supported for get" % output)
return self.send_command(command=command, prompt=prompt, answer=answer, sendonly=sendonly, check_all=check_all)
def get_capabilities(self):
result = dict()
result['rpc'] = self.get_base_rpc() + ['run_commands']
result['network_api'] = 'cliconf'
result['device_info'] = self.get_device_info()
return json.dumps(result)
def run_commands(self, commands=None, check_rc=True):
if commands is None:
raise ValueError("'commands' value is required")
responses = list()
for cmd in to_list(commands):
if not isinstance(cmd, Mapping):
cmd = {'command': cmd}
output = cmd.pop('output', None)
if output:
raise ValueError("'output' value %s is not supported for run_commands" % output)
try:
out = self.send_command(**cmd)
except AnsibleConnectionFailure as e:
if check_rc:
raise
out = getattr(e, 'err', e)
responses.append(out)
return responses

View file

@ -0,0 +1,87 @@
#
# (c) 2018 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/>.
#
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
import re
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_text, to_bytes
from ansible.plugins.terminal import TerminalBase
class TerminalModule(TerminalBase):
terminal_stdout_re = [
re.compile(br"\(([^\(\)]+)\) [>#]$"),
re.compile(br"\(([^\(\)]+)\) \(([^\(\)]+)\)#$")
]
terminal_stderr_re = [
re.compile(br"% ?Error"),
re.compile(br"% ?Bad secret"),
re.compile(br"invalid input", re.I),
re.compile(br"(?:incomplete|ambiguous) command", re.I),
re.compile(br"connection timed out", re.I),
re.compile(br"[^\r\n]+ not found"),
re.compile(br"'[^']' +returned error code: ?\d+"),
re.compile(br"An invalid")
]
def on_open_shell(self):
return
def on_become(self, passwd=None):
prompt = self._get_prompt()
if prompt and prompt.endswith(b'#'):
return
cmd = {u'command': u'enable'}
if passwd:
cmd[u'prompt'] = to_text(r"[\r\n]?[Pp]assword: ?$", errors='surrogate_or_strict')
cmd[u'answer'] = passwd
try:
self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict'))
prompt = self._get_prompt()
if prompt is None or not prompt.endswith(b'#'):
raise AnsibleConnectionFailure('failed to elevate privilege to enable mode still at prompt [%s]' % prompt)
cmd = {u'command': u'terminal length 0'}
self._exec_cli_command(to_bytes(json.dumps(cmd), errors='surrogate_or_strict'))
prompt = self._get_prompt()
if prompt is None or not prompt.endswith(b'#'):
raise AnsibleConnectionFailure('failed to setup terminal in enable mode')
except AnsibleConnectionFailure as e:
prompt = self._get_prompt()
raise AnsibleConnectionFailure('unable to elevate privilege to enable mode, at prompt [%s] with error: %s' % (prompt, e.message))
def on_unbecome(self):
prompt = self._get_prompt()
if prompt is None:
# if prompt is None most likely the terminal is hung up at a prompt
return
if b'(Config' in prompt:
self._exec_cli_command(b'end')
self._exec_cli_command(b'exit')
elif prompt.endswith(b'#'):
self._exec_cli_command(b'exit')

View file

@ -0,0 +1,86 @@
# (c) 2018 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/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import os
import json
from units.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase
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 TestEdgeswitchModule(ModuleTestCase):
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):
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):
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,26 @@
Interface Admin Link Description
--------- --------- ------ ----------------------------------------------------------------
0/1 Enable Up VMOTION ESX1
0/2 Enable Up DATA ESX1
0/3 Enable Up VMOTION ESX2
0/4 Enable Up DATA ESX2
0/5 Enable Up VMOTION ESX3
0/6 Enable Up DATA ESX3
0/7 Enable Up VMOTION ESX4
0/8 Enable Up DATA ESX4
0/9 Enable Up SAVE
0/10 Enable Down
0/11 Enable Down
0/12 Enable Down
0/13 Enable Down
0/14 Enable Down
0/15 Enable Up UPLINK VIDEO WITH A VERY LONG DESCRIPTION THAT HELPS NO ONE
0/16 Enable Up UPLINK LOCAL
3/1 Enable Down
3/2 Enable Down
3/3 Enable Down
3/4 Enable Down
3/5 Enable Down
3/6 Enable Down
4/1 Enable Up

View file

@ -0,0 +1,31 @@
Link Physical Physical Media Flow Control
Port Name State Mode Status Type Status
--------- ---------------------------- ------ ---------- ---------- ------------------ ------------
0/1 VMOTION ESX1 Up Auto D 10G Full 2.5G-BaseFX Inactive
0/2 DATA ESX1 Up Auto D 10G Full 2.5G-BaseFX Inactive
0/3 VMOTION ESX2 Up Auto D 10G Full 2.5G-BaseFX Inactive
0/4 DATA ESX2 Up Auto D 10G Full 2.5G-BaseFX Inactive
0/5 VMOTION ESX3 Up Auto D 10G Full 2.5G-BaseFX Inactive
0/6 DATA ESX3 Up Auto D 10G Full 2.5G-BaseFX Inactive
0/7 VMOTION ESX4 Up Auto D 10G Full 2.5G-BaseFX Inactive
0/8 DATA ESX4 Up Auto D 10G Full 2.5G-BaseFX Inactive
0/9 SAVE Up Auto D 10G Full 2.5G-BaseFX Inactive
0/10 Down Auto D 2.5G-BaseFX Inactive
0/11 Down Auto D 2.5G-BaseFX Inactive
0/12 Down Auto D 2.5G-BaseFX Inactive
0/13 Down Auto D 2.5G-BaseFX Inactive
0/14 Down Auto Unknown Inactive
0/15 Down Auto Unknown Inactive
0/15 UPLINK VIDEO WITH A VERY LON Up Auto 1000 Full Unknown Inactive
0/16 UPLINK LOCAL Up Auto 1000 Full Unknown Inactive
3/1 Down
3/2 Down
3/3 Down
3/4 Down
3/5 Down
3/6 Down
4/1 Up 10 Half 10 Half
Flow Control:Disabled

View file

@ -0,0 +1,7 @@
System Description............................. EdgeSwitch 16-Port 10G, 1.7.4.5075842, Linux 3.6.5, 1.0.0.4872137
System Name.................................... sw_test_1
System Location................................
System Contact.................................
System Object ID............................... 1.3.6.1.4.1.4413
System Up Time................................. 174 days 19 hrs 0 mins 51 secs
Current SNTP Synchronized Time................. Oct 20 22:53:01 2018 UTC

View file

@ -0,0 +1,8 @@
Switch: 1
System Description............................. EdgeSwitch 16-Port 10G, 1.7.4.5075842, Linux 3.6.5, 1.0.0.4872137
Machine Type................................... EdgeSwitch 16-Port 10G
Machine Model.................................. ES-16-XG
Serial Number.................................. F09FC2EFD310
Burned In MAC Address.......................... F0:9F:C2:EF:D3:10
Software Version............................... 1.7.4.5075842

View file

@ -0,0 +1,73 @@
# (c) 2018 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/>.
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
from units.compat.mock import patch
from ansible.modules.network.edgeswitch import edgeswitch_facts
from units.modules.utils import set_module_args
from .edgeswitch_module import TestEdgeswitchModule, load_fixture
class TestEdgeswitchFactsModule(TestEdgeswitchModule):
module = edgeswitch_facts
def setUp(self):
super(TestEdgeswitchFactsModule, self).setUp()
self.mock_run_commands = patch('ansible.modules.network.edgeswitch.edgeswitch_facts.run_commands')
self.run_commands = self.mock_run_commands.start()
def tearDown(self):
super(TestEdgeswitchFactsModule, self).tearDown()
self.mock_run_commands.stop()
def load_fixtures(self, commands=None):
def load_from_file(*args, **kwargs):
module = args
commands = kwargs['commands']
output = list()
for command in commands:
filename = str(command).split(' | ')[0].replace(' ', '_')
output.append(load_fixture('edgeswitch_facts_%s' % filename))
return output
self.run_commands.side_effect = load_from_file
def test_edgeswitch_facts_default(self):
set_module_args(dict(gather_subset=['all', '!interfaces', '!config']))
result = self.execute_module()
facts = result.get('ansible_facts')
self.assertEqual(len(facts), 5)
self.assertEqual(facts['ansible_net_hostname'], 'sw_test_1')
self.assertEqual(facts['ansible_net_serialnum'], 'F09FC2EFD310')
self.assertEqual(facts['ansible_net_version'], '1.7.4.5075842')
def test_edgeswitch_facts_interfaces(self):
set_module_args(dict(gather_subset='interfaces'))
result = self.execute_module()
facts = result.get('ansible_facts')
self.assertEqual(len(facts), 6)
self.assertEqual(facts['ansible_net_interfaces']['0/1']['operstatus'], 'Enable')
self.assertEqual(facts['ansible_net_interfaces']['0/2']['mediatype'], '2.5G-BaseFX')
self.assertEqual(facts['ansible_net_interfaces']['0/3']['physicalstatus'], '10G Full')
self.assertEqual(facts['ansible_net_interfaces']['0/4']['lineprotocol'], 'Up')
self.assertEqual(facts['ansible_net_interfaces']['0/15']['description'], 'UPLINK VIDEO WITH A VERY LONG DESCRIPTION THAT HELPS NO ONE')