mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-18 18:01:31 +00:00
Initial commit
This commit is contained in:
commit
aebc1b03fd
4861 changed files with 812621 additions and 0 deletions
176
plugins/modules/network/edgeos/edgeos_command.py
Normal file
176
plugins/modules/network/edgeos/edgeos_command.py
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# 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: edgeos_command
|
||||
author:
|
||||
- Chad Norgan (@beardymcbeards)
|
||||
- Sam Doran (@samdoran)
|
||||
short_description: Run one or more commands on EdgeOS devices
|
||||
description:
|
||||
- This command module allows running one or more commands on a remote
|
||||
device running EdgeOS, such as the Ubiquiti EdgeRouter.
|
||||
- This module does not support running commands in configuration mode.
|
||||
- Certain C(show) commands in EdgeOS produce many lines of output and
|
||||
use a custom pager that can cause this module to hang. If the
|
||||
value of the environment variable C(ANSIBLE_EDGEOS_TERMINAL_LENGTH)
|
||||
is not set, the default number of 10000 is used.
|
||||
- "This is a network module and requires C(connection: network_cli)
|
||||
in order to work properly."
|
||||
- For more information please see the L(Network Guide,../network/getting_started/index.html).
|
||||
options:
|
||||
commands:
|
||||
description:
|
||||
- The commands or ordered set of commands that should be run against the
|
||||
remote device. The output of the command is returned to the playbook.
|
||||
If the C(wait_for) argument is provided, the module is not returned
|
||||
until the condition is met or the number of retries is exceeded.
|
||||
required: True
|
||||
wait_for:
|
||||
description:
|
||||
- Causes the task to wait for a specific condition to be met before
|
||||
moving forward. If the condition is not met before the specified
|
||||
number of retries is exceeded, the task will fail.
|
||||
required: False
|
||||
match:
|
||||
description:
|
||||
- Used in conjunction with C(wait_for) to create match policy. If set to
|
||||
C(all), then all conditions in C(wait_for) must be met. If set to
|
||||
C(any), then only one condition must match.
|
||||
required: False
|
||||
default: 'all'
|
||||
choices: ['any', 'all']
|
||||
retries:
|
||||
description:
|
||||
- Number of times a command should be tried before it is considered failed.
|
||||
The command is run on the target device and evaluated against the
|
||||
C(wait_for) conditionals.
|
||||
required: False
|
||||
default: 10
|
||||
interval:
|
||||
description:
|
||||
- The number of seconds to wait between C(retries) of the command.
|
||||
required: False
|
||||
default: 1
|
||||
|
||||
notes:
|
||||
- Tested against EdgeOS 1.9.7
|
||||
- Running C(show system boot-messages all) will cause the module to hang since
|
||||
EdgeOS is using a custom pager setting to display the output of that command.
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
tasks:
|
||||
- name: Reboot the device
|
||||
edgeos_command:
|
||||
commands: reboot now
|
||||
|
||||
- name: Show the configuration for eth0 and eth1
|
||||
edgeos_command:
|
||||
commands: show interfaces ethernet {{ item }}
|
||||
loop:
|
||||
- eth0
|
||||
- eth1
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
stdout:
|
||||
description: The set of responses from the commands
|
||||
returned: always apart from low level errors (such as action plugin)
|
||||
type: list
|
||||
sample: ['...', '...']
|
||||
stdout_lines:
|
||||
description: The value of stdout split into a list
|
||||
returned: always
|
||||
type: list
|
||||
sample: [['...', '...'], ['...'], ['...']]
|
||||
"""
|
||||
import time
|
||||
|
||||
from ansible.module_utils._text import to_text
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import transform_commands, to_lines
|
||||
from ansible_collections.community.general.plugins.module_utils.network.edgeos.edgeos import run_commands
|
||||
|
||||
|
||||
def parse_commands(module, warnings):
|
||||
commands = transform_commands(module)
|
||||
|
||||
if module.check_mode:
|
||||
for item in list(commands):
|
||||
if not item['command'].startswith('show'):
|
||||
warnings.append(
|
||||
'Only show commands are supported when using check mode, not '
|
||||
'executing %s' % item['command']
|
||||
)
|
||||
commands.remove(item)
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def main():
|
||||
spec = dict(
|
||||
commands=dict(type='list', required=True),
|
||||
wait_for=dict(type='list'),
|
||||
match=dict(default='all', choices=['all', 'any']),
|
||||
retries=dict(default=10, type='int'),
|
||||
interval=dict(default=1, type='int')
|
||||
)
|
||||
|
||||
module = AnsibleModule(argument_spec=spec, supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
result = {'changed': False, 'warnings': warnings}
|
||||
commands = parse_commands(module, warnings)
|
||||
wait_for = module.params['wait_for'] or list()
|
||||
|
||||
try:
|
||||
conditionals = [Conditional(c) for c in wait_for]
|
||||
except AttributeError as exc:
|
||||
module.fail_json(msg=to_text(exc))
|
||||
|
||||
retries = module.params['retries']
|
||||
interval = module.params['interval']
|
||||
match = module.params['match']
|
||||
|
||||
while retries > 0:
|
||||
responses = run_commands(module, commands)
|
||||
|
||||
for item in list(conditionals):
|
||||
if item(responses):
|
||||
if match == 'any':
|
||||
conditionals = list()
|
||||
break
|
||||
conditionals.remove(item)
|
||||
|
||||
if not conditionals:
|
||||
break
|
||||
|
||||
time.sleep(interval)
|
||||
retries -= 1
|
||||
|
||||
if conditionals:
|
||||
failed_conditions = [item.raw for item in conditionals]
|
||||
msg = 'One or more conditional statements have not been satisfied'
|
||||
module.fail_json(msg=msg, failed_conditions=failed_conditions)
|
||||
|
||||
result.update({
|
||||
'stdout': responses,
|
||||
'stdout_lines': list(to_lines(responses)),
|
||||
})
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
317
plugins/modules/network/edgeos/edgeos_config.py
Normal file
317
plugins/modules/network/edgeos/edgeos_config.py
Normal file
|
|
@ -0,0 +1,317 @@
|
|||
#!/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: edgeos_config
|
||||
author:
|
||||
- "Nathaniel Case (@Qalthos)"
|
||||
- "Sam Doran (@samdoran)"
|
||||
short_description: Manage EdgeOS configuration on remote device
|
||||
description:
|
||||
- This module provides configuration file management of EdgeOS
|
||||
devices. It provides arguments for managing both the
|
||||
configuration file and state of the active configuration. All
|
||||
configuration statements are based on `set` and `delete` commands
|
||||
in the device configuration.
|
||||
- "This is a network module and requires the C(connection: network_cli) in order
|
||||
to work properly."
|
||||
- For more information please see the L(Network Guide,../network/getting_started/index.html).
|
||||
notes:
|
||||
- Tested against EdgeOS 1.9.7
|
||||
- Setting C(ANSIBLE_PERSISTENT_COMMAND_TIMEOUT) to 30 is recommended since
|
||||
the save command can take longer than the default of 10 seconds on
|
||||
some EdgeOS hardware.
|
||||
options:
|
||||
lines:
|
||||
description:
|
||||
- The ordered set of configuration lines to be managed and
|
||||
compared with the existing configuration on the remote
|
||||
device.
|
||||
src:
|
||||
description:
|
||||
- The C(src) argument specifies the path to the source config
|
||||
file to load. The source config file can either be in
|
||||
bracket format or set format. The source file can include
|
||||
Jinja2 template variables.
|
||||
match:
|
||||
description:
|
||||
- The C(match) argument controls the method used to match
|
||||
against the current active configuration. By default, the
|
||||
desired config is matched against the active config and the
|
||||
deltas are loaded. If the C(match) argument is set to C(none)
|
||||
the active configuration is ignored and the configuration is
|
||||
always loaded.
|
||||
default: line
|
||||
choices: ['line', 'none']
|
||||
backup:
|
||||
description:
|
||||
- The C(backup) argument will backup the current device's active
|
||||
configuration to the Ansible control host prior to making any
|
||||
changes. If the C(backup_options) value is not given, the backup
|
||||
file will be located in the backup folder in the playbook root
|
||||
directory or role root directory if the playbook is part of an
|
||||
ansible role. If the directory does not exist, it is created.
|
||||
type: bool
|
||||
default: 'no'
|
||||
comment:
|
||||
description:
|
||||
- Allows a commit description to be specified to be included
|
||||
when the configuration is committed. If the configuration is
|
||||
not changed or committed, this argument is ignored.
|
||||
default: 'configured by edgeos_config'
|
||||
config:
|
||||
description:
|
||||
- The C(config) argument specifies the base configuration to use
|
||||
to compare against the desired configuration. If this value
|
||||
is not specified, the module will automatically retrieve the
|
||||
current active configuration from the remote device.
|
||||
save:
|
||||
description:
|
||||
- The C(save) argument controls whether or not changes made
|
||||
to the active configuration are saved to disk. This is
|
||||
independent of committing the config. When set to C(True), the
|
||||
active configuration is saved.
|
||||
type: bool
|
||||
default: 'no'
|
||||
backup_options:
|
||||
description:
|
||||
- This is a dict object containing configurable options related to backup file path.
|
||||
The value of this option is read only when C(backup) is set to I(yes), if C(backup) is set
|
||||
to I(no) this option will be silently ignored.
|
||||
suboptions:
|
||||
filename:
|
||||
description:
|
||||
- The filename to be used to store the backup configuration. If the filename
|
||||
is not given it will be generated based on the hostname, current time and date
|
||||
in format defined by <hostname>_config.<current-date>@<current-time>
|
||||
dir_path:
|
||||
description:
|
||||
- This option provides the path ending with directory name in which the backup
|
||||
configuration file will be stored. If the directory does not exist it will be first
|
||||
created and the filename is either the value of C(filename) or default filename
|
||||
as described in C(filename) options description. If the path value is not given
|
||||
in that case a I(backup) directory will be created in the current working directory
|
||||
and backup configuration will be copied in C(filename) within I(backup) directory.
|
||||
type: path
|
||||
type: dict
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: configure the remote device
|
||||
edgeos_config:
|
||||
lines:
|
||||
- set system host-name {{ inventory_hostname }}
|
||||
- set service lldp
|
||||
- delete service dhcp-server
|
||||
|
||||
- name: backup and load from file
|
||||
edgeos_config:
|
||||
src: edgeos.cfg
|
||||
backup: yes
|
||||
|
||||
- name: configurable backup path
|
||||
edgeos_config:
|
||||
src: edgeos.cfg
|
||||
backup: yes
|
||||
backup_options:
|
||||
filename: backup.cfg
|
||||
dir_path: /home/user
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
commands:
|
||||
description: The list of configuration commands sent to the device
|
||||
returned: always
|
||||
type: list
|
||||
sample: ['...', '...']
|
||||
backup_path:
|
||||
description: The full path to the backup file
|
||||
returned: when backup is yes
|
||||
type: str
|
||||
sample: /playbooks/ansible/backup/edgeos_config.2016-07-16@22:28:34
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import NetworkConfig
|
||||
from ansible_collections.community.general.plugins.module_utils.network.edgeos.edgeos import load_config, get_config, run_commands
|
||||
|
||||
|
||||
DEFAULT_COMMENT = 'configured by edgeos_config'
|
||||
|
||||
|
||||
def config_to_commands(config):
|
||||
set_format = config.startswith('set') or config.startswith('delete')
|
||||
candidate = NetworkConfig(indent=4, contents=config)
|
||||
if not set_format:
|
||||
candidate = [c.line for c in candidate.items]
|
||||
commands = list()
|
||||
# this filters out less specific lines
|
||||
for item in candidate:
|
||||
for index, entry in enumerate(commands):
|
||||
if item.startswith(entry):
|
||||
del commands[index]
|
||||
break
|
||||
commands.append(item)
|
||||
|
||||
commands = ['set %s' % cmd.replace(' {', '') for cmd in commands]
|
||||
|
||||
else:
|
||||
commands = to_native(candidate).split('\n')
|
||||
|
||||
return commands
|
||||
|
||||
|
||||
def get_candidate(module):
|
||||
contents = module.params['src'] or module.params['lines']
|
||||
|
||||
if module.params['lines']:
|
||||
contents = '\n'.join(contents)
|
||||
|
||||
return config_to_commands(contents)
|
||||
|
||||
|
||||
def check_command(module, command):
|
||||
"""Tests against a command line to be valid otherwise raise errors
|
||||
|
||||
Error on uneven single quote which breaks ansible waiting for further input. Ansible
|
||||
will handle even single quote failures correctly.
|
||||
|
||||
:param command: the command line from current or new config
|
||||
:type command: string
|
||||
:raises ValueError:
|
||||
* if contains odd number of single quotes
|
||||
:return: command string unchanged
|
||||
:rtype: string
|
||||
"""
|
||||
if command.count("'") % 2 != 0:
|
||||
module.fail_json(msg="Unmatched single (') quote found in command: " + command)
|
||||
|
||||
return command
|
||||
|
||||
|
||||
def diff_config(module, commands, config):
|
||||
config = [to_native(check_command(module, c)) for c in config.splitlines()]
|
||||
|
||||
updates = list()
|
||||
visited = set()
|
||||
delete_commands = [line for line in commands if line.startswith('delete')]
|
||||
|
||||
for line in commands:
|
||||
item = to_native(check_command(module, line))
|
||||
|
||||
if not item.startswith('set') and not item.startswith('delete'):
|
||||
raise ValueError('line must start with either `set` or `delete`')
|
||||
|
||||
elif item.startswith('set'):
|
||||
|
||||
if item not in config:
|
||||
updates.append(line)
|
||||
|
||||
# If there is a corresponding delete command in the desired config, make sure to append
|
||||
# the set command even though it already exists in the running config
|
||||
else:
|
||||
ditem = re.sub('set', 'delete', item)
|
||||
for line in delete_commands:
|
||||
if ditem.startswith(line):
|
||||
updates.append(item)
|
||||
|
||||
elif item.startswith('delete'):
|
||||
if not config:
|
||||
updates.append(line)
|
||||
else:
|
||||
item = re.sub(r'delete', 'set', item)
|
||||
for entry in config:
|
||||
if entry.startswith(item) and line not in visited:
|
||||
updates.append(line)
|
||||
visited.add(line)
|
||||
|
||||
return list(updates)
|
||||
|
||||
|
||||
def run(module, result):
|
||||
# get the current active config from the node or passed in via
|
||||
# the config param
|
||||
config = module.params['config'] or get_config(module)
|
||||
|
||||
# create the candidate config object from the arguments
|
||||
candidate = get_candidate(module)
|
||||
|
||||
# create loadable config that includes only the configuration updates
|
||||
commands = diff_config(module, candidate, config)
|
||||
|
||||
result['commands'] = commands
|
||||
|
||||
commit = not module.check_mode
|
||||
comment = module.params['comment']
|
||||
|
||||
if commands:
|
||||
load_config(module, commands, commit=commit, comment=comment)
|
||||
|
||||
result['changed'] = True
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
backup_spec = dict(
|
||||
filename=dict(),
|
||||
dir_path=dict(type='path')
|
||||
)
|
||||
spec = dict(
|
||||
src=dict(type='path'),
|
||||
lines=dict(type='list'),
|
||||
|
||||
match=dict(default='line', choices=['line', 'none']),
|
||||
|
||||
comment=dict(default=DEFAULT_COMMENT),
|
||||
|
||||
config=dict(),
|
||||
|
||||
backup=dict(type='bool', default=False),
|
||||
backup_options=dict(type='dict', options=backup_spec),
|
||||
save=dict(type='bool', default=False),
|
||||
)
|
||||
|
||||
mutually_exclusive = [('lines', 'src')]
|
||||
|
||||
module = AnsibleModule(
|
||||
argument_spec=spec,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
warnings = list()
|
||||
|
||||
result = dict(changed=False, warnings=warnings)
|
||||
|
||||
if module.params['backup']:
|
||||
result['__backup__'] = get_config(module=module)
|
||||
|
||||
if any((module.params['src'], module.params['lines'])):
|
||||
run(module, result)
|
||||
|
||||
if module.params['save']:
|
||||
diff = run_commands(module, commands=['configure', 'compare saved'])[1]
|
||||
if diff != '[edit]':
|
||||
run_commands(module, commands=['save'])
|
||||
result['changed'] = True
|
||||
run_commands(module, commands=['exit'])
|
||||
|
||||
module.exit_json(**result)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
310
plugins/modules/network/edgeos/edgeos_facts.py
Normal file
310
plugins/modules/network/edgeos/edgeos_facts.py
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
#!/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: edgeos_facts
|
||||
author:
|
||||
- Nathaniel Case (@Qalthos)
|
||||
- Sam Doran (@samdoran)
|
||||
short_description: Collect facts from remote devices running EdgeOS
|
||||
description:
|
||||
- Collects a base set of device facts from a remote device that
|
||||
is running EdgeOS. This module prepends all of the
|
||||
base network fact keys with U(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 EdgeOS 1.9.7
|
||||
options:
|
||||
gather_subset:
|
||||
description:
|
||||
- When supplied, this argument will restrict the facts collected
|
||||
to a given subset. Possible values for this argument include
|
||||
all, default, config, and neighbors. Can specify a list of
|
||||
values to include a larger subset. Values can also be used
|
||||
with an initial C(M(!)) to specify that a specific subset should
|
||||
not be collected.
|
||||
required: false
|
||||
default: "!config"
|
||||
'''
|
||||
|
||||
EXAMPLES = """
|
||||
- name: collect all facts from the device
|
||||
edgeos_facts:
|
||||
gather_subset: all
|
||||
|
||||
- name: collect only the config and default facts
|
||||
edgeos_facts:
|
||||
gather_subset: config
|
||||
|
||||
- name: collect everything exception the config
|
||||
edgeos_facts:
|
||||
gather_subset: "!config"
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
ansible_net_config:
|
||||
description: The running-config from the device
|
||||
returned: when config is configured
|
||||
type: str
|
||||
ansible_net_commits:
|
||||
description: The set of available configuration revisions
|
||||
returned: when present
|
||||
type: list
|
||||
ansible_net_hostname:
|
||||
description: The configured system hostname
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_model:
|
||||
description: The device model string
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_serialnum:
|
||||
description: The serial number of the device
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_version:
|
||||
description: The version of the software running
|
||||
returned: always
|
||||
type: str
|
||||
ansible_net_neighbors:
|
||||
description: The set of LLDP neighbors
|
||||
returned: when interface is configured
|
||||
type: list
|
||||
ansible_net_gather_subset:
|
||||
description: The list of subsets gathered by the module
|
||||
returned: always
|
||||
type: list
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.six import iteritems
|
||||
from ansible_collections.community.general.plugins.module_utils.network.edgeos.edgeos import run_commands
|
||||
|
||||
|
||||
class FactsBase(object):
|
||||
|
||||
COMMANDS = frozenset()
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.facts = dict()
|
||||
self.responses = None
|
||||
|
||||
def populate(self):
|
||||
self.responses = run_commands(self.module, list(self.COMMANDS))
|
||||
|
||||
|
||||
class Default(FactsBase):
|
||||
|
||||
COMMANDS = [
|
||||
'show version',
|
||||
'show host name',
|
||||
]
|
||||
|
||||
def populate(self):
|
||||
super(Default, self).populate()
|
||||
data = self.responses[0]
|
||||
|
||||
self.facts['version'] = self.parse_version(data)
|
||||
self.facts['serialnum'] = self.parse_serialnum(data)
|
||||
self.facts['model'] = self.parse_model(data)
|
||||
|
||||
self.facts['hostname'] = self.responses[1]
|
||||
|
||||
def parse_version(self, data):
|
||||
match = re.search(r'Version:\s*v(\S+)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_model(self, data):
|
||||
match = re.search(r'HW model:\s*([A-Za-z0-9- ]+)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_serialnum(self, data):
|
||||
match = re.search(r'HW S/N:\s+(\S+)', data)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
|
||||
class Config(FactsBase):
|
||||
|
||||
COMMANDS = [
|
||||
'show configuration commands',
|
||||
'show system commit',
|
||||
]
|
||||
|
||||
def populate(self):
|
||||
super(Config, self).populate()
|
||||
|
||||
self.facts['config'] = self.responses
|
||||
|
||||
commits = self.responses[1]
|
||||
entries = list()
|
||||
entry = None
|
||||
|
||||
for line in commits.split('\n'):
|
||||
match = re.match(r'(\d+)\s+(.+)by(.+)via(.+)', line)
|
||||
if match:
|
||||
if entry:
|
||||
entries.append(entry)
|
||||
|
||||
entry = dict(revision=match.group(1),
|
||||
datetime=match.group(2),
|
||||
by=str(match.group(3)).strip(),
|
||||
via=str(match.group(4)).strip(),
|
||||
comment=None)
|
||||
elif entry:
|
||||
entry['comment'] = line.strip()
|
||||
|
||||
self.facts['commits'] = entries
|
||||
|
||||
|
||||
class Neighbors(FactsBase):
|
||||
|
||||
COMMANDS = [
|
||||
'show lldp neighbors',
|
||||
'show lldp neighbors detail',
|
||||
]
|
||||
|
||||
def populate(self):
|
||||
super(Neighbors, self).populate()
|
||||
|
||||
all_neighbors = self.responses[0]
|
||||
if 'LLDP not configured' not in all_neighbors:
|
||||
neighbors = self.parse(
|
||||
self.responses[1]
|
||||
)
|
||||
self.facts['neighbors'] = self.parse_neighbors(neighbors)
|
||||
|
||||
def parse(self, data):
|
||||
parsed = list()
|
||||
values = None
|
||||
for line in data.split('\n'):
|
||||
if not line:
|
||||
continue
|
||||
elif line[0] == ' ':
|
||||
values += '\n%s' % line
|
||||
elif line.startswith('Interface'):
|
||||
if values:
|
||||
parsed.append(values)
|
||||
values = line
|
||||
if values:
|
||||
parsed.append(values)
|
||||
return parsed
|
||||
|
||||
def parse_neighbors(self, data):
|
||||
facts = dict()
|
||||
for item in data:
|
||||
interface = self.parse_interface(item)
|
||||
host = self.parse_host(item)
|
||||
port = self.parse_port(item)
|
||||
if interface not in facts:
|
||||
facts[interface] = list()
|
||||
facts[interface].append(dict(host=host, port=port))
|
||||
return facts
|
||||
|
||||
def parse_interface(self, data):
|
||||
match = re.search(r'^Interface:\s+(\S+),', data)
|
||||
return match.group(1)
|
||||
|
||||
def parse_host(self, data):
|
||||
match = re.search(r'SysName:\s+(.+)$', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
def parse_port(self, data):
|
||||
match = re.search(r'PortDescr:\s+(.+)$', data, re.M)
|
||||
if match:
|
||||
return match.group(1)
|
||||
|
||||
|
||||
FACT_SUBSETS = dict(
|
||||
default=Default,
|
||||
neighbors=Neighbors,
|
||||
config=Config
|
||||
)
|
||||
|
||||
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
|
||||
|
||||
|
||||
def main():
|
||||
spec = dict(
|
||||
gather_subset=dict(default=['!config'], type='list')
|
||||
)
|
||||
|
||||
module = AnsibleModule(argument_spec=spec,
|
||||
supports_check_mode=True)
|
||||
|
||||
warnings = list()
|
||||
|
||||
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='Subset must be one of [%s], got %s' %
|
||||
(', '.join(VALID_SUBSETS), 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, warnings=warnings)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue