mirror of
https://github.com/xcat2/confluent.git
synced 2024-11-21 17:11:58 +00:00
174 lines
5.3 KiB
Plaintext
Executable File
174 lines
5.3 KiB
Plaintext
Executable File
#!/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 <start_node> -i <interface> <end_node> -e <eface>",
|
|
)
|
|
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.
|
|
|
|
|
|
|
|
|
|
|
|
|