More fixes to ovs modules

Because this code deserves to be beautiful.

Change-Id: I0cc85d9a1103d4d4d7a19bf617838179197f9c90
This commit is contained in:
Arie
2016-09-27 12:18:57 +03:00
parent b2555a6ca8
commit 4f7d1de85e
12 changed files with 321 additions and 186 deletions

View File

@@ -1,23 +1,36 @@
# 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
# #
# analyzer.py: # 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.
# This file implements the following: # This file implements the following:
# 1. Analysis of the collected info # 1. Analysis of the collected info
# 2. Report any problems # 2. Report any problems
# 3. Report what is correct # 3. Report what is correct
#
import pprint
import re
import argparse import argparse
import subprocess from itertools import combinations
import json import json
import os import os
from itertools import combinations import pprint
import re
from common import settings, debug, get_router import subprocess
from common import load_json, get_subnet, is_network_public
import yaml import yaml
from openstack_dashboard.don.ovs.common import debug
from openstack_dashboard.don.ovs.common import get_router
from openstack_dashboard.don.ovs.common import get_subnet
from openstack_dashboard.don.ovs.common import is_network_public
from openstack_dashboard.don.ovs.common import load_json
from openstack_dashboard.don.ovs.common import settings
tick = '✔' tick = '✔'
cross = '✘' cross = '✘'
@@ -146,9 +159,8 @@ def get_vm_credentials(config_file='credentials.yaml'):
try: try:
with open(config_file, 'r') as s: with open(config_file, 'r') as s:
return yaml.safe_load(s) return yaml.safe_load(s)
except IOError, e: except IOError as e:
print '%s :%s' % (e.args[1], config_file) raise '%s :%s' % (e.args[1], config_file)
raise
def test_ping(info): def test_ping(info):
@@ -203,7 +215,8 @@ def run_ovs_command(cmd, comment=''):
return subprocess.check_output(cmd, return subprocess.check_output(cmd,
shell=True, shell=True,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
universal_newlines=True).replace('\t', ' ') universal_newlines=True).replace(
'\t', ' ')
def process_ovs_output(output): def process_ovs_output(output):
@@ -361,7 +374,8 @@ def analyze(json_filename, params):
def check_args(): def check_args():
parser = argparse.ArgumentParser(description='Static analysis of output of commands', parser = argparse.ArgumentParser(
description='Static analysis of output of commands',
formatter_class=argparse.ArgumentDefaultsHelpFormatter) formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--debug', dest='debug', parser.add_argument('--debug', dest='debug',
help='Enable debugging', help='Enable debugging',

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
# a copy of the License at # a copy of the License at

View File

@@ -1,10 +1,22 @@
# common.py: Common functions and data structures used by multiple modules. # 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.
# common.py: Common functions and data structures used by multiple modules.
import json
import paramiko import paramiko
import sys
import re
import pprint import pprint
import re
import subprocess import subprocess
import sys
import yaml import yaml
# Program settings # Program settings
@@ -17,7 +29,7 @@ settings = {
def debug(msg): def debug(msg):
if settings['debug']: if settings['debug']:
if not sys.stdout == sys.__stdout__: if sys.stdout != sys.__stdout__:
tmp = sys.stdout tmp = sys.stdout
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
print('DEBUG: ' + msg) print('DEBUG: ' + msg)
@@ -27,7 +39,7 @@ def debug(msg):
def error(msg): def error(msg):
if not sys.stdout == sys.__stdout__: if sys.stdout != sys.__stdout__:
tmp = sys.stdout tmp = sys.stdout
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
print('ERROR: ' + msg) print('ERROR: ' + msg)
@@ -37,7 +49,7 @@ def error(msg):
def warning(msg): def warning(msg):
if not sys.stdout == sys.__stdout__: if sys.stdout != sys.__stdout__:
tmp = sys.stdout tmp = sys.stdout
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
print('WARNING: ' + msg) print('WARNING: ' + msg)
@@ -49,7 +61,7 @@ def warning(msg):
def status_update(msg): def status_update(msg):
# storing in log file for interactive display on UI # storing in log file for interactive display on UI
log = open('collector_log.txt', 'w') log = open('collector_log.txt', 'w')
if not sys.stdout == sys.__stdout__: if sys.stdout != sys.__stdout__:
tmp = sys.stdout tmp = sys.stdout
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
print('STATUS: ' + msg) print('STATUS: ' + msg)
@@ -61,12 +73,11 @@ def status_update(msg):
def dump_json(json_info, json_filename): def dump_json(json_info, json_filename):
import json
try: try:
outfile = open(json_filename, "w") outfile = open(json_filename, "w")
except IOError, e: except IOError as e:
print e print(e)
print 'Couldn\'t open <%s>; Redirecting output to stdout' % json_filename print('Couldn\'t open <%s>; Redirecting output to stdout' % json_filename)
outfile = sys.stdout outfile = sys.stdout
json.dump(json_info, outfile) json.dump(json_info, outfile)
@@ -75,12 +86,11 @@ def dump_json(json_info, json_filename):
def load_json(json_filename): def load_json(json_filename):
import json
try: try:
infile = open(json_filename, "r") infile = open(json_filename, "r")
except IOError, e: except IOError as e:
print e print(e)
print 'Couldn\'t open <%s>; Error!' % json_filename print('Couldn\'t open <%s>; Error!' % json_filename)
return None return None
tmp = json.load(infile) tmp = json.load(infile)
@@ -94,7 +104,7 @@ def connect_to_box(server, username, password, timeout=3):
try: try:
ssh.connect(server, username=username, ssh.connect(server, username=username,
password=password, timeout=timeout) password=password, timeout=timeout)
except: except Exception:
return None return None
return ssh return ssh
# def connect_to_box (server, username, password,timeout=3) : # def connect_to_box (server, username, password,timeout=3) :
@@ -108,7 +118,7 @@ def ssh_cmd(ssh, cmd):
ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd) ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd)
error = ssh_stderr.read() error = ssh_stderr.read()
if len(error): if len(error):
print 'ERROR: ' + error print('ERROR: ' + error)
output = ssh_stdout.read() output = ssh_stdout.read()
ssh_stdout.flush() ssh_stdout.flush()
return output return output
@@ -230,13 +240,15 @@ def execute_cmd(cmd, sudo=False, shell=False, env=None):
shell=shell, shell=shell,
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
env=env, env=env,
universal_newlines=True).replace('\t', ' ') universal_newlines=True).replace(
'\t', ' ')
def get_instance_ips(objs): def get_instance_ips(objs):
ip_list = [] ip_list = []
for line in objs: for line in objs:
if re.search('^\+', line) or re.search('^$', line) or re.search('Networks', line): if re.search('^\+', line) or re.search('^$', line) or re.search(
'Networks', line):
continue continue
parts = line.split('|') parts = line.split('|')
parts = [x.strip() for x in parts] parts = [x.strip() for x in parts]
@@ -247,7 +259,8 @@ def get_instance_ips(objs):
# excluding ipv6 ip # excluding ipv6 ip
if len(entry.split(',')) > 1: if len(entry.split(',')) > 1:
# network = entry.split('=')[0] # network = entry.split('=')[0]
ip = filter(lambda a: re.search("(\d+\.\d+\.\d+\.\d+)", a) is not None, ip = filter(lambda a: re.search("(\d+\.\d+\.\d+\.\d+)",
a) is not None,
entry.split('=')[1].split(','))[0].strip() entry.split('=')[1].split(','))[0].strip()
ip_list.append(ip) ip_list.append(ip)
else: else:
@@ -259,7 +272,8 @@ def get_router_names(objs):
routers = [] routers = []
for line in objs: for line in objs:
if re.search('^\+', line) or re.search('^$', line) or re.search('external_gateway_info', line): if re.search('^\+', line) or re.search('^$', line) or re.search(
'external_gateway_info', line):
continue continue
parts = line.split('|') parts = line.split('|')
parts = [x.strip() for x in parts] parts = [x.strip() for x in parts]
@@ -272,9 +286,8 @@ def get_router_names(objs):
def get_env(file_path): def get_env(file_path):
try: try:
lines = open(file_path, 'r').read().splitlines() lines = open(file_path, 'r').read().splitlines()
except IOError, e: except IOError as e:
print "%s :%s" % (e.args[1], file_path) raise "%s :%s" % (e.args[1], file_path)
raise
env = {} env = {}
for line in lines: for line in lines:
if line.startswith('export'): if line.startswith('export'):
@@ -290,6 +303,5 @@ def get_vm_credentials(config_file='credentials.yaml'):
try: try:
with open(config_file, 'r') as s: with open(config_file, 'r') as s:
return yaml.safe_load(s) return yaml.safe_load(s)
except IOError, e: except IOError as e:
print '%s :%s' % (e.args[1], config_file) raise '%s :%s' % (e.args[1], config_file)
raise

