Init: mediaserver

This commit is contained in:
2023-02-08 12:13:28 +01:00
parent 848bc9739c
commit f7c23d4ba9
31914 changed files with 6175775 additions and 0 deletions

View File

@@ -0,0 +1,185 @@
#!/usr/bin/python
# 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
DOCUMENTATION = '''
---
module: command
author: "Petr Klima (@qaxi)"
short_description: Run commands on remote Cisco SMB devices
description:
- Sends arbitrary commands to an Cisco SMB node and returns the results
read from the device. This module includes an
argument that will cause the module to wait for a specific condition
before returning or timing out if the condition is not met.
options:
commands:
description:
- List of commands to send to the remote Cisco SMB device over the
configured provider. The resulting output from the command
is returned. If the I(wait_for) argument is provided, the
module is not returned until the condition is satisfied or
the number of retries has expired.
required: true
type: list
elements: str
wait_for:
description:
- List of conditions to evaluate against the output of the
command. The task will wait for each condition to be true
before moving forward. If the conditional is not true
within the configured number of retries, the task fails.
See examples.
type: list
elements: str
match:
description:
- The I(match) argument is used in conjunction with the
I(wait_for) argument to specify the match policy. Valid
values are C(all) or C(any). If the value is set to C(all)
then all conditionals in the wait_for must be satisfied. If
the value is set to C(any) then only one of the values must be
satisfied.
default: all
type: str
choices: ['any', 'all']
retries:
description:
- Specifies the number of retries a command should by tried
before it is considered failed. The command is run on the
target device every retry and evaluated against the
I(wait_for) conditions.
default: 10
type: int
interval:
description:
- Configures the interval in seconds to wait between retries
of the command. If the command does not pass the specified
conditions, the interval indicates how long to wait before
trying the command again.
default: 1
type: int
'''
EXAMPLES = """
- name: Run command on remote devices
community.ciscosmb.command:
commands: show version
- name: Run command and check to see if output contains PID
community.ciscosmb.command:
commands: show inventory
wait_for: result[0] contains PID
- name: Run multiple commands on remote nodes
community.ciscosmb.command:
commands:
- show version
- show system
- name: Run multiple commands and evaluate the output
community.ciscosmb.command:
commands:
- show version
- show system
wait_for:
- result[0] contains Active-image
- result[1] contains "System Up Time"
"""
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 apart from low level errors (such as action plugin)
type: list
sample: [['...', '...'], ['...'], ['...']]
failed_conditions:
description: The list of conditionals that have failed.
returned: failed
type: list
sample: ['...', '...']
"""
import time
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.parsing import Conditional
from ansible_collections.community.ciscosmb.plugins.module_utils.ciscosmb import run_commands
from ansible_collections.community.ciscosmb.plugins.module_utils.ciscosmb import ciscosmb_argument_spec
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.six import string_types
def to_lines(stdout):
for item in stdout:
if isinstance(item, string_types):
item = str(item).split('\n')
yield item
def main():
"""main entry point for module execution
"""
argument_spec = dict(
commands=dict(type='list', elements='str', required=True),
wait_for=dict(type='list', elements='str'),
match=dict(default='all', choices=['all', 'any']),
retries=dict(default=10, type='int'),
interval=dict(default=1, type='int')
)
argument_spec.update(ciscosmb_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=False)
result = {'changed': False}
wait_for = module.params['wait_for'] or list()
conditionals = [Conditional(c) for c in wait_for]
retries = module.params['retries']
interval = module.params['interval']
match = module.params['match']
while retries > 0:
responses = run_commands(module, module.params['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({
'changed': False,
'stdout': responses,
'stdout_lines': list(to_lines(responses))
})
module.exit_json(**result)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,763 @@
#!/usr/bin/python
# 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
DOCUMENTATION = """
---
module: facts
author: "Petr Klima (@qaxi)"
short_description: Collect facts from remote devices running Cisco SMB
description:
- Collects a base set of device facts from a remote device that
is running Cisco SMB. 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.
options:
gather_subset:
description:
- When supplied, this argument will restrict the facts collected
to a given subset. Possible values for this argument include
C(all), C(hardware), C(config) and C(interfaces). Can specify a list of
values to include a larger subset. Values can also be used
with an initial C(!) to specify that a specific subset should
not be collected.
required: false
type: list
elements: str
choices: [ 'default', 'all', 'hardware', 'config', 'interfaces', '!hardware', '!config', '!interfaces' ]
default: '!config'
notes:
- Supports C(check_mode).
"""
EXAMPLES = """
- name: Collect all facts from the device
community.ciscosmb.facts:
gather_subset: all
- name: Collect only the config and default facts
community.ciscosmb.facts:
gather_subset:
- config
- name: Do not collect hardware facts
community.ciscosmb.facts:
gather_subset:
- "!hardware"
"""
RETURN = """
ansible_net_gather_subset:
description: The list of fact subsets collected from the device.
returned: always
type: list
# default
ansible_net_model:
description: The model name returned from the device.
returned: always
type: str
ansible_net_serialnum:
description: The serial number of the remote device.
returned: always
type: str
ansible_net_version:
description: The operating system version running on the remote device.
returned: always
type: str
ansible_net_hostname:
description: The configured hostname of the device.
returned: always
type: str
ansible_net_uptime:
description: The uptime of the device.
returned: always
type: str
ansible_net_cpu_load:
description: Current CPU load.
returned: always
type: str
ansible_net_stacked_models:
description: The model names of each device in the stack.
returned: when multiple devices are configured in a stack
type: list
ansible_net_stacked_serialnums:
description: The serial numbers of each device in the stack.
returned: when multiple devices are configured in a stack
type: list
# hardware
ansible_net_spacefree_mb:
description: The available disk space on the remote device in MiB.
returned: when hardware is configured
type: dict
ansible_net_spacetotal_mb:
description: The total disk space on the remote device in MiB.
returned: when hardware is configured
type: dict
ansible_net_memfree_mb:
description: The available free memory on the remote device in MiB.
returned: when hardware is configured
type: int
ansible_net_memtotal_mb:
description: The total memory on the remote device in MiB.
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.
returned: when interfaces is configured
type: dict
ansible_net_neighbors:
description: The list of neighbors from the remote device.
returned: when interfaces is configured
type: dict
"""
import re
from ansible_collections.community.ciscosmb.plugins.module_utils.ciscosmb import (
run_commands,
ciscosmb_argument_spec,
interface_canonical_name,
ciscosmb_split_to_tables,
ciscosmb_parse_table,
ciscosmb_merge_dicts,
)
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 system",
"show cpu utilization",
"show inventory",
]
def populate(self):
super(Default, self).populate()
data = self.responses[0]
if data:
self.facts["version"] = self.parse_version(data)
self.facts["boot_version"] = self.parse_boot_version(data)
data = self.responses[1]
if data:
self.facts["uptime"] = self.parse_uptime(data)
self.facts["hostname"] = self.parse_hostname(data)
data = self.responses[2]
if data:
self.facts["cpu_load"] = self.parse_cpu_load(data)
data = self.responses[3]
if data:
modules = self.parse_inventory(data)
stacked_models = self.parse_stacked_models(modules)
if len(stacked_models) >= 2:
stacked_serialnums = self.parse_stacked_serialnums(modules)
self.facts["stacked_models"] = stacked_models
self.facts["stacked_serialnums"] = stacked_serialnums
self.facts["model"] = self.parse_model(modules)
self.facts["serialnum"] = self.parse_serialnum(modules)
self.facts["hw_version"] = self.parse_hw_version(modules)
self.facts["hw_modules"] = modules
# show version
def parse_version(self, data):
# Cisco SMB 300 and 500 - fw 1.x.x.x
match = re.search(r"^SW version\s*(\S+)\s*.*$", data, re.M)
if match:
return match.group(1)
# Cisco SMB 350 and 550 - fw 2.x.x.x
match = re.search(r"^ Version:\s*(\S+)\s*.*$", data, re.M)
if match:
return match.group(1)
def parse_boot_version(self, data):
match = re.search(r"Boot version\s*(\S+)\s*.*$", data, re.M)
if match:
return match.group(1)
# show system
def parse_uptime(self, data):
match = re.search(r"^System Up Time \S+:\s+(\S+)\s*$", data, re.M)
if match:
(dayhour, mins, sec) = match.group(1).split(":")
(day, hour) = dayhour.split(",")
# output in seconds
return (int(day) * 86400) + (int(hour) * 3600) + (int(mins) * 60) + int(sec)
def parse_hostname(self, data):
match = re.search(r"^System Name:\s*(\S+)\s*$", data, re.M)
if match:
return match.group(1)
# show cpu utilization
def parse_cpu_load(self, data):
match = re.search(r"one minute:\s+(\d+)%;\s*", data, re.M)
if match:
return match.group(1)
# show inventory
def parse_inventory(self, data):
# make 1 module 1 line
data = re.sub(r"\nPID", " PID", data)
# delete empty lines
data = re.sub(r"^\n", "", data)
data = re.sub(r"\n\n", "", data)
data = re.sub(r"\n\s*\n", r"\n", data)
lines = data.splitlines()
modules = {}
for line in lines:
# remove extra chars
line = re.sub(r'"', r"", line)
line = re.sub(r"\s+", r" ", line)
# normalize lines
line = re.sub(r":\s", r'"', line)
line = re.sub(r'\s+DESCR"', r'"DESCR"', line)
line = re.sub(r'\s+PID"', r'"PID"', line)
line = re.sub(r'\s+VID"', r'"VID"', line)
line = re.sub(r'\s+SN"', r'"SN"', line)
line = re.sub(r"\s*$", r"", line)
match = re.search(
r'^NAME"(?P<name>[^"]+)"DESCR"(?P<descr>[^"]+)"PID"(?P<pid>[^"]+)"VID"(?P<vid>[^"]+)"SN"(?P<sn>\S+)\s*',
line,
)
modul = match.groupdict()
modules[modul["name"]] = modul
if modules:
return modules
def parse_stacked_models(self, data):
# every inventory has module with NAME: "1"
# stacks have modules 2 3 ... 8
models = []
for n in range(1, 9):
# index is string
if str(n) in data:
models.append(data[str(n)]["pid"])
return models
def parse_stacked_serialnums(self, data):
# every inventory has module with NAME: "1"
# stacks have modules 2 3 ... 8
sn = []
for n in range(1, 9):
# index is string
if str(n) in data:
sn.append(data[str(n)]["sn"])
return sn
def parse_model(self, data):
# every inventory has module with NAME: "1"
model = data["1"]["pid"]
if "stacked_models" in self.facts:
model = re.sub(r"-.*$", "", model)
model = "Stack " + model
return model
def parse_serialnum(self, data):
# every inventory has module with NAME: "1"
sn = data["1"]["sn"]
return sn
def parse_hw_version(self, data):
# every inventory has module with NAME: "1"
sn = data["1"]["vid"]
return sn
class Hardware(FactsBase):
COMMANDS = [
"dir",
]
def populate(self):
super(Hardware, self).populate()
data = self.responses[0]
if data:
self.parse_filesystem_info(data)
def parse_filesystem_info(self, data):
match = re.search(r"Total size of (\S+): (\d+) bytes", data, re.M)
if match: # fw 1.x
self.facts["spacetotal_mb"] = round(int(match.group(2)) / 1024 / 1024, 1)
match = re.search(r"Free size of (\S+): (\d+) bytes", data, re.M)
self.facts["spacefree_mb"] = round(int(match.group(2)) / 1024 / 1024, 1)
else:
match = re.search(r"(\d+)K of (\d+)K are free", data, re.M)
if match: # fw 2.x, 3.x
self.facts["spacetotal_mb"] = round(int(match.group(2)) / 1024, 1)
self.facts["spacefree_mb"] = round(int(match.group(1)) / 1024, 1)
class Config(FactsBase):
COMMANDS = ["show running-config detailed"]
def populate(self):
super(Config, self).populate()
data = self.responses[0]
if data:
self.facts["config"] = data
class Interfaces(FactsBase):
COMMANDS = [
"show ports jumbo-frame",
"show ip interface",
"show ipv6 interface brief",
"show interfaces status",
"show interfaces configuration",
"show interfaces description",
"show lldp neighbors",
]
DETAIL_RE = re.compile(
r"([\w\d\-]+)=\"?(\w{3}/\d{2}/\d{4}\s\d{2}:\d{2}:\d{2}|[\w\d\-\.:/]+)"
)
WRAPPED_LINE_RE = re.compile(r"^\s+(?!\d)")
def populate(self):
super(Interfaces, self).populate()
self.facts["interfaces"] = dict()
self.facts["all_ipv4_addresses"] = list()
self.facts["all_ipv6_addresses"] = list()
self.facts["neighbors"] = list()
data = self.responses[0]
if data:
self.populate_interfaces_mtu(data)
data = self.responses[1]
if data:
self.populate_addresses_ipv4(data)
data = self.responses[2]
if data:
self.populate_addresses_ipv6(data)
data = self.responses[3]
if data:
self.populate_interfaces_status(data)
data = self.responses[4]
if data:
self.populate_interfaces_configuration(data)
data = self.responses[5]
if data:
self.populate_interfaces_description(data)
data = self.responses[6]
if data:
self.populate_neighbors(data)
def _populate_interfaces_status_interface(self, interface_table):
interfaces = dict()
for key in interface_table:
i = interface_table[key]
interface = dict()
interface["state"] = i[6].lower()
interface["type"] = i[1]
interface["mtu"] = self._mtu
interface["duplex"] = i[2].lower()
interface["negotiation"] = i[4].lower()
interface["control"] = i[5].lower()
interface["presure"] = i[7].lower()
interface["mode"] = i[8].lower()
if i[6] == "Up":
interface["bandwith"] = int(i[3]) * 1000 # to get speed in kb
else:
interface["bandwith"] = None
for key in interface:
if interface[key] == "--":
interface[key] = None
interfaces[interface_canonical_name(i[0])] = interface
return interfaces
def _populate_interfaces_status_portchanel(self, interface_table):
interfaces = dict()
for key in interface_table:
interface = dict()
i = interface_table[key]
interface["state"] = i[6].lower()
interface["type"] = i[1]
interface["mtu"] = self._mtu
interface["duplex"] = i[2].lower()
interface["negotiation"] = i[4].lower()
interface["control"] = i[5].lower()
if i[6] == "Up":
interface["bandwith"] = int(i[3]) * 1000 # to get speed in kb
else:
interface["bandwith"] = None
for key in interface:
if interface[key] == "--":
interface[key] = None
interfaces[interface_canonical_name(i[0])] = interface
return interfaces
def populate_interfaces_status(self, data):
tables = ciscosmb_split_to_tables(data)
interface_table = ciscosmb_parse_table(tables[0])
portchanel_table = ciscosmb_parse_table(tables[1])
interfaces = self._populate_interfaces_status_interface(interface_table)
self.facts["interfaces"] = ciscosmb_merge_dicts(
self.facts["interfaces"], interfaces
)
interfaces = self._populate_interfaces_status_portchanel(portchanel_table)
self.facts["interfaces"] = ciscosmb_merge_dicts(
self.facts["interfaces"], interfaces
)
def _populate_interfaces_configuration_interface(self, interface_table):
interfaces = dict()
for key in interface_table:
i = interface_table[key]
interface = dict()
interface["admin_state"] = i[6].lower()
interface["mdix"] = i[8].lower()
interfaces[interface_canonical_name(i[0])] = interface
return interfaces
def _populate_interfaces_configuration_portchanel(self, interface_table):
interfaces = dict()
for key in interface_table:
interface = dict()
i = interface_table[key]
interface["admin_state"] = i[5].lower()
interfaces[interface_canonical_name(i[0])] = interface
return interfaces
def populate_interfaces_configuration(self, data):
tables = ciscosmb_split_to_tables(data)
interface_table = ciscosmb_parse_table(tables[0])
portchanel_table = ciscosmb_parse_table(tables[1])
interfaces = self._populate_interfaces_configuration_interface(interface_table)
self.facts["interfaces"] = ciscosmb_merge_dicts(
self.facts["interfaces"], interfaces
)
interfaces = self._populate_interfaces_configuration_portchanel(
portchanel_table
)
self.facts["interfaces"] = ciscosmb_merge_dicts(
self.facts["interfaces"], interfaces
)
def _populate_interfaces_description_interface(self, interface_table):
interfaces = dict()
for key in interface_table:
i = interface_table[key]
interface = dict()
interface["description"] = i[1]
if interface["description"] == "":
interface["description"] = None
interfaces[interface_canonical_name(i[0])] = interface
return interfaces
def _populate_interfaces_description_portchanel(self, interface_table):
interfaces = dict()
for key in interface_table:
interface = dict()
i = interface_table[key]
interface["description"] = i[1]
if interface["description"] == "":
interface["description"] = None
interfaces[interface_canonical_name(i[0])] = interface
return interfaces
def populate_interfaces_description(self, data):
tables = ciscosmb_split_to_tables(data)
interface_table = ciscosmb_parse_table(tables[0], False)
portchanel_table = ciscosmb_parse_table(tables[1], False)
interfaces = self._populate_interfaces_description_interface(interface_table)
self.facts["interfaces"] = ciscosmb_merge_dicts(
self.facts["interfaces"], interfaces
)
interfaces = self._populate_interfaces_description_portchanel(portchanel_table)
self.facts["interfaces"] = ciscosmb_merge_dicts(
self.facts["interfaces"], interfaces
)
def _populate_address_ipv4(self, ip_table):
ips = list()
for key in ip_table:
cidr = ip_table[key][0]
interface = interface_canonical_name(ip_table[key][1])
ip, mask = cidr.split("/")
ips.append(ip)
# add ips to interface
self._new_interface(interface)
if "ipv4" not in self.facts["interfaces"][interface]:
self.facts["interfaces"][interface]["ipv4"] = list()
self.facts["interfaces"][interface]["ipv4"].append(
dict(address=ip, subnet=mask)
)
return ips
def populate_addresses_ipv4(self, data):
tables = ciscosmb_split_to_tables(data)
ip_table = ciscosmb_parse_table(tables[0])
ips = self._populate_address_ipv4(ip_table)
self.facts["all_ipv4_addresses"] = ips
def _populate_address_ipv6(self, ip_table):
ips = list()
for key in ip_table:
ip = ip_table[key][3]
interface = interface_canonical_name(ip_table[key][0])
ips.append(ip)
# add ips to interface
self._new_interface(interface)
if "ipv6" not in self.facts["interfaces"][interface]:
self.facts["interfaces"][interface]["ipv6"] = list()
self.facts["interfaces"][interface]["ipv6"].append(dict(address=ip))
return ips
def _new_interface(self, interface):
if interface in self.facts["interfaces"]:
return
else:
self.facts["interfaces"][interface] = dict()
self.facts["interfaces"][interface]["mtu"] = self._mtu
self.facts["interfaces"][interface]["admin_state"] = "up"
self.facts["interfaces"][interface]["description"] = None
self.facts["interfaces"][interface]["state"] = "up"
self.facts["interfaces"][interface]["bandwith"] = None
self.facts["interfaces"][interface]["duplex"] = None
self.facts["interfaces"][interface]["negotiation"] = None
self.facts["interfaces"][interface]["control"] = None
return
def populate_addresses_ipv6(self, data):
tables = ciscosmb_split_to_tables(data)
ip_table = ciscosmb_parse_table(tables[0])
ips = self._populate_address_ipv6(ip_table)
self.facts["all_ipv6_addresses"] = ips
def populate_interfaces_mtu(self, data):
# by documentation SG350
match = re.search(r"Jumbo frames are enabled", data, re.M)
if match:
mtu = 9000
else:
mtu = 1518
self._mtu = mtu
def populate_neighbors(self, data):
tables = ciscosmb_split_to_tables(data)
neighbor_table = ciscosmb_parse_table(tables[0], allow_empty_fields=[3])
neighbors = dict()
for key in neighbor_table:
neighbor = neighbor_table[key]
ifcname = interface_canonical_name(neighbor[0])
host = neighbor[3]
port = neighbor[2]
hostport = {"host": host, "port": port}
if ifcname not in neighbors:
neighbors[ifcname] = list()
neighbors[ifcname].append(hostport)
self.facts["neighbors"] = neighbors
FACT_SUBSETS = dict(
default=Default,
hardware=Hardware,
interfaces=Interfaces,
config=Config,
)
VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
warnings = list()
def main():
"""main entry point for module execution"""
argument_spec = dict(
gather_subset=dict(
default=["!config"],
type="list",
elements="str",
choices=[
"all",
"default",
"hardware",
"interfaces",
"config",
"!hardware",
"!interfaces",
"!config",
],
)
)
argument_spec.update(ciscosmb_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: %s" % 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()