#!/usr/libexec/platform-python # vim: tabstop=4 shiftwidth=4 softtabstop=4 # Copyright 2017 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. __author__ = 'tkucherera' import optparse import os import signal import sys import subprocess try: signal.signal(signal.SIGPIPE, signal.SIG_DFL) except AttributeError: pass path = os.path.dirname(os.path.realpath(__file__)) path = os.path.realpath(os.path.join(path, '..', 'lib', 'python')) if path.startswith('/opt'): sys.path.append(path) import confluent.client as client argparser = optparse.OptionParser( usage="Usage: %prog -i -e ", ) argparser.add_option('-i', '--interface', type='str', help='interface to check path against for the start node') argparser.add_option('-e', '--eface', type='str', help='interface to check path against for the end node') argparser.add_option('-c', '--cumulus', action="store_true", dest="cumulus", help='return layer 2 route through cumulus switches only') (options, args) = argparser.parse_args() try: start_node = args[0] end_node = args[1] interface = options.interface eface = options.eface except IndexError: argparser.print_help() sys.exit(1) session = client.Command() def get_neighbors(switch): switch_neigbors = [] url = '/networking/neighbors/by-switch/{0}/by-peername/'.format(switch) for neighbor in session.read(url): switch = neighbor['item']['href'].strip('/') if switch in all_switches: switch_neigbors.append(switch) return switch_neigbors def find_path(start, end, path=[]): path = path + [start] if start == end: return path # If start and end are the same, return the path for node in get_neighbors(start): if node not in path: new_path = find_path(node, end, path) if new_path: return new_path # If a path is found, return it return None # If no path is found, return None def is_cumulus(switch): try: read_attrib = subprocess.check_output(['nodeattrib', switch, 'hardwaremanagement.method']) except subprocess.CalledProcessError: return False for attribs in read_attrib.decode('utf-8').split('\n'): if len(attribs.split(':')) > 1: attrib = attribs.split(':') if attrib[2].strip() == 'affluent': return True else: return False else: return False def host_to_switch(node, interface=None): # first check the the node config to see what switches are connected # if host is in rhel can use nmstate package if node in all_switches: return [node] switches = [] netarg = 'net.*.switch' if interface: netarg = 'net.{0}.switch'.format(interface) try: read_attrib = subprocess.check_output(['nodeattrib', node, netarg]) except subprocess.CalledProcessError: return False for attribs in read_attrib.decode('utf-8').split('\n'): attrib = attribs.split(':') try: if ' net.mgt.switch' in attrib or attrib[2] == '': continue except IndexError: continue switch = attrib[2].strip() if is_cumulus(switch) and options.cumulus: switches.append(switch) else: switches.append(switch) return switches def path_between_nodes(start_switches, end_switches): for start_switch in start_switches: for end_switch in end_switches: if start_switch == end_switch: return [start_switch] else: path = find_path(start_switch, end_switch) if path: return path else: return 'No path found' all_switches = [] for res in session.read('/networking/neighbors/by-switch/'): if 'error' in res: sys.stderr.write(res['error'] + '\n') exitcode = 1 else: switch = (res['item']['href'].replace('/', '')) all_switches.append(switch) end_nodeslist = [] nodelist = '/noderange/{0}/nodes/'.format(end_node) for res in session.read(nodelist): if 'error' in res: sys.stderr.write(res['error'] + '\n') exitcode = 1 else: elem=(res['item']['href'].replace('/', '')) end_nodeslist.append(elem) start_switches = host_to_switch(start_node, interface) for end_node in end_nodeslist: if end_node: end_switches = host_to_switch(end_node, eface) if not end_switches: print('Error: net.{0}.switch attribute is not valid') continue path = path_between_nodes(start_switches, end_switches) print(f'{start_node} to {end_node}: {path}') # TODO dont put switches that are connected through management interfaces.