View File

@@ -1,3 +1,15 @@
# 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.
from django import forms from django import forms

View File

@@ -1,7 +1,19 @@
# 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.
import pprint import pprint
import sys import sys
from common import load_json from openstack_dashboard.don.ovs.common import load_json
if len(sys.argv) != 2: if len(sys.argv) != 2:
print ('Usage: ' + sys.argv[0] + ' <json file to display>') print ('Usage: ' + sys.argv[0] + ' <json file to display>')

View File

@@ -1,12 +1,23 @@
# 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.
# ovs.py: Runs ovs-appctl command to check if A -> B flow is working fine. # ovs.py: Runs ovs-appctl command to check if A -> B flow is working fine.
#
#
import re
import argparse import argparse
import json import json
from common import debug, settings import re
from common import execute_cmd
from openstack_dashboard.don.ovs.common import debug
from openstack_dashboard.don.ovs.common import execute_cmd
from openstack_dashboard.don.ovs.common import settings
params = {} params = {}
@@ -74,7 +85,8 @@ def ovs_test(src_port_id, dst_port_id, tag, ovs_bridge):
if vlan != tag: if vlan != tag:
output_dict['errors'].append( output_dict['errors'].append(
'%s learnt on vlan %s but should have been learnt on vlan %s on port %s' % (smac, vlan, tag, port)) '%s learnt on vlan %s but should have been learnt on vlan %s on port %s' % (
smac, vlan, tag, port))
output_dict['pass'] = False output_dict['pass'] = False
return False return False
output_dict['debugs'].append( output_dict['debugs'].append(
@@ -116,11 +128,13 @@ def ovs_test(src_port_id, dst_port_id, tag, ovs_bridge):
output_dict['debugs'].append( output_dict['debugs'].append(
'Packet forwarded to correct port %s' % egress_port) 'Packet forwarded to correct port %s' % egress_port)
else: else:
output_dict['errors'].append('Packet forwarded to incorrect port %s, expected %s' % output_dict['errors'].append(
'Packet forwarded to incorrect port %s, expected %s' %
(egress_port, src_port_id)) (egress_port, src_port_id))
result = False result = False
else: else:
output_dict['errors'].append('No egress port assigned to packet! Expected %s' % output_dict['errors'].append(
'No egress port assigned to packet! Expected %s' %
src_port_id) src_port_id)
result = False result = False
@@ -132,17 +146,23 @@ def check_args():
global params global params
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='OVS test', formatter_class=argparse.ArgumentDefaultsHelpFormatter) description='OVS test',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--debug', dest='debug', parser.add_argument('--debug', dest='debug',
help='Enable debugging', default=False, action='store_true') help='Enable debugging',
default=False, action='store_true')
parser.add_argument('--src_port_id', dest='src_port_id', parser.add_argument('--src_port_id', dest='src_port_id',
help='OVS src port id (required)', type=str, required=True) help='OVS src port id (required)',
type=str, required=True)
parser.add_argument('--dst_port_id', dest='dst_port_id', parser.add_argument('--dst_port_id', dest='dst_port_id',
help='OVS dst port id (required)', type=str, required=True) help='OVS dst port id (required)',
type=str, required=True)
parser.add_argument( parser.add_argument(
'--tag', dest='tag', help='VLAN tag of port (required)', type=str, required=True) '--tag', dest='tag', help='VLAN tag of port (required)',
type=str, required=True)
parser.add_argument('--ovs_bridge', dest='ovs_bridge', parser.add_argument('--ovs_bridge', dest='ovs_bridge',
help='OVS bridge to be tested (required)', type=str, required=True) help='OVS bridge to be tested (required)',
type=str, required=True)
args = parser.parse_args() args = parser.parse_args()
settings['debug'] = args.debug settings['debug'] = args.debug
@@ -165,12 +185,12 @@ def main():
ovs_success = ovs_test(src_port_id, dst_port_id, tag, ovs_bridge) ovs_success = ovs_test(src_port_id, dst_port_id, tag, ovs_bridge)
output_dict[ output_dict[
'comment'] = 'ovs %s port %s --> %s' % (ovs_bridge, src_port_id, dst_port_id) 'comment'] = 'ovs %s port %s --> %s' % (
ovs_bridge, src_port_id, dst_port_id)
output_dict['pass'] = ovs_success output_dict['pass'] = ovs_success
a = json.dumps(output_dict, sort_keys=True, indent=4) a = json.dumps(output_dict, sort_keys=True, indent=4)
print a print(a)
pass
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,19 +1,35 @@
# 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.
# path.py: Figures out a path between two IP addresses and then traces it # path.py: Figures out a path between two IP addresses and then traces it
#
# HOWTO:
#
import re
import pprint
import subprocess
import argparse import argparse
import os.path
import signal
import json import json
import os.path
import pprint
import re
import signal
import subprocess
import time import time
from common import error, settings, debug, status_update
from common import load_json, execute_cmd, dump_json from openstack_dashboard.don.ovs.common import debug
from common import ip_to_intf, intf_to_namespace, router_to_namespace from openstack_dashboard.don.ovs.common import dump_json
from openstack_dashboard.don.ovs.common import error
from openstack_dashboard.don.ovs.common import execute_cmd
from openstack_dashboard.don.ovs.common import intf_to_namespace
from openstack_dashboard.don.ovs.common import ip_to_intf
from openstack_dashboard.don.ovs.common import load_json
from openstack_dashboard.don.ovs.common import router_to_namespace
from openstack_dashboard.don.ovs.common import settings
from openstack_dashboard.don.ovs.common import status_update
COUNT = 10 # how many packets to be captured by tcpdump COUNT = 10 # how many packets to be captured by tcpdump
@@ -63,7 +79,8 @@ def qrouter_usable(qrouter, src_ip, dst_ip, username, passwd):
return False return False
def launch_ping(src_ip, dst_ip, username, passwd, count, timeout, qrouter, filename): def launch_ping(src_ip, dst_ip, username, passwd,
count, timeout, qrouter, filename):
cmd = 'sudo ip netns exec ' + str(qrouter) cmd = 'sudo ip netns exec ' + str(qrouter)
cmd += ' python ping.py --src_ip %s --dst_ip %s --username "%s" --passwd "%s" --count %d --timeout %d' % \ cmd += ' python ping.py --src_ip %s --dst_ip %s --username "%s" --passwd "%s" --count %d --timeout %d' % \
(src_ip, dst_ip, username, passwd, count, timeout) (src_ip, dst_ip, username, passwd, count, timeout)
@@ -95,7 +112,6 @@ def capture_network_packets(params, hop_list):
net_info['pids'].append(pid) net_info['pids'].append(pid)
status_update( status_update(
'net: tcpdump launched with pid %d for interface %s' % (pid, dev)) 'net: tcpdump launched with pid %d for interface %s' % (pid, dev))
pass
def capture_packets(params, tag='src', src_tag=None): def capture_packets(params, tag='src', src_tag=None):
@@ -481,8 +497,6 @@ def path(params):
(src_ip, dst_ip)) (src_ip, dst_ip))
path_same_network(params, next_hop_list) path_same_network(params, next_hop_list)
pass
def main(): def main():

