From d689384e02bd85e3e78d3bfda1ed3ea07fcd0875 Mon Sep 17 00:00:00 2001 From: Erik Reid <erik.reid@geant.org> Date: Sat, 10 Nov 2018 10:36:49 +0100 Subject: [PATCH] pass raw console output from ssh process --- inventory_provider/juniper.py | 77 +++++++++++++++---------- inventory_provider/router_interfaces.py | 42 +++++++++----- 2 files changed, 74 insertions(+), 45 deletions(-) diff --git a/inventory_provider/juniper.py b/inventory_provider/juniper.py index 27ae3342..557ef658 100644 --- a/inventory_provider/juniper.py +++ b/inventory_provider/juniper.py @@ -8,9 +8,12 @@ import paramiko from inventory_provider.constants import JUNIPER_LOGGER_NAME -def neighbors(router, routing_instances=["IAS"], group_expression=None): +def neighbors( + parsed_json_output, + routing_instances=["IAS"], + group_expression=r'^GEANT-IX(v6)?[\s-].*$'): - for config in router["bgp"]["configuration"]: + for config in parsed_json_output["configuration"]: for ri in config["routing-instances"]: for inst in ri["instance"]: if inst["name"]["data"] not in routing_instances: @@ -25,8 +28,8 @@ def neighbors(router, routing_instances=["IAS"], group_expression=None): yield n -def interfaces(router): - for ifc_info in router["interfaces"]["interface-information"]: +def interfaces(parsed_json_output): + for ifc_info in parsed_json_output["interface-information"]: for ifc_list in ifc_info["logical-interface"]: for ifc in ifc_list: yield { @@ -40,50 +43,66 @@ def interfaces(router): @contextlib.contextmanager -def ssh_connection(router, ssh_params): +def ssh_connection(hostname, ssh_params): import os key_filename = os.path.join(os.path.dirname(__file__), ssh_params["private-key"]) known_hosts = os.path.join(os.path.dirname(__file__), ssh_params["known-hosts"]) k = paramiko.DSSKey.from_private_key_file(key_filename) - router["hostname"] = "mx1.ams.nl.geant.net" with paramiko.SSHClient() as ssh: ssh.load_host_keys(known_hosts) ssh.connect( - hostname=router["hostname"], + hostname=hostname, username="Monit0r", pkey=k) yield ssh -def exec_router_commands_json(router, ssh_params, commands): +def ssh_exec_commands(hostname, ssh_params, commands): juniper_logger = logging.getLogger(JUNIPER_LOGGER_NAME) - with ssh_connection(router, ssh_params) as ssh: + with ssh_connection(hostname, ssh_params) as ssh: _, stdout, _ = ssh.exec_command("set cli screen-length 0") assert stdout.channel.recv_exit_status() == 0 - def _dups_to_list(pairs): - counter_map = {} - for k, v in pairs: - counter_map.setdefault(k, []).append(v) - result = {} - for k, v in counter_map.items(): - if len(v) == 1: - result[k] = v[0] - else: - result[k] = v - return result - for c in commands: - juniper_logger.debug("command: '%s'" % (c + " | display json")) - _, stdout, _ = ssh.exec_command(c + " | display json") + juniper_logger.debug("command: '%s'" % c) + _, stdout, _ = ssh.exec_command(c) assert stdout.channel.recv_exit_status() == 0 # TODO: error handling - output = stdout.read() - if output: - juniper_logger.debug("%r output: [%d] %r" % (router, len(output), output[:20])) - yield json.loads(output, object_pairs_hook=_dups_to_list) + yield stdout.read().decode("utf-8") + + +def shell_commands(): + + def _dups_to_list(pairs): + counter_map = {} + for k, v in pairs: + counter_map.setdefault(k, []).append(v) + result = {} + for k, v in counter_map.items(): + if len(v) == 1: + result[k] = v[0] else: - juniper_logger.debug("%r output empty" % router) - yield {} + result[k] = v + return result + + yield { + "command": "set cli screen-length 0", + "parser": lambda _: (None, None) + } + + yield { + "command": 'show configuration routing-instances IAS protocols bgp | display json', + "parser": lambda txt: ("bgp", list(neighbors(json.loads(txt))) if txt else {}) + } + + yield { + "command": 'show configuration logical-systems VRR protocols bgp | display json', + "parser": lambda txt: ("vrr", json.loads(txt) if txt else {}) + } + + yield { + "command": 'show interfaces descriptions | display json', + "parser": lambda txt: ("interfaces", list(interfaces(json.loads(txt, object_pairs_hook=_dups_to_list))) if txt else {}) + } diff --git a/inventory_provider/router_interfaces.py b/inventory_provider/router_interfaces.py index f4e5751b..a8788a5d 100644 --- a/inventory_provider/router_interfaces.py +++ b/inventory_provider/router_interfaces.py @@ -17,11 +17,11 @@ def get_router_interfaces_q(router, params, q): threading_logger.debug("[<<EXIT] get_router_interfaces_q: %r" % router) -def exec_router_commands_json_q(router, ssh_params, commands, q): +def ssh_exec_commands_q(hostname, ssh_params, commands, q): threading_logger = logging.getLogger(constants.THREADING_LOGGER_NAME) - threading_logger.debug("[ENTER>>] exec_router_commands_q: %r" % router) - q.put(list(juniper.exec_router_commands_json(router, ssh_params, commands))) - threading_logger.debug("[<<EXIT] exec_router_commands_q: %r" % router) + threading_logger.debug("[ENTER>>] exec_router_commands_q: %r" % hostname) + q.put(list(juniper.ssh_exec_commands(hostname, ssh_params, commands))) + threading_logger.debug("[<<EXIT] exec_router_commands_q: %r" % hostname) def get_router_details(router, params, q): @@ -30,28 +30,38 @@ def get_router_details(router, params, q): threading_logger.debug("[ENTER>>]get_router_details: %r" % router) - commands = [ - 'show configuration routing-instances IAS protocols bgp', - # 'show configuration routing-instances IAS protocols bgp | display set | match neighbor | match description | match "GEANT-IX | GEANT-IX-"', - # 'show configuration routing-instances IAS protocols bgp | display set | match neighbor | match description | match "GEANT-IXv6 | GEANT-IXv6-"', - 'show configuration logical-systems VRR protocols bgp', - # 'show configuration logical-systems VRR protocols bgp group VPN-RR-INTERNAL | match "neigh|desc"', - # 'show configuration logical-systems VRR protocols bgp group VPN-RR | match "neigh|desc"' - 'show interfaces descriptions' - ] + commands = list(juniper.shell_commands()) snmpifc_proc_queue = Queue() - snmpifc_proc = Process(target=get_router_interfaces_q, args=(router, params, snmpifc_proc_queue)) + snmpifc_proc = Process( + target=get_router_interfaces_q, + args=(router, params, snmpifc_proc_queue)) snmpifc_proc.start() commands_proc_queue = Queue() - commands_proc = Process(target=exec_router_commands_json_q, args=(router, params["ssh"], commands, commands_proc_queue)) + commands_proc = Process( + target=ssh_exec_commands_q, + args=( + router["hostname"], + params["ssh"], + [c["command"] for c in commands], + commands_proc_queue)) commands_proc.start() threading_logger.debug("waiting for commands result: %r" % router) command_output = commands_proc_queue.get() assert len(command_output) == len(commands) - result = dict(zip(["bgp", "vrr", "interfaces"], command_output)) + + for i, o in enumerate(command_output): + with open("/tmp/%s-%d.output" % (router["hostname"], i), "w") as f: + f.write(o) + + result = {} + for c, o in zip(commands, command_output): + parsed = c["parser"](o) + if parsed[0]: + result[parsed[0]] = parsed[1] + commands_proc.join() threading_logger.debug("... got commands result & joined: %r" % router) -- GitLab