View File

@@ -1,19 +1,30 @@
# 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.
# ping.py: Runs a ping test from src_ip to dst_ip. Also provides analysis if # ping.py: Runs a ping test from src_ip to dst_ip. Also provides analysis if
# things are not okay (TBD). # things are not okay (TBD).
# #
# HOWTO: # HOWTO:
# #
# For OpenStack, this program must be run from inside the correct namespace # For OpenStack, this program must be run from inside the correct namespace
#
# sudo ip netns exec qrouter-ac41aab2-f9c3-4a06-8eef-f909ee1e6e50 python ping.py 10.0.3.3 10.0.2.4 cirros "cubswin:)" # sudo ip netns exec qrouter-ac41aab2-f9c3-4a06-8eef-f909ee1e6e50 python ping.py 10.0.3.3 10.0.2.4 cirros "cubswin:)"
#
import re
import argparse import argparse
import json import json
from common import connect_to_box, ssh_cmd import re
from common import settings
from openstack_dashboard.don.ovs.common import connect_to_box
from openstack_dashboard.don.ovs.common import settings
from openstack_dashboard.don.ovs.common import ssh_cmd
params = {} params = {}
@@ -56,8 +67,8 @@ def ping_test(src_ip, dst_ip, username, passwd, count, timeout):
result = True result = True
break break
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
print '\nkeyboardinterrupt caught (again)' print('\nkeyboardinterrupt caught (again)')
print '\n...Program Stopped Manually!' print('\n...Program Stopped Manually!')
raise raise
cmd_dict['pass'] = result cmd_dict['pass'] = result
output_dict['command_list'].append(cmd_dict) output_dict['command_list'].append(cmd_dict)
@@ -115,8 +126,7 @@ def main():
output_dict['comment'] = 'PING %s to %s' % (src_ip, dst_ip) output_dict['comment'] = 'PING %s to %s' % (src_ip, dst_ip)
output_dict['pass'] = ping_success output_dict['pass'] = ping_success
a = json.dumps(output_dict, sort_keys=True, indent=4) print(json.dumps(output_dict, sort_keys=True, indent=4))
print a
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,19 +1,36 @@
# 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
# #
# plot.py: Generates an SVG file showing the networking internals of a compute node. # http://www.apache.org/licenses/LICENSE-2.0
# #
import pprint # Unless required by applicable law or agreed to in writing, software
import subprocess # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
import re # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse import argparse
import sys import pprint
import random import random
import re
import subprocess
import sys
from common import settings, debug, warning from common import debug
from common import load_json, get_subnet from common import get_intf_ip
from common import get_vlan_tag, get_intf_ip, get_ip_network from common import get_ip_network
from common import get_subnet
from common import get_vlan_tag
from common import load_json
from common import settings
from common import warning
class DotGenerator: class DotGenerator(object):
"""Generates an SVG file showing the network internals of
a compute node.
"""
def __init__(self, in_json_filename, def __init__(self, in_json_filename,
compute_dot_file, compute_svg_file, compute_dot_file, compute_svg_file,
@@ -236,34 +253,34 @@ class DotGenerator:
return port_count return port_count
def __html_row_open(self): def __html_row_open(self):
print '<TR>' print('<TR>')
def __html_row_close(self): def __html_row_close(self):
print '</TR>' print('</TR>')
def __html_row(self, name, rspan, cspan, color, tag=None): def __html_row(self, name, rspan, cspan, color, tag=None):
# tags do not allow "-" (dash) in DOT language. Convert to "_" # tags do not allow "-" (dash) in DOT language. Convert to "_"
# (underscore) # (underscore)
if tag: if tag:
print '<TD ROWSPAN="%d" COLSPAN="%d" BGCOLOR="%s" PORT="%s">%s</TD>' % (rspan, cspan, color, tag.replace('-', '_'), name) print('<TD ROWSPAN="%d" COLSPAN="%d" BGCOLOR="%s" PORT="%s">%s</TD>' % (rspan, cspan, color, tag.replace('-', '_'), name))
else: else:
print '<TD ROWSPAN="%d" COLSPAN="%d" BGCOLOR="%s">%s</TD>' % (rspan, cspan, color, name) print('<TD ROWSPAN="%d" COLSPAN="%d" BGCOLOR="%s">%s</TD>' % (rspan, cspan, color, name))
pass pass
def __html_edge(selft, src_tag, dst_tag, color, penwidth="4", style=None): def __html_edge(selft, src_tag, dst_tag, color, penwidth="4", style=None):
src_tag = src_tag.replace('-', '_') src_tag = src_tag.replace('-', '_')
dst_tag = dst_tag.replace('-', '_') dst_tag = dst_tag.replace('-', '_')
if not style: if not style:
print '%s:s -> %s:n [color = "%s", penwidth = "%s"]' % (src_tag, print('%s:s -> %s:n [color = "%s", penwidth = "%s"]' % (src_tag,
dst_tag, dst_tag,
color, color,
penwidth) penwidth))
else: else:
print '%s:s -> %s:n [color = "%s", penwidth = "%s", style="%s"]' % (src_tag, print('%s:s -> %s:n [color = "%s", penwidth = "%s", style="%s"]' % (src_tag,
dst_tag, dst_tag,
color, color,
penwidth, penwidth,
style) style))
def __digraph_open(self, tag): def __digraph_open(self, tag):
msg = 'digraph DON_' + tag + ' {' + \ msg = 'digraph DON_' + tag + ' {' + \
@@ -276,36 +293,36 @@ concentrate = true;
compound = true; compound = true;
edge [dir=none] edge [dir=none]
''' '''
print msg print(msg)
def __digraph_close(self): def __digraph_close(self):
msg = '\n}\n' msg = '\n}\n'
print msg print(msg)
def __cluster_name(self, tag, col_span, color="white"): def __cluster_name(self, tag, col_span, color="white"):
self.__html_row_open() self.__html_row_open()
port = tag.replace(' ', '').replace('-', '_') port = tag.replace(' ', '').replace('-', '_')
print '<TD COLSPAN="%d" BORDER="0" BGCOLOR="%s" PORT="%s">%s</TD>' % (col_span, color, port, tag) print('<TD COLSPAN="%d" BORDER="0" BGCOLOR="%s" PORT="%s">%s</TD>' % (col_span, color, port, tag))
self.__html_row_close() self.__html_row_close()
def __cluster_open_plain(self, tag, label=None): def __cluster_open_plain(self, tag, label=None):
print 'subgraph cluster_%s {' % (tag) print('subgraph cluster_%s {' % (tag))
print 'style=filled' print('style=filled')
if label: if label:
print 'label="%s"' % (label) print('label="%s"' % (label))
def __cluster_close_plain(self): def __cluster_close_plain(self):
print '}\n' print('}\n')
def __cluster_open(self, tag, color="white"): def __cluster_open(self, tag, color="white"):
print 'subgraph cluster_%s {' % (tag) print('subgraph cluster_%s {' % (tag))
print '%s [ shape = plaintext, label = <' % (tag) print('%s [ shape = plaintext, label = <' % (tag))
print '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="5" CELLPADDING="5" BGCOLOR="%s">' % (color) print('<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="5" CELLPADDING="5" BGCOLOR="%s">' % (color))
pass pass
def __cluster_close(self): def __cluster_close(self):
print '</TABLE>>];\n' print('</TABLE>>];\n')
print '}\n' print('}\n')
pass pass
def __plot_title_edges(self, tag): def __plot_title_edges(self, tag):
@@ -451,7 +468,8 @@ edge [dir=none]
if br_int['ports'].has_key(qvo_port): if br_int['ports'].has_key(qvo_port):
tag = br_int['ports'][qvo_port]['tag'] tag = br_int['ports'][qvo_port]['tag']
self.__html_row('VLAN tag:' + tag, row_span, col_span, self.__html_row('VLAN tag:' + tag, row_span, col_span,
self.__get_vlan_color(tag), qvo_port + 'tag_' + tag) self.__get_vlan_color(tag),
qvo_port + 'tag_' + tag)
self.__html_row_close() self.__html_row_close()
col_span = self.__get_total_vm_port_count() col_span = self.__get_total_vm_port_count()
@@ -468,9 +486,7 @@ edge [dir=none]
self.__html_row_close() self.__html_row_close()
self.__cluster_close() self.__cluster_close()
pass
# TODO
def __plot_br_ex_to_br_int(self): def __plot_br_ex_to_br_int(self):
namespaces = self.info['namespaces'] namespaces = self.info['namespaces']
@@ -492,7 +508,6 @@ edge [dir=none]
dst_tag = 'network_br_int:' + intf dst_tag = 'network_br_int:' + intf
self.__html_edge(src_tag, dst_tag, self.__html_edge(src_tag, dst_tag,
self.__get_color('edge')) self.__get_color('edge'))
pass
def __plot_br_ex_network(self): def __plot_br_ex_network(self):
routers = self.info['routers'] routers = self.info['routers']
@@ -571,9 +586,11 @@ edge [dir=none]
port_id = '[' + br_int['ports'][intf]['id'] + '] ' port_id = '[' + br_int['ports'][intf]['id'] + '] '
color = self.__get_vlan_color(tag, intf) color = self.__get_vlan_color(tag, intf)
self.__html_row( self.__html_row(
port_id + intf, row_span, col_span, color, intf) port_id + intf, row_span,
col_span, color, intf)
# now plot the corresponding tap interface # now plot the corresponding tap interface
(tap_nms, tap) = self.__get_tap_interface(nms, intf) (tap_nms, tap) = self.__get_tap_interface(nms,
intf)
tag = br_int['ports'][tap]['tag'] tag = br_int['ports'][tap]['tag']
color = self.__get_vlan_color(tag, tap) color = self.__get_vlan_color(tag, tap)
port_id = '[' + br_int['ports'][tap]['id'] + '] ' port_id = '[' + br_int['ports'][tap]['id'] + '] '
@@ -602,7 +619,8 @@ edge [dir=none]
tag = br_int['ports'][tap_intf]['tag'] tag = br_int['ports'][tap_intf]['tag']
self.__html_row('VLAN tag:' + tag, row_span, col_span, self.__html_row('VLAN tag:' + tag, row_span, col_span,
self.__get_vlan_color(tag), tap_intf + 'tag_' + tag) self.__get_vlan_color(tag),
tap_intf + 'tag_' + tag)
self.__html_row_close() self.__html_row_close()
@@ -695,7 +713,6 @@ edge [dir=none]
color = self.__get_edge_color(src_tag, dst_tag) color = self.__get_edge_color(src_tag, dst_tag)
self.__html_edge(src_tag, dst_tag, color) self.__html_edge(src_tag, dst_tag, color)
break break
pass
def __plot_linuxbridge_to_br_int(self): def __plot_linuxbridge_to_br_int(self):
brctl = self.info['brctl'] brctl = self.info['brctl']
@@ -713,7 +730,6 @@ edge [dir=none]
color = self.__get_edge_color(src_tag, dst_tag) color = self.__get_edge_color(src_tag, dst_tag)
self.__html_edge(src_tag, dst_tag, color) self.__html_edge(src_tag, dst_tag, color)
break break
pass
def __plot_br_int_to_br_tun(self, tag): def __plot_br_int_to_br_tun(self, tag):
br_int = self.info['bridges']['br-int']['ports'] br_int = self.info['bridges']['br-int']['ports']
@@ -734,7 +750,6 @@ edge [dir=none]
self.__html_edge(src_tag, dst_tag, self.__html_edge(src_tag, dst_tag,
self.__get_color('edge')) self.__get_color('edge'))
return return
pass
def plot_combined(self): def plot_combined(self):
self.outfile = open(self.combined_dot_file, 'w') self.outfile = open(self.combined_dot_file, 'w')
@@ -845,12 +860,15 @@ edge [dir=none]
def check_args(): def check_args():
parser = argparse.ArgumentParser(description='Plot the compute node network internals', parser = argparse.ArgumentParser(
description='Plot the compute node network internals',
formatter_class=argparse.ArgumentDefaultsHelpFormatter) formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--debug', dest='debug', parser.add_argument('--debug', dest='debug',
help='Enable debugging', default=True, action='store_true') help='Enable debugging',
default=True, action='store_true')
parser.add_argument('--info_file', dest='info_file', parser.add_argument('--info_file', dest='info_file',
help='Info is read in JSON format in this file', default="don.json", type=str) help='Info is read in JSON format in this file',
default="don.json", type=str)
parser.add_argument('--compute_file', dest='compute_file', parser.add_argument('--compute_file', dest='compute_file',
help='[compute_file].dot and [compute_file].svg will be generated for compute node', default="compute", type=str) help='[compute_file].dot and [compute_file].svg will be generated for compute node', default="compute", type=str)
parser.add_argument('--network_file', dest='network_file', parser.add_argument('--network_file', dest='network_file',
@@ -858,7 +876,8 @@ def check_args():
parser.add_argument('--combined_file', dest='combined_file', parser.add_argument('--combined_file', dest='combined_file',
help='[combined_file].dot and [combined_file].svg will be generated', default="don", type=str) help='[combined_file].dot and [combined_file].svg will be generated', default="don", type=str)
parser.add_argument('--highlight_file', dest='highlight_file', parser.add_argument('--highlight_file', dest='highlight_file',
help='pass and fail node are specified in this file', default=None, type=str) help='pass and fail node are specified in this file',
default=None, type=str)
args = parser.parse_args() args = parser.parse_args()

View File

@@ -1,13 +1,23 @@
# 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.
# run_nms_cmd.py: This needs to be run from inside appropriate namespace # run_nms_cmd.py: This needs to be run from inside appropriate namespace
#
# sudo ip netns exec qrouter-ac41aab2-f9c3-4a06-8eef-f909ee1e6e50 python # run_nms_cmd.py "command" # sudo ip netns exec qrouter-ac41aab2-f9c3-4a06-8eef-f909ee1e6e50 python # run_nms_cmd.py "command"
#
import argparse import argparse
import json import json
from common import connect_to_box, ssh_cmd
from common import settings
from openstack_dashboard.don.ovs.common import connect_to_box
from openstack_dashboard.don.ovs.common import settings
from openstack_dashboard.don.ovs.common import ssh_cmd
params = {} params = {}
@@ -45,8 +55,8 @@ def run_nms_cmd(args):
cmd_dict['cmd'] = cmd cmd_dict['cmd'] = cmd
cmd_dict['output'] = output cmd_dict['output'] = output
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
print '\nkeyboardinterrupt caught (again)' print('\nkeyboardinterrupt caught (again)')
print '\n...Program Stopped Manually!' print('\n...Program Stopped Manually!')
result = False result = False
raise raise
cmd_dict['pass'] = result cmd_dict['pass'] = result
@@ -58,17 +68,23 @@ def check_args():
global params global params
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Run command from inside nms', formatter_class=argparse.ArgumentDefaultsHelpFormatter) description='Run command from inside nms',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--debug', dest='debug', parser.add_argument('--debug', dest='debug',
help='Enable debugging', default=False, action='store_true') help='Enable debugging',
default=False, action='store_true')
parser.add_argument('--host_ip', dest='host_ip', parser.add_argument('--host_ip', dest='host_ip',
help='IP where the command will be run', type=str, required=True) help='IP where the command will be run',
type=str, required=True)
parser.add_argument('--username', dest='username', parser.add_argument('--username', dest='username',
help='SSH login username (required)', type=str, required=True) help='SSH login username (required)',
type=str, required=True)
parser.add_argument('--passwd', dest='passwd', parser.add_argument('--passwd', dest='passwd',
help='SSH login passwd (required)', type=str, required=True) help='SSH login passwd (required)',
type=str, required=True)
parser.add_argument('--cmd', dest='cmd', parser.add_argument('--cmd', dest='cmd',
help='cmd to be run', type=str, required=True) help='cmd to be run',
type=str, required=True)
args = parser.parse_args() args = parser.parse_args()
settings['debug'] = args.debug settings['debug'] = args.debug
@@ -85,8 +101,7 @@ def main():
output_dict['pass'] = run_nms_cmd(params) output_dict['pass'] = run_nms_cmd(params)
a = json.dumps(output_dict, sort_keys=True, indent=4) print(json.dumps(output_dict, sort_keys=True, indent=4))
print a
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,11 +1,19 @@
# 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.
from django.conf.urls import patterns from django.conf.urls import patterns
from django.conf.urls import url from django.conf.urls import url
from don.ovs.views \ from don.ovs.views import IndexView
import IndexView import openstack_dashboard.don.ovs.views as views
from . import views
urlpatterns = patterns( urlpatterns = patterns(
'', '',

View File

@@ -1,20 +1,33 @@
from horizon import views # Licensed under the Apache License, Version 2.0 (the "License"); you may
from django.http import HttpResponse # 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.
from django.conf import settings from django.conf import settings
from plot import DotGenerator from django.http import HttpResponse
import os
import subprocess
from .forms import PingForm
# from django.shortcuts import render_to_response
from horizon import messages
import analyzer
import path
from common import execute_cmd, get_instance_ips, get_env, get_router_names
import json
import shlex
from django.shortcuts import render from django.shortcuts import render
from forms import PingForm
from horizon import messages
from horizon import views
import json
import os
import shlex
import subprocess
import openstack_dashboard.don.ovs.analyzer as analyzer
from openstack_dashboard.don.ovs.common import execute_cmd
from openstack_dashboard.don.ovs.common import get_env
from openstack_dashboard.don.ovs.common import get_instance_ips
from openstack_dashboard.don.ovs.common import get_router_names
import openstack_dashboard.don.ovs.path as path
from openstack_dashboard.don.ovs.plot import DotGenerator
class IndexView(views.APIView): class IndexView(views.APIView):
@@ -31,8 +44,6 @@ def index(request):
def view(request): def view(request):
# import pdb
# pdb.set_trace()
pwd = settings.ROOT_PATH # +'/openstack_dashboard/dashboards/admin/don/' pwd = settings.ROOT_PATH # +'/openstack_dashboard/dashboards/admin/don/'
JSON_FILE = pwd + '/don/ovs/don.json' JSON_FILE = pwd + '/don/ovs/don.json'
@@ -53,7 +64,6 @@ def view(request):
COMBINED_SVG_FILE = static_path + '/don/don.svg' COMBINED_SVG_FILE = static_path + '/don/don.svg'
macro = {} macro = {}
# return HttpResponseRedirect('static/view.html')
plotter = DotGenerator(JSON_FILE, plotter = DotGenerator(JSON_FILE,
COMPUTE_DOT_FILE, COMPUTE_DOT_FILE,
@@ -72,12 +82,11 @@ def view(request):
plotter.plot_combined() plotter.plot_combined()
plotter.generate_combined_svg() plotter.generate_combined_svg()
# return HttpResponseRedirect('static/view.html')
return render(request, "don/ovs/views.html", macro) return render(request, "don/ovs/views.html", macro)
def analyze(request): def analyze(request):
# pwd = settings.BASE_DIR
pwd = settings.ROOT_PATH pwd = settings.ROOT_PATH
JSON_FILE = pwd + '/don/ovs/don.json' JSON_FILE = pwd + '/don/ovs/don.json'
@@ -89,14 +98,9 @@ def analyze(request):
'test:ovs': True, 'test:ovs': True,
'test:report_file': pwd + '/don/templates/don/don.report.html', 'test:report_file': pwd + '/don/templates/don/don.report.html',
} }
print "params ====> ", params print("params ====> ", params)
analyzer.analyze(JSON_FILE, params) analyzer.analyze(JSON_FILE, params)
# output = analyzer.analyze(JSON_FILE, params)
# html = '<html><body>Output: %s</body></html>' % output
# return HttpResponse(html)
# return HttpResponseRedirect('/static/don.report.html')
return render(request, "don/ovs/analyze.html") return render(request, "don/ovs/analyze.html")
# return render_to_response('don/ovs/analyze.html')
def test(request): def test(request):
@@ -111,13 +115,10 @@ def ping(request):
# check whether it's valid: # check whether it's valid:
if form.is_valid(): if form.is_valid():
# process the data in form.cleaned_data as required # process the data in form.cleaned_data as required
# ...
# redirect to a new URL: # redirect to a new URL:
src_ip = form.cleaned_data['src_ip'] src_ip = form.cleaned_data['src_ip']
dst_ip = form.cleaned_data['dst_ip'] dst_ip = form.cleaned_data['dst_ip']
router = form.cleaned_data['router'] router = form.cleaned_data['router']
# html = '<html><body>SIP: %s DIP: %s router: %s</body></html>' % (src_ip, dst_ip, router)
# return HttpResponse(html)
static_path = settings.STATIC_ROOT static_path = settings.STATIC_ROOT
pwd = settings.ROOT_PATH pwd = settings.ROOT_PATH
JSON_FILE = pwd + '/don/ovs/don.json' JSON_FILE = pwd + '/don/ovs/don.json'
@@ -148,7 +149,6 @@ def ping(request):
NETWORK_SVG_FILE = None NETWORK_SVG_FILE = None
COMBINED_DOT_FILE = static_path + '/don/ping.dot' COMBINED_DOT_FILE = static_path + '/don/ping.dot'
COMBINED_SVG_FILE = static_path + '/don/ping.svg' COMBINED_SVG_FILE = static_path + '/don/ping.svg'
# HIGHLIGHT_FILE = pwd + '/don/ovs/static/ping.html'
HIGHLIGHT_FILE = static_path + '/don/ping.html' HIGHLIGHT_FILE = static_path + '/don/ping.html'
plotter = DotGenerator(JSON_FILE, plotter = DotGenerator(JSON_FILE,
@@ -163,7 +163,6 @@ def ping(request):
plotter.plot_combined() plotter.plot_combined()
plotter.generate_combined_svg() plotter.generate_combined_svg()
# return HttpResponseRedirect('/static/path.html')
return render(request, 'don/ovs/path.html') return render(request, 'don/ovs/path.html')
# if a GET (or any other method) we'll create a blank form # if a GET (or any other method) we'll create a blank form
@@ -177,7 +176,8 @@ def ping(request):
ip_list = get_instance_ips(output) ip_list = get_instance_ips(output)
ip_list.sort() ip_list.sort()
router_op = execute_cmd( router_op = execute_cmd(
['neutron', 'router-list'], sudo=False, shell=False, env=myenv).split('\n') ['neutron', 'router-list'],
sudo=False, shell=False, env=myenv).split('\n')
router_list = get_router_names(router_op) router_list = get_router_names(router_op)
router_list.sort() router_list.sort()
# insert first value of select menu # insert first value of select menu
@@ -197,11 +197,11 @@ def collect(request):
status = 0 status = 0
BASE_DIR = settings.ROOT_PATH BASE_DIR = settings.ROOT_PATH
# CUR_DIR = os.getcwd()
os.chdir(BASE_DIR + '/don/ovs') os.chdir(BASE_DIR + '/don/ovs')
cmd = 'sudo python collector.py' cmd = 'sudo python collector.py'
for line in run_command(cmd): for line in run_command(cmd):
if line.startswith('STATUS:') and line.find('Writing collected info') != -1: if line.startswith('STATUS:') and line.find(
'Writing collected info') != -1:
status = 1 status = 1
macro['collect_status'] = \ macro['collect_status'] = \
"Collecton successful. Click visualize to display" "Collecton successful. Click visualize to display"
@@ -230,4 +230,5 @@ def get_status(request):
BASE_DIR = settings.ROOT_PATH + '/don/ovs/' BASE_DIR = settings.ROOT_PATH + '/don/ovs/'
status = open(BASE_DIR + 'collector_log.txt', 'r').readline() status = open(BASE_DIR + 'collector_log.txt', 'r').readline()
if status != " " and status != '\n': if status != " " and status != '\n':
return HttpResponse(json.dumps({'status': status}), content_type="application/json") return HttpResponse(json.dumps(
{'status': status}), content_type="application/json")