diff --git a/etc/don/don.conf b/etc/don/don.conf new file mode 100644 index 0000000..3649b18 --- /dev/null +++ b/etc/don/don.conf @@ -0,0 +1,6 @@ +[DEFAULT] +# Deployment type should be devstack/multinode +deployment_type=multinode + +[neutron] + diff --git a/openstack_dashboard/don/__init__.py b/openstack_dashboard/don/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openstack_dashboard/don/api.py b/openstack_dashboard/don/api.py new file mode 100644 index 0000000..8503965 --- /dev/null +++ b/openstack_dashboard/don/api.py @@ -0,0 +1,22 @@ +from don import models + + +def save_data(timestamp, data): + wb = models.collector.objects.create(timestamp=timestamp, data=data) + wb.save() + return True + + +def list_collection(request): + return models.collector.objects.values('id', 'timestamp', 'data') + + +def get_collection(request, id=None): + try: + return models.collector.objects.get(id=id) + except models.collector.DoesNotExist: + return None + + +def remove_collection(request, id): + models.collector.objects.get(id=id).delete() diff --git a/openstack_dashboard/don/archive/__init__.py b/openstack_dashboard/don/archive/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openstack_dashboard/don/archive/panel.py b/openstack_dashboard/don/archive/panel.py new file mode 100644 index 0000000..136765b --- /dev/null +++ b/openstack_dashboard/don/archive/panel.py @@ -0,0 +1,10 @@ +from django.utils.translation import ugettext_lazy as _ +import horizon +from don import dashboard + + +class archive(horizon.Panel): + name = _("Archive") + slug = "archive" + +dashboard.DonDashboard.register(archive) diff --git a/openstack_dashboard/don/archive/templates/archive/index.html b/openstack_dashboard/don/archive/templates/archive/index.html new file mode 100644 index 0000000..40a83ca --- /dev/null +++ b/openstack_dashboard/don/archive/templates/archive/index.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "DON Archive" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("DON Archive") %} +{% endblock page_header %} + +{% block main %} + {{ table.render }} +{% endblock %} diff --git a/openstack_dashboard/don/archive/templates/archive/view.html b/openstack_dashboard/don/archive/templates/archive/view.html new file mode 100644 index 0000000..1b533a3 --- /dev/null +++ b/openstack_dashboard/don/archive/templates/archive/view.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "DON View" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("View") %} +{% endblock page_header %} + +{% block main %} + +{% endblock %} diff --git a/openstack_dashboard/don/archive/urls.py b/openstack_dashboard/don/archive/urls.py new file mode 100644 index 0000000..d7f59db --- /dev/null +++ b/openstack_dashboard/don/archive/urls.py @@ -0,0 +1,13 @@ +from django.conf.urls import patterns +from django.conf.urls import url +from don.archive.views \ + import ArchiveView +from . import views + +urlpatterns = patterns( + '', + # url(r'^dbview/',DBView.as_view() , name='dbview'), + url(r'^dbview/', views.dbview, name='dbview'), + url(r'^$', ArchiveView.as_view(), name='index'), + +) diff --git a/openstack_dashboard/don/archive/views.py b/openstack_dashboard/don/archive/views.py new file mode 100644 index 0000000..bab02aa --- /dev/null +++ b/openstack_dashboard/don/archive/views.py @@ -0,0 +1,49 @@ +from django.core.urlresolvers import reverse +from don import api +from don import tables as don_tables +from horizon import tables +# from horizon.views import APIView +import time +from django.conf import settings +from django import http + + +class ArchiveView(tables.DataTableView): + template_name = 'don/archive/index.html' + table_class = don_tables.CollectionTable + + def get_data(self): + data = api.list_collection(self.request) + for item in data: + item['timestamp'] = str(time.ctime(float(item.get('timestamp')))) + return data + + +def dbview(request): + id = request.GET.get('id') + data = api.get_collection(request, id) + pwd = settings.ROOT_PATH + JSON_FILE = pwd + '/don/ovs/don.json' + don = open(JSON_FILE, 'w') + don.write(str(data.data)) + don.close() + return http.HttpResponseRedirect( + reverse('horizon:don:ovs:view')) + +''' +class DBView(APIView): + template_name = 'don/archive/view.html' + + def get_data(self,request, context, *args, **kwargs): + id = self.request.GET.get('id') + data = api.get_collection(self.request,id) + pwd = settings.ROOT_PATH + JSON_FILE = pwd + '/don/ovs/don.json' + don = open(JSON_FILE,'w') + don.write(str(data.data)) + don.close() + time.sleep(2) + return http.HttpResponseRedirect( + reverse('horizon:don:ovs:view')) + +''' diff --git a/openstack_dashboard/don/dashboard.py b/openstack_dashboard/don/dashboard.py new file mode 100644 index 0000000..3b138a8 --- /dev/null +++ b/openstack_dashboard/don/dashboard.py @@ -0,0 +1,13 @@ +from django.utils.translation import ugettext_lazy as _ + +import horizon + + +class DonDashboard(horizon.Dashboard): + name = _("DON") + slug = "don" + panels = ('ovs', 'archive') # Add your panels here. + default_panel = 'ovs' # Specify the slug of the dashboard's default panel. + + +horizon.register(DonDashboard) diff --git a/openstack_dashboard/don/models.py b/openstack_dashboard/don/models.py new file mode 100644 index 0000000..ed5b3e0 --- /dev/null +++ b/openstack_dashboard/don/models.py @@ -0,0 +1,11 @@ +""" +Stub file to work around django bug: https://code.djangoproject.com/ticket/7198 +""" + +from django.db import models + + +class collector(models.Model): + # collector table fields + timestamp = models.CharField(max_length=50) + data = models.TextField() diff --git a/openstack_dashboard/don/ovs/__init__.py b/openstack_dashboard/don/ovs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/openstack_dashboard/don/ovs/admin-openrc.sh b/openstack_dashboard/don/ovs/admin-openrc.sh new file mode 100644 index 0000000..bbdb7c3 --- /dev/null +++ b/openstack_dashboard/don/ovs/admin-openrc.sh @@ -0,0 +1,37 @@ + +#!/bin/bash + +# To use an OpenStack cloud you need to authenticate against the Identity +# service named keystone, which returns a **Token** and **Service Catalog**. +# The catalog contains the endpoints for all services the user/tenant has +# access to - such as Compute, Image Service, Identity, Object Storage, Block +# Storage, and Networking (code-named nova, glance, keystone, swift, +# cinder, and neutron). +# +# *NOTE*: Using the 2.0 *Identity API* does not necessarily mean any other +# OpenStack API is version 2.0. For example, your cloud provider may implement +# Image API v1.1, Block Storage API v2, and Compute API v2.0. OS_AUTH_URL is +# only for the Identity API served through keystone. +export OS_AUTH_URL=http://10.0.2.15:5000/v2.0 + +# With the addition of Keystone we have standardized on the term **tenant** +# as the entity that owns the resources. +export OS_TENANT_ID=5e40d18284c34e0fb9cdb6f92f651f71 +export OS_TENANT_NAME="admin" +export OS_PROJECT_NAME="admin" + +# In addition to the owning entity (tenant), OpenStack stores the entity +# performing the action as the **user**. +export OS_USERNAME="admin" + +# With Keystone you pass the keystone password. +#echo "Please enter your OpenStack Password: " +#read -sr OS_PASSWORD_INPUT +#export OS_PASSWORD=$OS_PASSWORD_INPUT +export OS_PASSWORD="password" + +# If your configuration has multiple regions, we set that information here. +# OS_REGION_NAME is optional and only valid in certain environments. +export OS_REGION_NAME="RegionOne" +# Don't leave a blank variable, unset it if it was empty +if [ -z "$OS_REGION_NAME" ]; then unset OS_REGION_NAME; fi diff --git a/openstack_dashboard/don/ovs/analyzer.py b/openstack_dashboard/don/ovs/analyzer.py new file mode 100644 index 0000000..4e84840 --- /dev/null +++ b/openstack_dashboard/don/ovs/analyzer.py @@ -0,0 +1,412 @@ +# +# analyzer.py: +# +# This file implements the following: +# 1. Analysis of the collected info +# 2. Report any problems +# 3. Report what is correct +# +import pprint +import re +import argparse +import subprocess +import json +import os +from itertools import combinations + +from common import settings, debug, get_router +from common import load_json, get_subnet, is_network_public +import yaml + +tick = '✔' +cross = '✘' + + +def get_vm_qrouters(info, vm): + vms = info['vms'] + if not vms.has_key(vm): + return 'unknown' + + # Get IP of any of the VM's interfaces + for ip in vms[vm]['interfaces'].keys(): + break + + routers = [] + subnet = get_subnet(ip) + namespaces = info['namespaces'] + for nms in namespaces.keys(): + if re.search('^qrouter-', nms): + if not namespaces[nms].has_key('interfaces'): + continue + for intf in namespaces[nms]['interfaces'].keys(): + ip = namespaces[nms]['interfaces'][intf] + if re.search(subnet, ip): + routers.append(nms) + return routers + +# Even if there is one qrouter namespace via which all ping tests passed, we +# consider the ping test to be a success. + + +def did_ping_test_pass(cmds): + qrouter_result = True + for qrouter in sorted(cmds.keys()): + debug('Checking ping status in qrouter %s' % qrouter) + qrouter_result = True + for key in cmds[qrouter].keys(): + (src_vm, dst_vm) = key + for key2 in sorted(cmds[qrouter][key].keys()): + (src_ip, dst_ip) = key2 + result = cmds[qrouter][key][key2]['pass'] + if not result: + qrouter_result = False + break # check the next namsepace, this one failed + # if all ping passed via this qrouter, return true + if qrouter_result: + return qrouter_result + + # There was no qrouter via which all pings passed! + return qrouter_result + + +def run_ping_command(cmd, comment=''): + debug('Running ' + comment + ': ' + cmd) + return subprocess.check_output(cmd, + shell=True, + stderr=subprocess.STDOUT, + universal_newlines=True).replace('\t', ' ') + + +def report_file_open(report_file): + f = open(report_file, 'w') + f.write('\n') + f.write('\n') + f.write( + '\n') + f.write( + '\n') + f.write('DON: Analysis Results\n') + f.write('\n') + f.write('\n') + + return f + + +def report_file_close(file_handle): + file_handle.write('\n') + file_handle.write('\n') + + +def print_ping_result(cmds, overall_result, info, comment=None): + lines = [] + lines.append('

Ping Test Results

\n') + if comment: + lines.append('

%s

\n' % comment) + lines.append('\n') + + overall_str = '%s' % cross + if overall_result: + overall_str = '%s' % tick + lines.append('OVERALL RESULT: %s\n' % overall_str) + return lines + + +def get_vm_credentials(config_file='credentials.yaml'): + try: + with open(config_file, 'r') as s: + return yaml.safe_load(s) + except IOError, e: + print '%s :%s' % (e.args[1], config_file) + raise + + +def test_ping(info): + debug('Running ping test') + vms = info['vms'] + vm_credentials = get_vm_credentials() + for vm in sorted(vms.keys()): + vms[vm]['qrouter'] = get_vm_qrouters(info, vm) + + vm_pairs = list(combinations(sorted(vms.keys()), 2)) + pprint.pprint(vm_pairs) + cmds = {} + for (src_vm, dst_vm) in vm_pairs: + for qrouter in vms[src_vm]['qrouter']: + debug(qrouter) + if not cmds.has_key(qrouter): + cmds[qrouter] = {} + cmds[qrouter][(src_vm, dst_vm)] = {} + for src_ip in vms[src_vm]['interfaces'].keys(): + if is_network_public(src_ip, src_vm, info): + continue + for dst_ip in vms[dst_vm]['interfaces'].keys(): + if is_network_public(dst_ip, dst_vm, info): + continue + username = vm_credentials.get( + src_vm, vm_credentials['default'])['username'] + passwd = vm_credentials.get(src_vm, vm_credentials[ + 'default'])['password'] + cmd = 'sudo ip netns exec ' + qrouter + cmd += ' python ping.py --src_ip %s --dst_ip %s --username "%s" --passwd "%s" --count %d --timeout %d' % \ + (src_ip, dst_ip, username, passwd, 1, 2) + + comment = 'Ping [%s (%s) => %s (%s)]' % ( + src_vm, src_ip, dst_vm, dst_ip) + output = run_ping_command(cmd, comment=comment) + a = json.loads(output) + success = a['pass'] + cmds[qrouter][(src_vm, dst_vm)][(src_ip, dst_ip)] = { + 'cmd': cmd, + 'output': output, + 'pass': success + } + + debug(pprint.pformat(cmds)) + ping_test_passed = did_ping_test_pass(cmds) + + return (ping_test_passed, cmds) + + +def run_ovs_command(cmd, comment=''): + debug('Running ' + comment + ': ' + cmd) + return subprocess.check_output(cmd, + shell=True, + stderr=subprocess.STDOUT, + universal_newlines=True).replace('\t', ' ') + + +def process_ovs_output(output): + for line in output: + if re.search('PASS', line): + return True + return False + + +def print_ovs_result(cmds, overall_result, info, comment=None): + lines = [] + lines.append('

OVS Test Results

\n') + if comment: + lines.append('

%s

\n' % comment) + lines.append('\n') + + overall_str = '%s' % cross + if overall_result: + overall_str = '%s' % tick + lines.append('OVERALL RESULT: %s\n' % overall_str) + return lines + + +def test_ovs(info): + debug('Running OVS test') + ovs_test_passed = True + cmds = {} + + # first construct a dictionary of tag to br-int ports + br_int_ports = info['bridges']['br-int']['ports'] + + tag_to_port = {} + for port in br_int_ports.keys(): + if not re.search('^qvo', port): + continue + tag = br_int_ports[port]['tag'] + port_id = br_int_ports[port]['id'] + if not tag_to_port.has_key(tag): + tag_to_port[tag] = [] + tag_to_port[tag].append((port_id, port)) + + debug(pprint.pformat(tag_to_port)) + for tag in sorted(tag_to_port.keys(), key=lambda x: int(x)): + cmds[tag] = {} + port_count = len(tag_to_port[tag]) + if port_count < 2: + debug('tag %s is used by single port %s. Skipping test!' % + (tag, tag_to_port[tag][0])) + continue + + port_list = list(map(lambda (x, y): x, tag_to_port[tag])) + sorted_port_list = sorted(port_list, key=lambda x: int(x)) + port_pairs = list(combinations(sorted_port_list, 2)) + + for (src_port, dst_port) in port_pairs: + cmds[tag][(src_port, dst_port)] = {} + + cmd = '' + cmd += 'python ovs.py --src_port_id %s --dst_port_id %s --tag %s --ovs_bridge br-int' % \ + (src_port, dst_port, tag) + comment = 'ovs [tag: %s port %s => %s' % (tag, src_port, dst_port) + output = run_ovs_command(cmd, comment=comment) + success = process_ovs_output(output) + if not success: + ovs_test_passed = False + + cmds[tag][(src_port, dst_port)] = { + 'cmd': cmd, + 'output': output, + 'pass': success + } + + return (ovs_test_passed, cmds) + + +# Dictionary of tests +test_suite = { + 'ping': { + 'help': 'Ping test between all pairs of VMs', + 'func': test_ping, + 'result': 'not run', + 'formatter': print_ping_result, + 'html': None, + }, + 'ovs': { + 'help': 'OVS test between all pairs of ports using the same tag in br-int', + 'func': test_ovs, + 'result': 'not run', + 'formatter': print_ovs_result, + 'html': None, + }, +} + + +def analyze(json_filename, params): + settings['debug'] = True + BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + CUR_DIR = os.getcwd() + os.chdir(BASE_DIR + '/ovs') + # NEW_DIR = os.getcwd() + # return BASE_DIR + ':' + CUR_DIR + ':' + NEW_DIR + debug('This is what I am going to analyze') + my_info = load_json(json_filename) + + for test in test_suite.keys(): + flag = 'test:' + test + if params[flag] or params['test:all']: + (result, cmds) = test_suite[test]['func'](my_info) + if result: + test_suite[test]['result'] = 'PASS' + else: + test_suite[test]['result'] = 'FAIL' + lines = test_suite[test]['formatter'](cmds, + result, + my_info, + test_suite[test]['help']) + test_suite[test]['html'] = lines + + debug(params['test:report_file']) + f = report_file_open(params['test:report_file']) + for test in test_suite.keys(): + if test_suite[test]['html']: + for line in test_suite[test]['html']: + f.write(line) + report_file_close(f) + os.chdir(CUR_DIR) + + +def check_args(): + parser = argparse.ArgumentParser(description='Static analysis of output of commands', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--debug', dest='debug', + help='Enable debugging', + default=True, action='store_true') + parser.add_argument('--info_file', dest='info_file', + help='Info is read in JSON format in this file', + default="don.json", type=str) + parser.add_argument('--ping', dest='ping', + help='ping test between all VMs', default=False, + action='store_true') + parser.add_argument('--ping_count', dest='ping_count', + help='how many ping packets to send', + default=2, type=int) + parser.add_argument('--ping_timeout', dest='ping_timeout', + help='ping timeout period in seconds', + default=2, type=int) + parser.add_argument('--ovs', dest='ovs', + help='ovs test between ports using same tag in br-int', + default=False, action='store_true') + parser.add_argument('--test_all', dest='test_all', + help='Perform all tests in test suite', + default=False, action='store_true') + parser.add_argument('--error_file', dest='error_file', + help='All errors will be reported to this file', + type=str, default='don.error.txt') + parser.add_argument('--report_file', dest='report_file', + help='Report will be written in this file in HTML format', + type=str, default='don.report.html') + args = parser.parse_args() + + settings['debug'] = args.debug + settings['info_file'] = args.info_file + settings['error_file'] = args.error_file + settings['test:all'] = args.test_all + settings['test:ping'] = args.test_all or args.ping + settings['test:ping_count'] = args.ping_count + settings['test:ping_timeout'] = args.ping_timeout + settings['test:ovs'] = args.test_all or args.ovs + settings['test:report_file'] = args.report_file + + +def main(): + check_args() + analyze(settings['info_file'], settings) + pprint.pprint(test_suite) + +if __name__ == "__main__": + main() diff --git a/openstack_dashboard/don/ovs/collector.py b/openstack_dashboard/don/ovs/collector.py new file mode 100644 index 0000000..fb60ba3 --- /dev/null +++ b/openstack_dashboard/don/ovs/collector.py @@ -0,0 +1,965 @@ +# +# This file runs a whole bunch of commands (on the shell), parses their outputs +# and constructs a dictionary of extracted information. +# +import pprint +import re +import argparse +import os +from common import settings, debug, error, status_update, dump_json +from common import execute_cmd, connect_to_box, get_vm_credentials +import ConfigParser +# from analyzer import analyze + +don_config = ConfigParser.ConfigParser() +try: + don_config.read('/etc/don/don.conf') +except Exception, e: + print e +deployment_type = don_config.get('DEFAULT', 'deployment_type') + + +def get_env(filename): + try: + lines = open(os.getcwd() + os.sep + filename, 'r').read().splitlines() + except IOError, e: + print "%s :%s" % (e.args[1], filename) + raise + env = {} + for line in lines: + if line.startswith('export'): + m = re.search(r'export (.+)=(.+)', line) + if m: + key = m.group(1).replace('"', '') + val = m.group(2).replace('"', '') + env.update({key: val}) + return env + +myenv = os.environ.copy() +myenv.update(get_env('admin-openrc.sh')) + +# Contains all info gathered by parsing the output of commands +info = { + 'vms': {}, + 'brctl': {}, + 'bridges': { + 'br-ex': {'ports': {}}, + 'br-int': {'ports': {}}, + 'br-tun': {'ports': {}} + }, + 'floating_ips': {}, +} + + +def add_new_command(cmd_dict, cmd_key, cmd): + if cmd_dict.has_key(cmd_key): + error(cmd_key + ' already exists in command dictionary') + return + cmd_dict[cmd_key] = cmd + + +def record_linuxbridge(bridge, interface_list): + brctl_dict = info['brctl'] + if brctl_dict.has_key(bridge): + error('Bridge ' + bridge + ' repeated! Overwriting!') + brctl_dict[bridge] = {'interfaces': interface_list} + + +def get_bridge_entry(br): + bridge_dict = info['bridges'] + if not bridge_dict.has_key(br): + error('Bridge ' + br + ' does not exist! Supported bridges: ' + + str(bridge_dict.keys())) + return None + return bridge_dict.get(br) + + +# +# Parser functions (for each command). Each function has the sample input +# as a comment above it. +# +''' + 31b1cfcc-ca85-48a9-a84a-8b222d377080 + VM1 + + f9743f1c-caeb-4892-af83-9dc0ac757545 + VM2 + +''' + + +def cat_instance_parser(parse_this): + vm_dict = info['vms'] + + uuid = None + name = None + src_bridge = None + for line in parse_this: + m = re.search('(\S+)', line) + if m: + uuid = m.group(1) + continue + m = re.search('(\S+)', line) + if m: + name = m.group(1) + continue + m = re.search('', line) + if m: + src_bridge = m.group(1) + + if not vm_dict.has_key(name): + vm_dict[name] = {} + + vm_entry = vm_dict[name] + vm_entry['uuid'] = uuid + if not vm_entry.has_key('src_bridge'): + vm_entry['src_bridge'] = [] + vm_entry['tap_dev'] = [] + vm_entry['src_bridge'].append(src_bridge) + vm_entry['tap_dev'].append(src_bridge.replace('qbr', 'tap')) + + +''' +bridge name bridge id STP enabled interfaces +qbr6ce314cb-a5 8000.9255d5550cf8 no qvb6ce314cb-a5 + tap6ce314cb-a5 +qbrb0f5cfc8-4d 8000.b2277f2c981b no qvbb0f5cfc8-4d + tapb0f5cfc8-4d +virbr0 8000.000000000000 yes +''' + + +def brctl_show_parser(parse_this): + interfaces = [] + bridge = None + for line in parse_this: + m = re.search('(qbr\S+)\s+\S+\s+\S+\s+(\S+)', line) + if m: + # We already have a bridge, that means we are now lookign at the + # next bridge + if bridge: + record_linuxbridge(bridge, interfaces) + interfaces = [] + bridge = m.group(1) + interfaces.append(m.group(2)) + continue + m = re.search('^\s+(\S+)', line) + if m: + interfaces.append(m.group(1)) + + # handle the last bridge + if bridge: + record_linuxbridge(bridge, interfaces) + +''' +ubuntu@ubuntu-VirtualBox:~/don$ sudo ovs-vsctl show +0fc4d93f-28e0-408a-8edb-21d5ec76b2c3 + Bridge br-tun + fail_mode: secure + Port patch-int + Interface patch-int + type: patch + options: {peer=patch-tun} + Port br-tun + Interface br-tun + type: internal + Bridge br-int + fail_mode: secure + Port "tap3b74b285-71" + tag: 2 + Interface "tap3b74b285-71" + type: internal + Port patch-tun + Interface patch-tun + type: patch + options: {peer=patch-int} + Port "qvob0f5cfc8-4d" + tag: 2 + Interface "qvob0f5cfc8-4d" + Port "qr-77ce7d4c-d5" + tag: 1 + Interface "qr-77ce7d4c-d5" + type: internal + Port "qr-56cf8a2d-27" + tag: 2 + Interface "qr-56cf8a2d-27" + type: internal + Port "qvo6ce314cb-a5" + tag: 2 + Interface "qvo6ce314cb-a5" + Port br-int + Interface br-int + type: internal + Port "tap9d44135a-45" + tag: 1 + Interface "tap9d44135a-45" + type: internal + Bridge br-ex + Port "qg-2909632b-b8" + Interface "qg-2909632b-b8" + type: internal + Port br-ex + Interface br-ex + type: internal + Port "qg-e2fb759b-60" + Interface "qg-e2fb759b-60" + type: internal + ovs_version: "2.0.2" +''' + + +def ovs_vsctl_show_parser(parse_this): + bridge = None + bridge_dict = info['bridges'] + for line in parse_this: + m = re.search('Bridge\s+(br-\S+)', line) + if m: + bridge = str(m.group(1)) + if not bridge_dict.has_key(bridge): + error( + 'Skipping bridge [' + bridge + ']! Supported bridges: ' + + str(bridge_dict.keys())) + bridge = None + continue + bridge_entry = bridge_dict.get(bridge) + if bridge: + m = re.search('fail_mode: (\S+)', line) + if m: + bridge_entry['fail_mode'] = m.group(1) + continue + m = re.search('Port (\S+)', line) + if m: + # the port names seem to have double quotes around them! + port = m.group(1).replace('"', '') + if not bridge_entry['ports'].has_key(port): + bridge_entry['ports'][port] = {} + port_entry = bridge_entry['ports'][port] + continue + m = re.search('tag: (\d+)', line) + if m: + port_entry['tag'] = m.group(1) + continue + m = re.search('Interface (\S+)', line) + if m: + # the interface names seem to have double quotes around them! + interface = m.group(1).replace('"', '') + if not port_entry.has_key('interfaces'): + port_entry['interfaces'] = {} + port_entry['interfaces'][interface] = {} + interface_entry = port_entry['interfaces'][interface] + continue + m = re.search('type: (\S+)', line) + if m: + interface_entry['type'] = m.group(1) + continue + m = re.search('options: {(\S+)}', line) + if m: + options = m.group(1) + interface_entry['options'] = options + continue + +''' +OFPT_FEATURES_REPLY (xid=0x2): dpid:00008207ee8eee4d +n_tables:254, n_buffers:256 +capabilities: FLOW_STATS TABLE_STATS PORT_STATS QUEUE_STATS ARP_MATCH_IP +actions: OUTPUT SET_VLAN_VID SET_VLAN_PCP STRIP_VLAN SET_DL_SRC SET_DL_DST \ +SET_NW_SRC SET_NW_DST SET_NW_TOS SET_TP_SRC SET_TP_DST ENQUEUE + 4(patch-tun): addr:e2:ce:31:60:94:e0 + config: 0 + state: 0 + speed: 0 Mbps now, 0 Mbps max + 5(tap9d44135a-45): addr:00:00:00:00:00:00 + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + 6(qr-77ce7d4c-d5): addr:00:00:00:00:00:00 + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + 7(tap3b74b285-71): addr:00:00:00:00:00:00 + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + 8(qr-56cf8a2d-27): addr:00:00:00:00:00:00 + config: PORT_DOWN + state: LINK_DOWN + speed: 0 Mbps now, 0 Mbps max + 9(qvob0f5cfc8-4d): addr:7a:82:4f:4e:a0:ab + config: 0 + state: 0 + current: 10GB-FD COPPER + speed: 10000 Mbps now, 0 Mbps max + 10(qvo6ce314cb-a5): addr:42:92:2a:95:28:ed + config: 0 + state: 0 + current: 10GB-FD COPPER + speed: 10000 Mbps now, 0 Mbps max + LOCAL(br-int): addr:82:07:ee:8e:ee:4d + config: 0 + state: 0 + speed: 0 Mbps now, 0 Mbps max +OFPT_GET_CONFIG_REPLY (xid=0x4): frags=normal miss_send_len=0 +''' + + +def ovs_ofctl_show_br_parser(bridge, parse_this): + bridge_dict = info['bridges'] + if not bridge_dict.has_key(bridge): + error('Skipping bridge [' + bridge + + ']! Supported bridges: ' + str(bridge_dict.keys())) + return + bridge_entry = bridge_dict.get(bridge) + pprint.pprint(bridge_entry) + + for line in parse_this: + m = re.search('(\d+)\((\S+)\):\s+addr:(\S+)', line) + if m: + port_id = m.group(1) + port = m.group(2) + port_mac = m.group(3) + if not bridge_entry['ports'].has_key(port): + bridge_entry['ports'][port] = {} + port_entry = bridge_entry['ports'][port] + port_entry['id'] = port_id + port_entry['mac'] = port_mac + continue + + m = re.search('(\w+)\((\S+)\):\s+addr:(\S+)', line) + if m: + port_id = m.group(1) + port = m.group(2) + port_mac = m.group(3) + if not bridge_entry['ports'].has_key(port): + bridge_entry['ports'][port] = {} + port_entry = bridge_entry['ports'][port] + port_entry['id'] = port_id + port_entry['mac'] = port_mac + + pass + +# These three are all wrappers for each of the three bridges + + +def ovs_ofctl_show_br_int_parser(parse_this): + ovs_ofctl_show_br_parser('br-int', parse_this) + + +def ovs_ofctl_show_br_ex_parser(parse_this): + ovs_ofctl_show_br_parser('br-ex', parse_this) + + +def ovs_ofctl_show_br_tun_parser(parse_this): + ovs_ofctl_show_br_parser('br-tun', parse_this) + +''' ++--------------------------------------+-------+--------+------------+-------------+--------------------------------------------------------+ +| ID | Name | Status | Task State | Power State | Networks | ++--------------------------------------+-------+--------+------------+-------------+--------------------------------------------------------+ +| 31b1cfcc-ca85-48a9-a84a-8b222d377080 | VM1 | ACTIVE | - | Running | private=10.0.2.3 | +| f9743f1c-caeb-4892-af83-9dc0ac757545 | VM2 | ACTIVE | - | Running | private=10.0.2.4 | +| 83b547b9-9578-4840-997a-5aa1c4e829b0 | VM3-1 | ACTIVE | - | Running | private2=10.0.3.3 | +| 17b4685e-5cbe-4dd1-862a-6f89c191e1e7 | VM3-2 | ACTIVE | - | Running | private2=10.0.3.4 | +| ee4952a3-0700-42ea-aab3-7503bc9d87e2 | VM4 | ACTIVE | - | Running | private2=10.0.3.5; public=172.24.4.4; private=10.0.2.5 | ++--------------------------------------+-------+--------+------------+-------------+--------------------------------------------------------+ +''' + + +def nova_list_parser(parse_this): + vm_dict = info['vms'] + + for line in parse_this: + if re.search('^\+', line) or re.search('^$', line) or \ + re.search('Networks', line): + continue + parts = line.split('|') + parts = [x.strip() for x in parts] + + vm = parts[2] + networks = parts[6].split(';') + networks = [x.strip() for x in networks] + + if not vm_dict.has_key(vm): + vm_dict[vm] = {'interfaces': {}} + + for entry in networks: + # excluding ipv6 ip + if len(entry.split(',')) > 1: + network = entry.split('=')[0] + ip = filter(lambda a: re.search("(\d+\.\d+\.\d+\.\d+)", a) is not + None, entry.split('=')[1].split(','))[0].strip() + else: + (network, ip) = entry.split(',')[0].split('=') + vm_dict[vm]['interfaces'][ip] = {'network': network} + + pass + + +''' ++--------------------------------------+------+-------------------+-----------------------------------------------------------------------------------+ +| id | name | mac_address | fixed_ips | ++--------------------------------------+------+-------------------+-----------------------------------------------------------------------------------+ +| 1dd820b1-98bd-4f39-b1ab-e89ecc67ae43 | | fa:16:3e:0f:36:26 | {"subnet_id": "75ae4ce8-495d-4f53-93d1-bf98e55d6658", "ip_address": "172.24.4.4"} | +| 1f73af79-fa69-4433-bcab-16d7a0bc2607 | | fa:16:3e:dc:c8:de | {"subnet_id": "dbc9717f-5a08-48bb-92e2-ed2da443541b", "ip_address": "10.0.3.1"} | +| 2909632b-b8a3-436b-aabd-9868d0c1051e | | fa:16:3e:af:95:a9 | {"subnet_id": "75ae4ce8-495d-4f53-93d1-bf98e55d6658", "ip_address": "172.24.4.2"} | +| 3b74b285-71d0-4311-8a69-2b032eebbe13 | | fa:16:3e:70:09:45 | {"subnet_id": "1083b740-45ce-49be-b603-73cbc26af5d7", "ip_address": "10.0.2.2"} | +| 56cf8a2d-27b7-4eab-a334-349c70520868 | | fa:16:3e:8a:ce:cb | {"subnet_id": "1083b740-45ce-49be-b603-73cbc26af5d7", "ip_address": "10.0.2.1"} | +| 6ce314cb-a599-4af8-8187-bdb0bfa88809 | | fa:16:3e:83:b1:60 | {"subnet_id": "1083b740-45ce-49be-b603-73cbc26af5d7", "ip_address": "10.0.2.4"} | +| 77ce7d4c-d5b9-4669-b23c-b0d9ee5f58c8 | | fa:16:3e:a6:de:15 | {"subnet_id": "531f1674-2b46-4ad7-9d73-4c41d215cc99", "ip_address": "10.0.0.1"} | +| 9c34adc0-c655-4b00-89ba-ca65def56fe0 | | fa:16:3e:a1:e7:f5 | {"subnet_id": "dbc9717f-5a08-48bb-92e2-ed2da443541b", "ip_address": "10.0.3.4"} | +| 9d44135a-4551-4448-9c80-d211b023c3eb | | fa:16:3e:80:83:c9 | {"subnet_id": "531f1674-2b46-4ad7-9d73-4c41d215cc99", "ip_address": "10.0.0.2"} | +| b0f5cfc8-4da0-42ad-8c18-6f29870bfb2a | | fa:16:3e:ae:a2:17 | {"subnet_id": "1083b740-45ce-49be-b603-73cbc26af5d7", "ip_address": "10.0.2.3"} | +| c03437a8-8a44-4615-b160-e1ef227d63c5 | | fa:16:3e:7f:b6:a5 | {"subnet_id": "dbc9717f-5a08-48bb-92e2-ed2da443541b", "ip_address": "10.0.3.5"} | +| cb7d8a29-8140-4ed0-a1c7-03cbf0be0c5b | | fa:16:3e:33:ee:b1 | {"subnet_id": "1083b740-45ce-49be-b603-73cbc26af5d7", "ip_address": "10.0.2.5"} | +| e2fb759b-602a-4fcd-8674-e8f5fe297dbc | | fa:16:3e:ea:47:b5 | {"subnet_id": "75ae4ce8-495d-4f53-93d1-bf98e55d6658", "ip_address": "172.24.4.3"} | +| e4f25d71-5684-4ccc-8114-2465a84ecc58 | | fa:16:3e:90:c7:d3 | {"subnet_id": "dbc9717f-5a08-48bb-92e2-ed2da443541b", "ip_address": "10.0.3.2"} | +| f57aa80e-2ef3-4031-a0a4-bc12d2445687 | | fa:16:3e:2e:6e:91 | {"subnet_id": "dbc9717f-5a08-48bb-92e2-ed2da443541b", "ip_address": "10.0.3.3"} | ++--------------------------------------+------+-------------------+-----------------------------------------------------------------------------------+ +''' + + +def neutron_port_list_parser(parse_this): + tap_to_ip = {} + + for line in parse_this: + if re.search('^\+', line) or re.search('^$', line) or \ + re.search('fixed_ips', line): + continue + + parts = line.split('|') + parts = [x.strip() for x in parts] + + tap = parts[1][:11] + # ip = parts[4].split(':')[-1].replace('}', '') + m = re.search('"ip_address": "(\S+)"', parts[4]) + if m: + ip = m.group(1) + tap_to_ip[tap] = ip + + info['tap_to_ip'] = tap_to_ip + pass + +''' ++--------------------------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------+ +| id | name | external_gateway_info | distributed | ha | ++--------------------------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------+ +| 8c981cdb-c19f-47c1-8149-f85a506c486c | router1 | {"network_id": "640ece56-c6dc-4868-8e7a-12547508098a", "enable_snat": true, "external_fixed_ips": [{"subnet_id": "75ae4ce8-495d-4f53-93d1-bf98e55d6658", "ip_address": "172.24.4.2"}]} | False | False | +| ac41aab2-f9c3-4a06-8eef-f909ee1e6e50 | router | {"network_id": "640ece56-c6dc-4868-8e7a-12547508098a", "enable_snat": true, "external_fixed_ips": [{"subnet_id": "75ae4ce8-495d-4f53-93d1-bf98e55d6658", "ip_address": "172.24.4.3"}]} | False | False | ++--------------------------------------+---------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------+-------+ +''' + + +def neutron_router_list_parser(parse_this): + routers = {} + + for line in parse_this: + if re.search('^\+', line) or re.search('^$', line) or \ + re.search('external_gateway_info', line): + continue + + parts = line.split('|') + parts = [x.strip() for x in parts] + + router_id = parts[1] + name = parts[2] + + network_id = 'unknown' + m = re.search('"network_id":\s+"(\S+)"', parts[3]) + if m: + network_id = m.group(1) + + ip_address = 'x.x.x.x' + m = re.search('"ip_address":\s+"(\d+\.\d+\.\d+\.\d+)"', parts[3]) + if m: + ip_address = m.group(1) + + routers[name] = {'id': router_id, + 'ip_address': ip_address, + 'network_id': network_id, + } + + info['routers'] = routers + + # now add some more commands to get further information for + # l3-agents which run in different namespaces + for router in info['routers'].keys(): + uuid = info['routers'][router]['id'] + namespace = 'qrouter-' + uuid + + cmd_key = 'netns_' + namespace + cmd = { + 'cmd': 'echo namespace: ' + namespace + '; echo "sudo ip netns exec ' + namespace + ' ip a" > /tmp/don.bash; bash /tmp/don.bash', + 'help': 'Collect namespace info for l3-agent', + 'shell': True, + 'output': None, + 'order': 100, + 'parser': ip_namespace_qrouter_parser, + } + add_new_command(commands, cmd_key, cmd) + pass + + +def ip_namespace_qrouter_parser(parse_this): + nm_dict = info['namespaces'] + + qr_intf = None + qg_intf = None + ip = None + for line in parse_this: + if re.search('^\+', line) or re.search('^$', line): + continue + m = re.search('^namespace: (\S+)', line) + if m: + namespace = m.group(1) + continue + + m = re.search('^\d+: (qr-\S+):', line) + if m: + qr_intf = m.group(1) + continue + + m = re.search('^\d+: (qg-\S+):', line) + if m: + qg_intf = m.group(1) + continue + + m = re.search('inet (\d+\.\d+\.\d+\.\d+/\d+)', line) + if m: + ip = m.group(1) + + if not nm_dict[namespace].has_key('interfaces'): + nm_dict[namespace] = {'interfaces': {}} + + if qg_intf: + nm_dict[namespace]['interfaces'][qg_intf] = ip + elif qr_intf: + nm_dict[namespace]['interfaces'][qr_intf] = ip + else: + continue + + qr_intf = None + qg_intf = None + ip = None + pass + + +def ip_namespace_qdhcp_parser(parse_this): + nm_dict = info['namespaces'] + + tap_intf = None + ip = None + for line in parse_this: + if re.search('^\+', line) or re.search('^$', line): + continue + m = re.search('^namespace: (\S+)', line) + if m: + namespace = m.group(1) + continue + + m = re.search('^\d+: (tap\S+):', line) + if m: + tap_intf = m.group(1) + + m = re.search('inet (\d+\.\d+\.\d+\.\d+/\d+)', line) + if m: + ip = m.group(1) + + if not nm_dict[namespace].has_key('interfaces'): + nm_dict[namespace] = {'interfaces': {}} + + if tap_intf: + nm_dict[namespace]['interfaces'][tap_intf] = ip + + tap_intf = None + ip = None + pass + + +''' ++--------------------------------------+----------+----------------------------------------------------------+ +| id | name | subnets | ++--------------------------------------+----------+----------------------------------------------------------+ +| 0a355cf0-00d0-45e1-9a3a-9aca436510d5 | private2 | 8393a2da-09dd-46e8-a26f-caf9f12c48f5 10.0.3.0/24 | +| 3b4ddfcb-49b8-46ae-9ecd-cb4f9b1830fc | public | 2dd78cb6-eb90-44ea-82b0-bbdb7316edb2 172.24.4.0/24 | +| | | 304ce342-18fe-4b4a-aa49-f5c7e5e31b2a 2001:db8::/64 | +| 4b7a42e8-cc16-411c-b932-989106c2f934 | private1 | cc580da4-0b61-4982-ae7b-d2d5c441b1d7 10.0.2.0/24 | +| bfedebe8-c436-4056-8d12-1d2f7e62e8ec | private | 4deed2ad-e184-43a9-8cc7-4493aa07f78f fdfd:57f1:b2ba::/64 | +| | | 8e2c5cfd-fbc1-4fe0-9f5e-f0b0dc070fb8 10.0.0.0/24 | ++--------------------------------------+----------+----------------------------------------------------------+ +''' + + +def neutron_net_list_parser(parse_this): + networks = {} + + ip = 'unknown' + for line in parse_this: + if re.search('^\+', line) or re.search('^$', line) or re.search('subnets', line): + continue + + # Skip IPv6 for the time being + m = re.search('^\| (\S+) \| (\S+)\s+\| \S+ (\S+)', line) + if m: + network_id = m.group(1) + name = m.group(2) + possible_ip = m.group(3) + if re.search('\.', possible_ip): + ip = possible_ip + networks[network_id] = {'name': name, + 'ip': ip + } + m = re.search('^\|\s+\|\s+\| \S+ (\S+)', line) + if m: + possible_ip = m.group(1) + if re.search('\.', possible_ip): + ip = possible_ip + networks[network_id] = {'name': name, + 'ip': ip + } + ip = 'Unknown' + + info['networks'] = networks + + # now add some more commands to get further information for + # dhcp agents which run in different namespaces + for network_id in networks.keys(): + # There is no dhcp agent run for public network + if networks[network_id]['name'] == 'public': + continue + + namespace = 'qdhcp-' + network_id + + cmd_key = 'netns_' + namespace + cmd = { + 'cmd': 'echo namespace: ' + namespace + '; echo "sudo ip netns exec ' + namespace + ' ip a" > /tmp/don.bash; bash /tmp/don.bash', + 'help': 'Collect namespace info for dhcp-agent', + 'shell': True, + 'output': None, + 'order': 110, + 'parser': ip_namespace_qdhcp_parser, + } + add_new_command(commands, cmd_key, cmd) + pass + + +''' +qdhcp-d5357ad8-df8b-4f19-8433-9db13304e4b2 +qrouter-ac41aab2-f9c3-4a06-8eef-f909ee1e6e50 +qdhcp-49be53de-33ed-480a-a06e-6e77c8f887dc +qrouter-8c981cdb-c19f-47c1-8149-f85a506c486c +qdhcp-82b0e328-4530-495e-a43f-238ef7a53d62 +''' + + +def ip_netns_parser(parse_this): + namespaces = {} + + for line in parse_this: + if re.search('^q', line): + namespaces[line] = {} + + info['namespaces'] = namespaces + + +def dummy_parser(parse_this): + debug('Dummy Parser :-)') + pass + + +def floating_ip_list_parser(parse_this): + floating_ips = {} + for line in parse_this: + if re.search('^\+', line) or re.search('^$', line) or re.search('Pool', line): + continue + parts = line.split('|') + parts = [x.strip() for x in parts] + floating_ip = parts[2] + vm_id = parts[3] + pool = parts[5] + # ignore floating ips which is not assigned to any vms + if vm_id != '-': + floating_ips.update( + {vm_id: {'floating_ip': floating_ip, 'pool': pool}}) + info['floating_ips'] = floating_ips + + +# static commands whose output have info that help us diagnose +commands = { + 'nova_list': + { + 'cmd': ['nova', 'list'], + 'help': 'Collect list of VMs from nova', + 'env': True, + 'output': None, + 'order': 1, + 'parser': nova_list_parser, + }, + 'cat_instance': + { + 'cmd': 'cat /etc/libvirt/qemu/instance-*.xml | egrep -e "" -e "nova:name" -e "source bridge"', + 'help': 'Collect some info from the launched VMs', + 'sudo': True, + 'shell': True, + 'output': None, + 'order': 2, + 'parser': cat_instance_parser, + }, + 'neutron_port_list': + { + 'cmd': ['neutron', 'port-list'], + 'help': 'Collect neutron configured ports', + 'env': True, + 'output': None, + 'order': 3, + 'parser': neutron_port_list_parser, + }, + 'neutron_router_list': + { + 'cmd': ['neutron', 'router-list'], + 'help': 'Collect neutron configured routers', + 'env': True, + 'output': None, + 'order': 4, + 'parser': neutron_router_list_parser, + }, + 'neutron_net_list': + { + 'cmd': ['neutron', 'net-list'], + 'help': 'Collect neutron configured networks', + 'env': True, + 'output': None, + 'order': 5, + 'parser': neutron_net_list_parser, + }, + 'ip_netns': + { + 'cmd': ['ip', 'netns'], + 'help': 'Collect network namespaces', + 'output': None, + 'order': 6, + 'parser': ip_netns_parser, + }, + + 'brctl_show': + { + 'cmd': ['brctl', 'show'], + 'help': 'Collect information about bridges (linuxbridge) configured', + 'output': None, + 'order': 10, + 'parser': brctl_show_parser, + }, + 'ovs_appctl_fdb_show_br_ex': + { + 'cmd': ['ovs-appctl', 'fdb/show', 'br-ex'], + 'help': 'Collect mac data base for bridge br-ex', + 'sudo': True, + 'output': None, + 'order': 20, + 'parser': None, + }, + 'ovs_appctl_fdb_show_br_int': + { + 'cmd': ['ovs-appctl', 'fdb/show', 'br-int'], + 'help': 'Collect mac data base for ovs bridge br-int', + 'sudo': True, + 'output': None, + 'order': 21, + 'parser': None, + }, + 'ovs_appctl_fdb_show_br_tun': + { + 'cmd': ['ovs-appctl', 'fdb/show', 'br-tun'], + 'help': 'Collect mac data base for ovs bridge br-tun', + 'sudo': True, + 'output': None, + 'order': 22, + 'parser': None, + }, + 'ovs_vsctl_show': + { + 'cmd': ['ovs-vsctl', 'show'], + 'help': 'Collect ovs bridge info', + 'sudo': True, + 'output': None, + 'order': 30, + 'parser': ovs_vsctl_show_parser, + }, + 'ovs_ofctl_show_br_ex': + { + 'cmd': ['ovs-ofctl', 'show', 'br-ex'], + 'help': 'Collect openflow information for ovs bridge br-ex', + 'sudo': True, + 'output': None, + 'order': 40, + 'parser': ovs_ofctl_show_br_ex_parser, + }, + 'ovs_ofctl_show_br_int': + { + 'cmd': ['ovs-ofctl', 'show', 'br-int'], + 'help': 'Collect openflow information for ovs bridge br-int', + 'sudo': True, + 'output': None, + 'order': 41, + 'parser': ovs_ofctl_show_br_int_parser, + }, + 'ovs_ofctl_show_br_tun': + { + 'cmd': ['ovs-ofctl', 'show', 'br-tun'], + 'help': 'Collect openflow information for ovs bridge br-tun', + 'sudo': True, + 'output': None, + 'order': 42, + 'parser': ovs_ofctl_show_br_tun_parser, + }, + 'ovs_ofctl_dump_flows_br_ex': + { + 'cmd': ['ovs-ofctl', 'dump-flows', 'br-ex'], + 'help': 'Collect openflow flow table information for ovs bridge br-ex', + 'sudo': True, + 'output': None, + 'order': 50, + 'parser': None, + }, + 'ovs_ofctl_dump_flows_br_int': + { + 'cmd': ['ovs-ofctl', 'dump-flows', 'br-int'], + 'help': 'Collect openflow flow table information for ovs bridge br-int', + 'sudo': True, + 'output': None, + 'order': 51, + 'parser': None, + }, + 'ovs_ofctl_dump_flows_br_tun': + { + 'cmd': ['ovs-ofctl', 'dump-flows', 'br-tun'], + 'help': 'Collect openflow flow table information for ovs bridge br-tun', + 'sudo': True, + 'output': None, + 'order': 52, + 'parser': None, + }, + 'instance_floating_ip_list': + { + 'cmd': ['nova', 'floating-ip-list'], + 'help': 'Collect floating ip information for instances', + 'env': True, + 'output': None, + 'order': 53, + 'parser': floating_ip_list_parser, + }, + +} + + +def check_args(): + parser = argparse.ArgumentParser(description='Runs commands, collects, and parses output', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--debug', dest='debug', help='Enable debugging', + default=True, action='store_true') + parser.add_argument('--info_file', dest='info_file', + help='Info will be stored in JSON format in this file', + default="don.json", type=str) + args = parser.parse_args() + + settings['debug'] = args.debug + settings['info_file'] = args.info_file + + +def all_commands_executed(commands): + for cmd in commands.keys(): + if commands[cmd]['parser']: + done = commands[cmd].get('done', False) + if done is False: + return False + return True + + +def get_vm_info_from_compute(cmd): + output = execute_cmd(['nova', 'hypervisor-list'], + sudo=False, shell=False, env=myenv).split('\n') + compute_list = get_hypervisor(output) + vm_info = [] + compute_creds = get_vm_credentials() + for node in compute_list: + creds = compute_creds.get('hypervisor').get( + node, compute_creds.get('hypervisor')['default']) + ssh = connect_to_box(node, creds['username'], creds['password']) + (stdin, out, err) = ssh.exec_command('sudo ' + cmd) + vm_info.extend(out.read().splitlines()) + ssh.close() + return vm_info + + +def exec_on_remote(cmd): + node_details = get_vm_credentials() + creds = node_details.get('network') + # print "sudo "+cmd + ssh = connect_to_box(creds['hostname'], creds[ + 'username'], creds['password']) + (stdin, out, err) = ssh.exec_command(cmd) + if len(err.read()): + return [] + return out.read().splitlines() + + +def get_hypervisor(parse_this): + hypervisor = [] + for line in parse_this: + if re.search('^\+', line) or re.search('^$', line) or re.search('Hypervisor hostname', line): + continue + parts = line.split('|') + parts = [x.strip() for x in parts] + name = parts[2] + hypervisor.append(name) + return hypervisor + + +def main(): + check_args() + + iteration = 0 + # Parser of any specific command might add more commands to be executed. + # Hence continue in a loop. + while True: + if (all_commands_executed(commands) or iteration >= 10): + break + iteration += 1 + status_update('Iteration: ' + str(iteration)) + + sorted_keys = sorted(commands.items(), key=lambda (k, v): v['order']) + for (cmd, dontcare) in sorted_keys: + # Only collect stuff for which we have written a parser + if commands[cmd]['parser']: + if commands[cmd].get('done', False): + continue + if commands[cmd].has_key('help'): + status_update(commands[cmd]['help']) + shell = commands[cmd].get('shell', False) + env = None + if commands[cmd].get('env', False): + env = myenv + sudo = commands[cmd].get('sudo', False) + if deployment_type == 'multinode': + # handling for network node + if cmd.startswith('netns_'): + commands[cmd]['output'] = exec_on_remote( + commands[cmd]['cmd']) + if cmd == 'cat_instance': + commands[cmd]['output'] = get_vm_info_from_compute(commands[ + cmd]['cmd']) + print commands[cmd]['output'] + else: + commands[cmd]['output'] = execute_cmd( + commands[cmd]['cmd'], sudo=sudo, shell=shell, env=env).split('\n') + else: + commands[cmd]['output'] = execute_cmd( + commands[cmd]['cmd'], sudo=sudo, shell=shell, env=env).split('\n') + commands[cmd]['parser'](commands[cmd]['output']) + commands[cmd]['done'] = True + + debug('============= COMMANDS =============') + # debug(pprint.pformat(commands)) + status_update('Writing collected info into ' + settings['info_file']) + dump_json(info, settings['info_file']) + +if __name__ == "__main__": + main() diff --git a/openstack_dashboard/don/ovs/common.py b/openstack_dashboard/don/ovs/common.py new file mode 100644 index 0000000..0dc3f18 --- /dev/null +++ b/openstack_dashboard/don/ovs/common.py @@ -0,0 +1,295 @@ +# common.py: Common functions and data structures used by multiple modules. + +import paramiko +import sys +import re +import pprint +import subprocess +import yaml + +# Program settings +settings = { + 'debug': False, +} + +# Helper functions. + + +def debug(msg): + if settings['debug']: + if not sys.stdout == sys.__stdout__: + tmp = sys.stdout + sys.stdout = sys.__stdout__ + print('DEBUG: ' + msg) + sys.stdout = tmp + else: + print('DEBUG: ' + msg) + + +def error(msg): + if not sys.stdout == sys.__stdout__: + tmp = sys.stdout + sys.stdout = sys.__stdout__ + print('ERROR: ' + msg) + sys.stdout = tmp + else: + print('ERROR: ' + msg) + + +def warning(msg): + if not sys.stdout == sys.__stdout__: + tmp = sys.stdout + sys.stdout = sys.__stdout__ + print('WARNING: ' + msg) + sys.stdout = tmp + else: + print('WARNING: ' + msg) + + +def status_update(msg): + # storing in log file for interactive display on UI + log = open('collector_log.txt', 'w') + if not sys.stdout == sys.__stdout__: + tmp = sys.stdout + sys.stdout = sys.__stdout__ + print('STATUS: ' + msg) + log.write('msg') + sys.stdout = tmp + else: + print('STATUS: ' + msg) + log.write(msg) + + +def dump_json(json_info, json_filename): + import json + try: + outfile = open(json_filename, "w") + except IOError, e: + print e + print 'Couldn\'t open <%s>; Redirecting output to stdout' % json_filename + outfile = sys.stdout + + json.dump(json_info, outfile) + outfile.flush() + outfile.close() + + +def load_json(json_filename): + import json + try: + infile = open(json_filename, "r") + except IOError, e: + print e + print 'Couldn\'t open <%s>; Error!' % json_filename + return None + + tmp = json.load(infile) + infile.close() + return tmp + + +def connect_to_box(server, username, password, timeout=3): + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + ssh.connect(server, username=username, + password=password, timeout=timeout) + except: + return None + return ssh +# def connect_to_box (server, username, password,timeout=3) : +# pass + +# this function i will modify to get data from a file instead of giving +# command directly + + +def ssh_cmd(ssh, cmd): + ssh_stdin, ssh_stdout, ssh_stderr = ssh.exec_command(cmd) + error = ssh_stderr.read() + if len(error): + print 'ERROR: ' + error + output = ssh_stdout.read() + ssh_stdout.flush() + return output + +# TODO (right now assumes subnet mask to be 24 bits long) + + +def get_subnet(ip): + subnet = '.'.join(ip.split('.')[:3]) + return subnet + + +def get_router(namespace, info): + routers = info.get('routers', None) + if not routers: + return 'Unknown' + for router in routers.keys(): + if routers[router]['id'] in namespace: + return router + + return 'Unknown' + +# TODO (guaranteed way of figuring out whether network is private or public) + + +def is_network_public(ip, vm, info): + vm_entry = info['vms'].get(vm) + entry = vm_entry['interfaces'].get(ip, None) + if not entry: + error('Interface: ' + ip + ' does not exist!') + return False + if re.search('public', entry['network']): + return True + return False + + +def get_intf_ip(info, interface): + intf = strip_interface(interface) + return info['tap_to_ip'].get(intf, 'x.x.x.x') + + +def ip_to_intf(info, ip): + for intf, intf_ip in info['tap_to_ip'].iteritems(): + if intf_ip == ip: + return intf + return None + + +def router_to_namespace(info, router): + router_entry = info['routers'].get(router, None) + if not router_entry: + return None + net_id = router_entry.get('id', None) + if not net_id: + return None + return 'qrouter-' + net_id + + +def intf_to_namespace(info, intf): + nms_dict = info['namespaces'] + for nms in nms_dict.keys(): + if nms_dict[nms].has_key('interfaces'): + if nms_dict[nms]['interfaces'].has_key(intf): + return nms + return None + + +def get_ip_network(info, vm, ip): + intf_entry = info['vms'][vm]['interfaces'].get(ip, None) + if not intf_entry: + return 'unknown' + return intf_entry.get('network', 'unknown') + + +def get_vlan_tag(info, interface): + intf = strip_interface(interface) + + intf = 'qvo' + intf + br_int = info['bridges']['br-int'] + debug('Getting vlan tag for ' + intf) + if br_int['ports'].has_key(intf): + return br_int['ports'][intf].get('tag', '0') + return '0' + + +def strip_interface(intf): + x = intf + x = x.replace('tap', '') + x = x.replace('qbr', '') + x = x.replace('qvb', '') + x = x.replace('qvo', '') + return x + + +def get_port_ovs_id_tag(info, vm, port_ip): + for key, ip in info['tap_to_ip'].iteritems(): + if ip == port_ip: + qvo = 'qvo' + key + qvo_entry = info['bridges']['br-int']['ports'].get(qvo, None) + if not qvo_entry: + return None + ovs_id = qvo_entry.get('id', None) + ovs_tag = qvo_entry.get('tag', None) + return (ovs_id, ovs_tag) + return None + + +def execute_cmd(cmd, sudo=False, shell=False, env=None): + if sudo: + if shell is False: + mycmd = ['sudo'] + cmd + else: + mycmd = 'sudo ' + cmd + else: + mycmd = cmd + + pprint.pprint(mycmd) + return subprocess.check_output(mycmd, + shell=shell, + stderr=subprocess.STDOUT, + env=env, + universal_newlines=True).replace('\t', ' ') + + +def get_instance_ips(objs): + ip_list = [] + for line in objs: + if re.search('^\+', line) or re.search('^$', line) or re.search('Networks', line): + continue + parts = line.split('|') + parts = [x.strip() for x in parts] + # vm = parts[2] + networks = parts[6].split(';') + networks = [x.strip() for x in networks] + for entry in networks: + # excluding ipv6 ip + if len(entry.split(',')) > 1: + # network = entry.split('=')[0] + ip = filter(lambda a: re.search("(\d+\.\d+\.\d+\.\d+)", a) is not None, + entry.split('=')[1].split(','))[0].strip() + ip_list.append(ip) + else: + ip_list.append(entry.split(',')[0].split('=')[1]) + return ip_list + + +def get_router_names(objs): + routers = [] + + for line in objs: + if re.search('^\+', line) or re.search('^$', line) or re.search('external_gateway_info', line): + continue + parts = line.split('|') + parts = [x.strip() for x in parts] + + name = parts[2] + routers.append(name) + return routers + + +def get_env(file_path): + try: + lines = open(file_path, 'r').read().splitlines() + except IOError, e: + print "%s :%s" % (e.args[1], file_path) + raise + env = {} + for line in lines: + if line.startswith('export'): + m = re.search(r'export (.+)=(.+)', line) + if m: + key = m.group(1).replace('"', '') + val = m.group(2).replace('"', '') + env.update({key: val}) + return env + + +def get_vm_credentials(config_file='credentials.yaml'): + try: + with open(config_file, 'r') as s: + return yaml.safe_load(s) + except IOError, e: + print '%s :%s' % (e.args[1], config_file) + raise diff --git a/openstack_dashboard/don/ovs/credentials.yaml b/openstack_dashboard/don/ovs/credentials.yaml new file mode 100644 index 0000000..3ba6ef3 --- /dev/null +++ b/openstack_dashboard/don/ovs/credentials.yaml @@ -0,0 +1,24 @@ +# provide VM credentials Ex: vm1,vm2 .. if no vms specified default values will be taken + +default: + username: cirros + password: cubswin:) +vm1: + username: cirros + password: cubswin:) +vm2: + username: cirros + password: cubswin:) + +hypervisor: + default: + username: ubuntu + password: cisco123 + compute1: + username: + password: + +network: + hostname: 127.0.0.1 + username: ubuntu + password: cisco123 diff --git a/openstack_dashboard/don/ovs/don.json b/openstack_dashboard/don/ovs/don.json new file mode 100644 index 0000000..1079d52 --- /dev/null +++ b/openstack_dashboard/don/ovs/don.json @@ -0,0 +1 @@ +{"bridges": {"br-tun": {"fail_mode": "secure", "ports": {"br-tun": {"mac": "6e:7a:19:13:c9:4e", "interfaces": {"br-tun": {"type": "internal"}}, "id": "LOCAL"}, "patch-int": {"mac": "42:49:d2:83:bc:8b", "interfaces": {"patch-int": {"type": "patch", "options": "peer=patch-tun"}}, "id": "1"}}}, "br-int": {"fail_mode": "secure", "ports": {"qr-8c1c0cb3-5e": {"mac": "00:00:00:00:00:00", "interfaces": {"qr-8c1c0cb3-5e": {"type": "internal"}}, "tag": "1", "id": "4"}, "qr-43b83157-3b": {"mac": "00:00:00:00:00:00", "interfaces": {"qr-43b83157-3b": {"type": "internal"}}, "tag": "2", "id": "8"}, "qr-a9ed186b-b6": {"mac": "00:00:00:00:00:00", "interfaces": {"qr-a9ed186b-b6": {"type": "internal"}}, "tag": "1", "id": "3"}, "qvo71ac5bef-7c": {"mac": "22:ef:c1:2f:74:b4", "interfaces": {"qvo71ac5bef-7c": {}}, "tag": "2", "id": "7"}, "qvo8aa60600-7b": {"mac": "6e:4a:ba:9e:41:fc", "interfaces": {"qvo8aa60600-7b": {}}, "tag": "2", "id": "6"}, "tap88750e1e-d1": {"mac": "00:00:00:00:00:00", "interfaces": {"tap88750e1e-d1": {"type": "internal"}}, "tag": "1", "id": "9"}, "tap59f90a3b-f5": {"mac": "42:e1:86:34:b2:89", "interfaces": {"tap59f90a3b-f5": {"type": "internal"}}, "id": "5"}, "patch-tun": {"mac": "46:85:0f:44:94:dd", "interfaces": {"patch-tun": {"type": "patch", "options": "peer=patch-int"}}, "id": "1"}, "br-int": {"mac": "f6:7f:12:2e:66:49", "interfaces": {"br-int": {"type": "internal"}}, "id": "LOCAL"}, "tap053da1b7-53": {"mac": "2e:e3:08:d3:e4:fa", "interfaces": {"tap053da1b7-53": {"type": "internal"}}, "id": "2"}, "tap1e1c73c9-35": {"mac": "00:00:00:00:00:00", "interfaces": {"tap1e1c73c9-35": {"type": "internal"}}, "tag": "2", "id": "10"}}}, "br-ex": {"ports": {"qg-757bf552-73": {"mac": "00:00:00:00:00:00", "interfaces": {"qg-757bf552-73": {"type": "internal"}}, "id": "2"}, "qg-26e738c4-f8": {"mac": "00:00:00:00:00:00", "interfaces": {"qg-26e738c4-f8": {"type": "internal"}}, "id": "1"}, "br-ex": {"mac": "62:ed:7e:21:23:08", "interfaces": {"br-ex": {"type": "internal"}}, "id": "LOCAL"}}}}, "routers": {"router1": {"network_id": "3f6dda86-0ae2-475d-a71c-a0d8d7233a0a", "ip_address": "172.24.4.3", "id": "90111551-6cfc-4be0-b1c2-ce8bffb7edf6"}}, "vms": {"vm2": {"tap_dev": ["tap71ac5bef-7c"], "interfaces": {"10.10.0.4": {"network": "private1"}}, "src_bridge": ["qbr71ac5bef-7c"], "uuid": "ddb4de16-4729-4732-a73b-557d75f28180"}, "vm1": {"tap_dev": ["tap8aa60600-7b"], "interfaces": {"10.10.0.3": {"network": "private1"}}, "src_bridge": ["qbr8aa60600-7b"], "uuid": "72973345-78b0-4886-8fe9-9e29aa9fba9f"}}, "brctl": {"qbr71ac5bef-7c": {"interfaces": ["qvb71ac5bef-7c", "tap71ac5bef-7c"]}, "qbr8aa60600-7b": {"interfaces": ["qvb8aa60600-7b"]}}, "namespaces": {"qrouter-5a5b1d7f-263e-4d7f-a508-60d359d68a01": {}, "qdhcp-25fe7626-552c-4040-b2b6-8050ff7590e0": {"interfaces": {"tap88750e1e-d1": "10.0.0.3/24"}}, "qrouter-90111551-6cfc-4be0-b1c2-ce8bffb7edf6": {"interfaces": {"qg-757bf552-73": "172.24.4.3/24", "qr-43b83157-3b": "10.10.0.1/24"}}, "qdhcp-f89297fc-3edf-47cf-9d3a-15d40639cb77": {"interfaces": {"tap1e1c73c9-35": "10.10.0.5/24"}}}, "networks": {"f89297fc-3edf-47cf-9d3a-15d40639cb77": {"ip": "10.10.0.0/24", "name": "private1"}, "3f6dda86-0ae2-475d-a71c-a0d8d7233a0a": {"ip": "172.24.4.0/24", "name": "public"}, "25fe7626-552c-4040-b2b6-8050ff7590e0": {"ip": "10.0.0.0/24", "name": "private"}}, "tap_to_ip": {"": "fdb4:816a:6faf:0:f816:3eff:feb1:fb45", "a9ed186b-b6": "10.0.0.1", "1e1c73c9-35": "10.10.0.5", "71ac5bef-7c": "10.10.0.4", "8aa60600-7b": "10.10.0.3", "26e738c4-f8": "172.24.4.2", "88750e1e-d1": "10.0.0.3", "43b83157-3b": "10.10.0.1", "8c1c0cb3-5e": "fdb4:816a:6faf::1", "757bf552-73": "172.24.4.3"}} \ No newline at end of file diff --git a/openstack_dashboard/don/ovs/forms.py b/openstack_dashboard/don/ovs/forms.py new file mode 100644 index 0000000..e044214 --- /dev/null +++ b/openstack_dashboard/don/ovs/forms.py @@ -0,0 +1,10 @@ +from django import forms + + +class PingForm(forms.Form): + src_ip = forms.CharField(label='Source IP', widget=forms.Select( + attrs={'class': 'form-control switchable'}, choices=[('', "")])) + dst_ip = forms.CharField(label='Destination IP', widget=forms.Select( + attrs={'class': 'form-control switchable'}, choices=[('', "")])) + router = forms.CharField(label='Router', widget=forms.Select( + attrs={'class': 'form-control switchable'}, choices=[('', "")])) diff --git a/openstack_dashboard/don/ovs/load_json.py b/openstack_dashboard/don/ovs/load_json.py new file mode 100644 index 0000000..5c0fd36 --- /dev/null +++ b/openstack_dashboard/don/ovs/load_json.py @@ -0,0 +1,11 @@ +import pprint +import sys + +from common import load_json + +if len(sys.argv) != 2: + print ('Usage: ' + sys.argv[0] + ' ') + exit(1) + +info = load_json(sys.argv[1]) +pprint.pprint(info) diff --git a/openstack_dashboard/don/ovs/ovs.py b/openstack_dashboard/don/ovs/ovs.py new file mode 100644 index 0000000..fdc57e8 --- /dev/null +++ b/openstack_dashboard/don/ovs/ovs.py @@ -0,0 +1,176 @@ +# +# ovs.py: Runs ovs-appctl command to check if A -> B flow is working fine. +# +# +import re +import argparse +import json +from common import debug, settings +from common import execute_cmd + +params = {} + +output_dict = { + 'comment': None, + 'pass': None, + 'command_list': [], + 'errors': [], + 'debugs': [], +} + +# Learn a MAC on the dst port and then check if sending from the src port to +# the learned MAC gives correct lookup + + +def ovs_test(src_port_id, dst_port_id, tag, ovs_bridge): + smac = 'AA:BB:CC:DD:EE:11' + dmac = 'AA:BB:CC:DD:EE:22' + cmd_dict = {} + + # Step 0. Flush the fdb + cmd = '' + cmd += 'sudo ovs-appctl fdb/flush br-int' + output = execute_cmd(cmd, shell=True).split('\n') + cmd_dict['cmd'] = cmd + cmd_dict['output'] = output + output_dict['command_list'].append(cmd_dict) + cmd_dict = {} + + # Step 1. run command that will learn smac + cmd = '' + cmd += 'sudo ovs-appctl ofproto/trace %s in_port=%s' % ( + ovs_bridge, src_port_id) + cmd += ',dl_src=' + smac + ',dl_dst=' + dmac + ' -generate' + output = execute_cmd(cmd, shell=True).split('\n') + cmd_dict['cmd'] = cmd + cmd_dict['output'] = output + output_dict['command_list'].append(cmd_dict) + cmd_dict = {} + + # Step 2. verify that the mac has been learnt + cmd = '' + cmd += 'sudo ovs-appctl fdb/show br-int' + output = execute_cmd(cmd, shell=True).split('\n') + cmd_dict['cmd'] = cmd + cmd_dict['output'] = output + output_dict['command_list'].append(cmd_dict) + cmd_dict = {} + + port = None + for line in output: + m = re.search('(\d)\s+(\d+)\s+(\S+)\s+\d+', line) + if m: + mac = m.group(3) + if mac.lower() == smac.lower(): + port = m.group(1) + vlan = m.group(2) + debug(line) + break + if not port: + output_dict['errors'].append( + '%s not learnt on port %s' % (smac, src_port_id)) + output_dict['pass'] = False + return False + + if vlan != tag: + output_dict['errors'].append( + '%s learnt on vlan %s but should have been learnt on vlan %s on port %s' % (smac, vlan, tag, port)) + output_dict['pass'] = False + return False + output_dict['debugs'].append( + '%s learnt on expected vlan %s on port %s' % (smac, vlan, port)) + + # Step 3. now do a lookup using the dst port id and dmac as the smac of + # step 1. + cmd = '' + cmd += 'sudo ovs-appctl ofproto/trace %s in_port=%s' % ( + ovs_bridge, dst_port_id) + cmd += ',dl_src=' + dmac + ',dl_dst=' + smac + ' -generate' + output = execute_cmd(cmd, shell=True).split('\n') + cmd_dict['cmd'] = cmd + cmd_dict['output'] = output + output_dict['command_list'].append(cmd_dict) + cmd_dict = {} + + forwarded = False + egress_port = None + for line in output: + if re.search('forwarding to learned port', line): + forwarded = True + continue + m = re.search('Datapath actions: (.*)', line) + if m: + egress_port = m.group(1) + continue + + result = True + if not forwarded: + output_dict['errors'].append('Packet for learnt mac not forwarded!') + result = False + else: + output_dict['debugs'].append( + 'Packet for learnt mac forwarded properly') + + if egress_port: + if egress_port == src_port_id: + output_dict['debugs'].append( + 'Packet forwarded to correct port %s' % egress_port) + else: + output_dict['errors'].append('Packet forwarded to incorrect port %s, expected %s' % + (egress_port, src_port_id)) + result = False + else: + output_dict['errors'].append('No egress port assigned to packet! Expected %s' % + src_port_id) + result = False + + output_dict['pass'] = result + return result + + +def check_args(): + global params + + parser = argparse.ArgumentParser( + description='OVS test', formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--debug', dest='debug', + help='Enable debugging', default=False, action='store_true') + parser.add_argument('--src_port_id', dest='src_port_id', + help='OVS src port id (required)', type=str, required=True) + parser.add_argument('--dst_port_id', dest='dst_port_id', + help='OVS dst port id (required)', type=str, required=True) + parser.add_argument( + '--tag', dest='tag', help='VLAN tag of port (required)', type=str, required=True) + parser.add_argument('--ovs_bridge', dest='ovs_bridge', + help='OVS bridge to be tested (required)', type=str, required=True) + args = parser.parse_args() + + settings['debug'] = args.debug + params['src_port_id'] = args.src_port_id + params['dst_port_id'] = args.dst_port_id + params['tag'] = args.tag + params['ovs_bridge'] = args.ovs_bridge + + +def main(): + global output_dict + + check_args() + + src_port_id = params['src_port_id'] + dst_port_id = params['dst_port_id'] + tag = params['tag'] + ovs_bridge = params['ovs_bridge'] + + ovs_success = ovs_test(src_port_id, dst_port_id, tag, ovs_bridge) + + output_dict[ + 'comment'] = 'ovs %s port %s --> %s' % (ovs_bridge, src_port_id, dst_port_id) + output_dict['pass'] = ovs_success + + a = json.dumps(output_dict, sort_keys=True, indent=4) + print a + pass + +if __name__ == "__main__": + main() diff --git a/openstack_dashboard/don/ovs/panel.py b/openstack_dashboard/don/ovs/panel.py new file mode 100644 index 0000000..41346fb --- /dev/null +++ b/openstack_dashboard/don/ovs/panel.py @@ -0,0 +1,12 @@ +from django.utils.translation import ugettext_lazy as _ + +import horizon +from don import dashboard + + +class ovs(horizon.Panel): + name = _("OVS") + slug = "ovs" + + +dashboard.DonDashboard.register(ovs) diff --git a/openstack_dashboard/don/ovs/path.py b/openstack_dashboard/don/ovs/path.py new file mode 100644 index 0000000..e48574a --- /dev/null +++ b/openstack_dashboard/don/ovs/path.py @@ -0,0 +1,496 @@ +# +# path.py: Figures out a path between two IP addresses and then traces it +# +# HOWTO: +# +import re +import pprint +import subprocess +import argparse +import os.path +import signal +import json +import time +from common import error, settings, debug, status_update +from common import load_json, execute_cmd, dump_json +from common import ip_to_intf, intf_to_namespace, router_to_namespace + +COUNT = 10 # how many packets to be captured by tcpdump + + +def get_port_info(info, port_ip): + port_info = None + for tap, ip in info['tap_to_ip'].iteritems(): + if ip == port_ip: + port_info = {} + port_info['ip'] = ip + port_info['ports'] = {} + port_info['ports']['tap'] = 'tap' + tap + port_info['ports']['brctl'] = 'qbr' + tap + port_info['ports']['qvb'] = 'qvb' + tap + port_info['ports']['qvo'] = 'qvo' + tap + + # also get the tag (used later to figure out where to run tcpdump) + br_int = info['bridges'].get('br-int', None) + if not br_int: + error('No OVS integration bridge (br-int)! Cannot proceed') + return None + + tag = br_int['ports'][port_info['ports']['qvo']]['tag'] + port_info['tag'] = tag + + break + return port_info + + +def qrouter_usable(qrouter, src_ip, dst_ip, username, passwd): + status_update('Testing whether %s is reachable via qrouter %s (dst %s)' % ( + src_ip, qrouter, dst_ip)) + outfile = 'path.testping.txt' + ping_process = launch_ping( + src_ip, dst_ip, username, passwd, 2, 2, qrouter, outfile) + status_update("Ping process %s" % (ping_process)) + time.sleep(5) + + ping_pass = process_ping(outfile, src_ip, check_ssh_connectivity_only=True) + + if ping_pass: + status_update('IP %s is reachable via qrouter: %s' % (src_ip, qrouter)) + return True + else: + error('IP %s is reachable via qrouter: %s' % (src_ip, qrouter)) + + return False + + +def launch_ping(src_ip, dst_ip, username, passwd, count, timeout, qrouter, filename): + 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' % \ + (src_ip, dst_ip, username, passwd, count, timeout) + cmd += ' > %s 2>&1' % filename + + p = subprocess.Popen(cmd, shell=True) + + return p + + +def capture_network_packets(params, hop_list): + global net_info + + net_info = { + 'pids': [], + 'hops': hop_list, + } + + for hop in net_info['hops']: + dev = hop['dev'] + nms = hop['nms'] + filename = 'net.tcpdump.%s.txt' % (dev) + if os.path.isfile(filename): + os.remove(filename) + cmd = 'sudo ip netns exec %s ' % nms + cmd += 'tcpdump -v icmp -i %s -c %d -l > %s 2>&1' % ( + dev, params['count'], filename) + pid = subprocess.Popen(cmd, shell=True).pid + net_info['pids'].append(pid) + status_update( + 'net: tcpdump launched with pid %d for interface %s' % (pid, dev)) + pass + + +def capture_packets(params, tag='src', src_tag=None): + if tag == 'src': + port_info = src_info + elif tag == 'dst': + port_info = dst_info + else: + error('tag has to be one of [src, dst]!') + return + + # XXX TODO + # If src_tag and dst_tag are the same, then we need to monitor on just + # br-int. Else, we will need to monitor on qr- ports (router ports) + + port_info['pids'] = [] + for port in port_info['ports'].keys(): + intf = port_info['ports'][port] + filename = '%s.tcpdump.%s.txt' % (tag, intf) + if os.path.isfile(filename): + os.remove(filename) + cmd = 'sudo tcpdump -v icmp -i %s -c %d -l > %s 2>&1' % ( + intf, params['count'], filename) + pid = subprocess.Popen(cmd, shell=True).pid + port_info['pids'].append(pid) + status_update( + '%s: tcpdump launched with pid %d for interface %s' % (tag, pid, intf)) + + +def process_ping(filename, ip=None, check_ssh_connectivity_only=False): + if not os.path.isfile(filename): + return False + + status_update('Trying to read ' + filename) + with open(filename) as f: + lines = f.readlines() + pprint.pprint(lines) + + info = load_json(filename) + if not check_ssh_connectivity_only: + return info.get('pass', False) + + cmd_list = info['command_list'] + for cmd in cmd_list: + m = re.search( + 'ssh (\S+) with provided username and passwd', cmd['cmd']) + if m: + if ip == m.group(1): + return cmd['pass'] + return False + + +def process_network_captures(): + global net_info + + net_info['counts'] = {} + net_info['pass'] = [] + net_info['fail'] = [] + + for hop in net_info['hops']: + dev = hop['dev'] + + # Assume tcpdump did not capture anything + net_info['counts'][dev] = 0 + net_info['fail'].append(dev) + + filename = 'net.tcpdump.%s.txt' % (dev) + if not os.path.isfile(filename): + continue + + with open(filename) as f: + lines = f.readlines() + for line in lines: + m = re.search('(\d+)\s+packets captured', line) + if m: + net_info['counts'][dev] = int(m.group(1)) + net_info['pass'].append(dev) + break +''' + cmd = 'grep captured ' + filename + output = execute_cmd(cmd, shell=True).split('\n')[0] + + m = re.search('(\d+)\s+packets captured', output) + if m: + net_info['counts'][dev] = int(m.group(1)) + net_info['pass'].append(dev) + else: + net_info['counts'][dev] = 0 + net_info['fail'].append(dev) +''' + + +def process_captures(tag='src'): + if tag == 'src': + port_info = src_info + elif tag == 'dst': + port_info = dst_info + else: + error('tag has to be one of [src, dst]!') + return + + port_info['counts'] = {} + port_info['pass'] = [] + port_info['fail'] = [] + for key in port_info['ports'].keys(): + intf = port_info['ports'][key] + + # Assume tcpdump did not capture anything + port_info['counts'][key] = 0 + port_info['fail'].append(intf) + filename = '%s.tcpdump.%s.txt' % (tag, intf) + + if not os.path.isfile(filename): + continue + + with open(filename) as f: + lines = f.readlines() + for line in lines: + m = re.search('(\d+)\s+packets captured', line) + if m: + port_info['counts'][key] = int(m.group(1)) + port_info['pass'].append(intf) + break + +''' + cmd = 'grep captured ' + filename + output = execute_cmd(cmd, shell=True).split('\n')[0] + + m = re.search('(\d+)\s+packets captured', output) + if m: + port_info['counts'][key] = int(m.group(1)) + port_info['pass'].append(intf) + else: + port_info['counts'][key] = 0 + port_info['fail'].append(intf) +''' + + +def cleanup_processes(pid_list): + pprint.pprint(pid_list) + for pid in pid_list: + try: + os.kill(pid, signal.SIGKILL) + status_update('Successfully killed pid: %d' % pid) + except OSError: + status_update('Process with pid: %d no longer exists' % pid) + continue + pass + + +def check_args(params): + + parser = argparse.ArgumentParser(description='Does ping test and captures packets along the expected path', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--debug', dest='debug', + help='Enable debugging', default=True, + action='store_true') + parser.add_argument('--src_ip', dest='src_ip', + help='IP from where ping will be run (required)', + type=str, required=True) + parser.add_argument('--dst_ip', dest='dst_ip', + help='IP to which ping will be run (required)', + type=str, required=True) + parser.add_argument('--username', dest='username', + help='SSH login username (required)', + type=str, required=True) + parser.add_argument('--passwd', dest='passwd', + help='SSH login passwd (required)', + type=str, required=True) + parser.add_argument('--json_file', dest='json_file', + help='JSON file having info of installation (required)', + type=str, required=True) + parser.add_argument('--count', dest='count', + help='ping count', type=int, default=COUNT) + parser.add_argument('--timeout', dest='timeout', + help='ping timeout (-W option of ping) in seconds', + type=int, default=2) + parser.add_argument('--router', dest='router', + help='router to be used for the test', type=str, + required=True) + parser.add_argument('--path_file', dest='path_file', + help="Test results are printed in this file in JSON format", + type=str, default='path.json') + args = parser.parse_args() + + params['debug'] = args.debug + params['src_ip'] = args.src_ip + params['dst_ip'] = args.dst_ip + params['username'] = args.username + params['passwd'] = args.passwd + params['count'] = args.count + params['timeout'] = args.timeout + params['json_file'] = args.json_file + params['router'] = args.router + params['path_file'] = args.path_file + + +def path_same_network(params, nms_hops=None): + src_ip = params['src_ip'] + dst_ip = params['dst_ip'] + json_file = params['json_file'] + username = params['username'] + passwd = params['passwd'] + count = params['count'] + timeout = params['timeout'] + qrouter = params['qrouter'] + router = params['router'] + + if qrouter_usable(qrouter, src_ip, dst_ip, username, passwd): + outfile = 'path.ping.txt' + ping_process = launch_ping(src_ip, dst_ip, username, passwd, count, + timeout, qrouter, outfile) + debug('Ping started with pid: %d' % ping_process.pid) + + capture_packets(params, 'src') + capture_packets(params, 'dst', src_tag=src_info['tag']) + if src_info['tag'] != dst_info['tag']: + capture_network_packets(params, nms_hops) + + status_update('Waiting %s sec for tcpdump and ping processes to complete' % ( + params['count'] + 2)) + time.sleep(params['count'] + 4) + + status_update('if processes have not stopped, lets kill them') + cleanup_processes([ping_process.pid] + + src_info['pids'] + dst_info['pids']) + if net_info: + cleanup_processes(net_info['pids']) + + process_captures('src') + process_captures('dst') + if src_info['tag'] != dst_info['tag']: + process_network_captures() + ping_pass = process_ping(outfile) + + debug(pprint.pformat(src_info)) + debug(pprint.pformat(dst_info)) + debug(pprint.pformat(net_info)) + info = { + 'src': src_ip, + 'dst': dst_ip, + 'src_info': src_info, + 'dst_info': dst_info, + 'net_info': net_info, + 'ping_pass': ping_pass, + 'error': '', + } + + status_update('Dumping results into %s in JSON format' % + params['path_file']) + dump_json(info, params['path_file']) + + if params['plot']: + cmd = 'python plot.py --info_file %s --highlight_file %s --combined_file static/ping' % ( + json_file, params['path_file']) + status_update('Running ' + cmd) + output = execute_cmd(cmd, shell=True).split('\n') + debug(pprint.pformat(output)) + status_update('Done') + else: + err_msg = 'Cannot reach %s via router %s' % (src_ip, router) + info = { + 'src': src_ip, + 'dst': dst_ip, + 'src_info': src_info, + 'dst_info': dst_info, + 'ping_pass': False, + 'error': err_msg + } + error(err_msg) + status_update('Dumping results into %s in JSON format' % + params['path_file']) + dump_json(info, params['path_file']) + status_update('Done') + + +def run_remote_cmd(cmd): + debug('Running: ' + cmd) + return subprocess.check_output(cmd, + shell=True, + stderr=subprocess.STDOUT, + universal_newlines=True).replace('\t', ' ') + + +def get_next_hop(src_info, dst_info, qrouter, params): + next_hop_list = [] + next_hop = None + + username = params['username'] + passwd = params['passwd'] + src_ip = src_info['ip'] + dst_ip = dst_info['ip'] + + remote_cmd = ' ip route get %s' % dst_ip + + cmd = 'sudo ip netns exec ' + qrouter + cmd += ' python run_nms_cmd.py --host_ip %s --username "%s" --passwd "%s" --cmd "%s" ' % \ + (src_ip, username, passwd, remote_cmd) + + output = run_remote_cmd(cmd) + a = json.loads(output) + + if not a['pass']: + return [] + + json_file = params['json_file'] + info = load_json(json_file) + + next_hop = {} + for cmd in a['command_list']: + if re.search('ip route get', cmd['cmd']): + m = re.search('\S+\s+via\s+(\S+)', cmd['output'][0]) + if m: + next_hop['ip'] = m.group(1) + next_hop['dev'] = 'qr-' + ip_to_intf(info, next_hop['ip']) + next_hop['nms'] = intf_to_namespace(info, next_hop['dev']) + break + + next_hop_list.append(next_hop) + + cmd = 'sudo ip netns exec ' + next_hop['nms'] + cmd += remote_cmd + + output = run_remote_cmd(cmd).split('\n') + + prev_nms = next_hop['nms'] + next_hop = {} + m = re.search('\S+\s+dev\s+(\S+)', output[0]) + if m: + next_hop['dev'] = m.group(1) + next_hop['nms'] = prev_nms + + next_hop_list.append(next_hop) + return next_hop_list + + +def path(params): + global src_info + global dst_info + global net_info + + src_info = None + dst_info = None + net_info = None + + settings['debug'] = True + BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + CUR_DIR = os.getcwd() + if not re.search('/openstack_dashboard/don/', CUR_DIR): + os.chdir(BASE_DIR + '/ovs') + NEW_DIR = os.getcwd() + debug(BASE_DIR + ':' + CUR_DIR + ':' + NEW_DIR) + + src_ip = params['src_ip'] + dst_ip = params['dst_ip'] + json_file = params['json_file'] + router = params['router'] + + debug('Json_file: ' + json_file) + + info = load_json(json_file) + qrouter = router_to_namespace(info, router) + params['qrouter'] = qrouter + + src_info = get_port_info(info, src_ip) + dst_info = get_port_info(info, dst_ip) + + if src_info is None: + return "Source ip not found on the network" + if dst_info is None: + return "Destination ip not found on the network" + if qrouter is None: + return "No such router information found on the network" + + # src and dst are in the same network + if src_info['tag'] == dst_info['tag']: + path_same_network(params) + else: + status_update('The source and destination are in different networks') + next_hop_list = get_next_hop(src_info, dst_info, qrouter, params) + if len(next_hop_list) == 0: + error('Could not find next hop list from %s to %s' % + (src_ip, dst_ip)) + path_same_network(params, next_hop_list) + + pass + + +def main(): + + params = {} + check_args(params) + + settings['debug'] = params['debug'] + path(params) + +if __name__ == "__main__": + main() diff --git a/openstack_dashboard/don/ovs/ping.py b/openstack_dashboard/don/ovs/ping.py new file mode 100644 index 0000000..0322e63 --- /dev/null +++ b/openstack_dashboard/don/ovs/ping.py @@ -0,0 +1,122 @@ +# +# ping.py: Runs a ping test from src_ip to dst_ip. Also provides analysis if +# things are not okay (TBD). +# +# HOWTO: +# +# 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:)" +# +import re +import argparse +import json +from common import connect_to_box, ssh_cmd +from common import settings + + +params = {} + +output_dict = { + 'comment': None, + 'pass': None, + 'command_list': [], + 'errors': [], +} + + +def ping_test(src_ip, dst_ip, username, passwd, count, timeout): + global output_dict + result = False + cmd_dict = {} + try: + ssh = connect_to_box(src_ip, username, passwd) + cmd_dict['cmd'] = 'ssh %s with provided username and passwd' % src_ip + if not ssh: + cmd_dict['output'] = 'Could not ssh to ' + src_ip + cmd_dict['pass'] = False + output_dict['command_list'].append(cmd_dict) + return False + else: + cmd_dict['pass'] = True + output_dict['command_list'].append(cmd_dict) + cmd_dict = {} + cmd = 'ping -c %s -W %s %s' % (count, timeout, dst_ip) + output = ssh_cmd(ssh, cmd).split('\n') + cmd_dict['cmd'] = cmd + cmd_dict['output'] = output + for line in output: + m = re.search('(\d+) packets transmitted, (\d+) packets received', line) or \ + re.search('(\d+) packets transmitted, (\d+) received', + line) # also handles cirros vm ping response + if m: + tx_pkts = float(m.group(1)) + rx_pkts = float(m.group(2)) + if rx_pkts / tx_pkts >= 0.75: + result = True + break + except (KeyboardInterrupt, SystemExit): + print '\nkeyboardinterrupt caught (again)' + print '\n...Program Stopped Manually!' + raise + cmd_dict['pass'] = result + output_dict['command_list'].append(cmd_dict) + return result + + +def check_args(): + global params + + parser = argparse.ArgumentParser( + description='Ping test', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--debug', dest='debug', + help='Enable debugging', + default=False, action='store_true') + parser.add_argument('--src_ip', dest='src_ip', + help='IP from where ping will be run (required)', + type=str, required=True) + parser.add_argument('--dst_ip', dest='dst_ip', + help='IP to which ping will be run (required)', + type=str, required=True) + parser.add_argument('--username', dest='username', + help='SSH login username (required)', type=str, + required=True) + parser.add_argument('--passwd', dest='passwd', + help='SSH login passwd (required)', + type=str, required=True) + parser.add_argument('--count', dest='count', + help='ping count', type=str, default='2') + parser.add_argument('--timeout', dest='timeout', + help='ping timeout (-W option of ping) in seconds', + type=str, default='4') + args = parser.parse_args() + + settings['debug'] = args.debug + params['src_ip'] = args.src_ip + params['dst_ip'] = args.dst_ip + params['username'] = args.username + params['passwd'] = args.passwd + params['count'] = args.count + params['timeout'] = args.timeout + + +def main(): + global output_dict + + check_args() + + src_ip = params['src_ip'] + dst_ip = params['dst_ip'] + ping_success = ping_test(src_ip, dst_ip, + params['username'], params['passwd'], + params['count'], params['timeout']) + + output_dict['comment'] = 'PING %s to %s' % (src_ip, dst_ip) + output_dict['pass'] = ping_success + + a = json.dumps(output_dict, sort_keys=True, indent=4) + print a + +if __name__ == "__main__": + main() diff --git a/openstack_dashboard/don/ovs/plot.py b/openstack_dashboard/don/ovs/plot.py new file mode 100644 index 0000000..f859907 --- /dev/null +++ b/openstack_dashboard/don/ovs/plot.py @@ -0,0 +1,898 @@ +# +# plot.py: Generates an SVG file showing the networking internals of a compute node. +# +import pprint +import subprocess +import re +import argparse +import sys +import random + +from common import settings, debug, warning +from common import load_json, get_subnet +from common import get_vlan_tag, get_intf_ip, get_ip_network + + +class DotGenerator: + + def __init__(self, in_json_filename, + compute_dot_file, compute_svg_file, + network_dot_file, network_svg_file, + combined_dot_file, combined_svg_file, + highlight_file): + self.json_filename = in_json_filename + + self.compute_dot_file = compute_dot_file + self.compute_svg_file = compute_svg_file + self.network_dot_file = network_dot_file + self.network_svg_file = network_svg_file + self.combined_dot_file = combined_dot_file + self.combined_svg_file = combined_svg_file + self.highlight_file = highlight_file + + settings['debug'] = True + + self.highlight_info = None + if highlight_file: + self.highlight_info = load_json(self.highlight_file) + if not self.highlight_info.get('net_info'): + self.highlight_info['net_info'] = {'pass': [], + 'fail': [] + } + + self.info = load_json(self.json_filename) + self.outfile = None + + self.colors = { + 'vms': '#ff9933', + 'tap': '#99ffff', + 'qbr': '#9966ff', + 'br-int': '#ff6666', + 'br-tun': '#ff6666', + 'qvb': '#ffcc00', + 'qvo': '#ffcc00', + 'tun': '#ffcc00', + 'int': '#ffcc00', + 'routers': '#ff9933', + 'vlan': [], + 'error': '#f00000', + 'edge': '#0066cc', + 'dontcare': '#909090', + 'pass': '#b2f379', + 'fail': '#f00000', + 'edge_pass': '#009900', + 'floating_ip': '#b3ffb3', + } + self.__set_vlan_color_table() + pprint.pprint(self.info) + + def __port_pass(self, port): + if self.highlight_file: + if port.replace('.', '') == self.highlight_info['src_info']['ip'].replace('.', '') or \ + port.replace('.', '') == self.highlight_info['dst_info']['ip'].replace('.', ''): + return self.highlight_info['ping_pass'] + if self.highlight_info['src_info'].has_key('pass') and port in self.highlight_info['src_info']['pass'] or \ + self.highlight_info['dst_info'].has_key('pass') and port in self.highlight_info['dst_info']['pass'] or \ + self.highlight_info['net_info'].has_key('pass') and port in self.highlight_info['net_info']['pass']: + return True + return False + + def __port_fail(self, port): + if self.highlight_file: + if port.replace('.', '') == self.highlight_info['src_info']['ip'].replace('.', '') or \ + port.replace('.', '') == self.highlight_info['dst_info']['ip'].replace('.', ''): + return not self.highlight_info['ping_pass'] + if self.highlight_info['src_info'].has_key('fail') and port in self.highlight_info['src_info']['fail'] or \ + self.highlight_info['dst_info'].has_key('fail') and port in self.highlight_info['dst_info']['fail'] or \ + self.highlight_info['net_info'].has_key('fail') and port in self.highlight_info['net_info']['fail']: + return True + return False + + def __get_edge_color(self, src_tag, dst_tag): + if not self.highlight_file: + return self.__get_color('edge') + + sport = src_tag + dport = dst_tag + m = re.search('\S+:(\S+)', src_tag) + if m: + sport = m.group(1) + + m = re.search('\S+:(\S+)', dst_tag) + if m: + dport = m.group(1) + + spass = self.__port_pass(sport) + dpass = self.__port_pass(dport) + + sfail = self.__port_fail(sport) + dfail = self.__port_fail(dport) + + debug('%s (p%d f%d) -> %s (p%d f%d)' % (sport, spass, sfail, dport, + dpass, dfail)) + + if spass or dpass: + return self.colors['edge_pass'] + if sfail and dfail: + return self.colors['fail'] + + return self.colors['dontcare'] + + def __get_color(self, tag): + if self.highlight_file: + return self.colors['dontcare'] + else: + return self.colors[tag] + + def __hsv_to_rgb(self, h, s, v): + h_i = int((h * 6)) + f = h * 6 - h_i + p = v * (1 - s) + q = v * (1 - f * s) + t = v * (1 - (1 - f) * s) + + if h_i == 0: + r, g, b = v, t, p + if h_i == 1: + r, g, b = q, v, p + if h_i == 2: + r, g, b = p, v, t + if h_i == 3: + r, g, b = p, q, v + if h_i == 4: + r, g, b = t, p, v + if h_i == 5: + r, g, b = v, p, q + + return [r * 256, g * 256, b * 256] + + def __set_vlan_color_table(self): + i = 20 + random.seed(1) + while i > 0: + colors = self.__hsv_to_rgb(random.random(), 0.5, 0.95) + colors = [hex(int(x)).split('x')[1] for x in colors] + colors = ''.join(x for x in colors) + self.colors['vlan'].append('#' + colors) + i -= 1 + debug(pprint.pformat(self.colors['vlan'])) + + # port becomes relevant only if highlight_file is specified. + def __get_vlan_color(self, tag, port='dummy'): + if self.highlight_file: + if self.__port_pass(port): + return self.colors['pass'] + elif self.__port_fail(port): + return self.colors['fail'] + else: + return self.colors['dontcare'] + else: + total_colors = len(self.colors['vlan']) + return self.colors['vlan'][int(tag) % total_colors] + + def __get_total_vm_port_count(self): + port_count = 0 + for vm in self.info['vms'].keys(): + port_count += len(self.info['vms'][vm]['src_bridge']) + return port_count + + # TODO XXX needs some work to handle different subnet mask length. LPM needs + # to be implemented! + def __get_network_id(self, ip): + networks = self.info['networks'] + subnet = get_subnet(ip) + + for net in networks.keys(): + if re.search(subnet, networks[net]['ip']): + return net + return None + + def __get_network_name(self, ip): + network_id = self.__get_network_id(ip) + return self.info['networks'][network_id]['name'] + + def __get_tap_interface(self, namespace, qr_intf): + namespaces = self.info['namespaces'] + ip = namespaces[namespace]['interfaces'][qr_intf] + network_id = self.__get_network_id(ip) + if not network_id: + return 'No TAP! 1' + qdhcp = 'qdhcp-' + network_id + if not namespaces.has_key(qdhcp): + return 'No TAP! 2' + for intf in namespaces[qdhcp]['interfaces'].keys(): + return (qdhcp, intf) + pass + + def __get_router_port_count(self, router, port_type='qr'): + port_count = 0 + router_id = self.info['routers'][router]['id'] + qrouter = 'qrouter-' + router_id + + namespaces = self.info['namespaces'] + for nms in namespaces.keys(): + if re.search('^' + qrouter, nms): + for intf in namespaces[nms]['interfaces'].keys(): + if re.search('^' + port_type, intf): + port_count += 1 + return port_count + + def __get_total_port_count(self, port_type='qr'): + port_count = 0 + for router in self.info['routers'].keys(): + port_count += self.__get_router_port_count(router, port_type) + + return port_count + + def __get_total_dhcp_port_count(self): + port_count = 0 + namespaces = self.info['namespaces'] + + for nms in namespaces.keys(): + if re.search('^qdhcp-', nms): + for intf in namespaces[nms]['interfaces'].keys(): + if re.search('^tap', intf): + port_count += 1 + return port_count + + def __html_row_open(self): + print '' + + def __html_row_close(self): + print '' + + def __html_row(self, name, rspan, cspan, color, tag=None): + # tags do not allow "-" (dash) in DOT language. Convert to "_" + # (underscore) + if tag: + print '%s' % (rspan, cspan, color, tag.replace('-', '_'), name) + else: + print '%s' % (rspan, cspan, color, name) + pass + + def __html_edge(selft, src_tag, dst_tag, color, penwidth="4", style=None): + src_tag = src_tag.replace('-', '_') + dst_tag = dst_tag.replace('-', '_') + if not style: + print '%s:s -> %s:n [color = "%s", penwidth = "%s"]' % (src_tag, + dst_tag, + color, + penwidth) + else: + print '%s:s -> %s:n [color = "%s", penwidth = "%s", style="%s"]' % (src_tag, + dst_tag, + color, + penwidth, + style) + + def __digraph_open(self, tag): + msg = 'digraph DON_' + tag + ' {' + \ + ''' +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] +''' + print msg + + def __digraph_close(self): + msg = '\n}\n' + print msg + + def __cluster_name(self, tag, col_span, color="white"): + self.__html_row_open() + port = tag.replace(' ', '').replace('-', '_') + print '%s' % (col_span, color, port, tag) + self.__html_row_close() + + def __cluster_open_plain(self, tag, label=None): + print 'subgraph cluster_%s {' % (tag) + print 'style=filled' + if label: + print 'label="%s"' % (label) + + def __cluster_close_plain(self): + print '}\n' + + def __cluster_open(self, tag, color="white"): + print 'subgraph cluster_%s {' % (tag) + print '%s [ shape = plaintext, label = <' % (tag) + print '' % (color) + pass + + def __cluster_close(self): + print '
>];\n' + print '}\n' + pass + + def __plot_title_edges(self, tag): + if tag == 'compute': + src_tag = 'ComputeNode' + dst_tag = 'VMs' + else: + src_tag = 'NetworkNode' + dst_tag = 'br_ex' + self.__html_edge(src_tag, dst_tag, + self.__get_color('edge'), style="invis") + + def __plot_vms(self): + col_span = self.__get_total_vm_port_count() + row_span = 1 + self.__cluster_open('VMs') + self.__cluster_name('VMs', col_span) + + # Plot each VM at a time + self.__html_row_open() + for vm in sorted(self.info['vms'].keys()): + col_span = len(self.info['vms'][vm]['src_bridge']) + if self.info['floating_ips'].get(self.info['vms'][vm]['uuid']): + col_span = col_span + 1 + self.__html_row(vm, row_span, col_span, self.__get_color('vms')) + self.__html_row_close() + + # Plot the networks for each port + self.__html_row_open() + col_span = 1 + for vm in sorted(self.info['vms'].keys()): + floating_ip_info = self.info['floating_ips'].get( + self.info['vms'][vm]['uuid']) + if floating_ip_info: + network = floating_ip_info.get('pool') + self.__html_row('Floating -' + network, row_span, + col_span, self.colors['floating_ip']) + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + tag = get_vlan_tag(self.info, bridge) + ip = get_intf_ip(self.info, bridge) + network = get_ip_network(self.info, vm, ip) + color = self.__get_vlan_color(tag) + if re.search('unknown', network): + color = self.__get_color('error') + self.__html_row(network, row_span, col_span, color) + self.__html_row_close() + + # Plot the IPs for each port + self.__html_row_open() + for vm in sorted(self.info['vms'].keys()): + floating_ip_info = self.info['floating_ips'].get( + self.info['vms'][vm]['uuid']) + if floating_ip_info: + ip = floating_ip_info.get('floating_ip') + self.__html_row(ip, row_span, col_span, self.colors[ + 'floating_ip'], ip.replace('.', '')) + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + tag = get_vlan_tag(self.info, bridge) + ip = get_intf_ip(self.info, bridge) + color = self.__get_vlan_color(tag, ip) + if re.search('x.x.x.x', ip): + color = self.__get_color('error') + self.__html_row(ip, row_span, col_span, + color, ip.replace('.', '')) + self.__html_row_close() + + self.__cluster_close() + pass + + def __plot_linux_bridge(self): + row_span = 1 + col_span = self.__get_total_vm_port_count() + self.__cluster_open('LinuxBridge') + self.__cluster_name('Linux Bridge', col_span) + + # There must be one linuxbridge entity per VM port. + col_span = 1 + # First, the tap devices + self.__html_row_open() + for vm in sorted(self.info['vms'].keys()): + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + if self.info['brctl'].has_key(bridge): + for dev in self.info['brctl'][bridge]['interfaces']: + if re.search('^tap', dev): + tag = get_vlan_tag(self.info, bridge) + self.__html_row(dev, row_span, col_span, + self.__get_vlan_color(tag, dev), dev) + break + self.__html_row_close() + + # Second, the linuxbridges + self.__html_row_open() + for vm in sorted(self.info['vms'].keys()): + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + if self.info['brctl'].has_key(bridge): + tag = get_vlan_tag(self.info, bridge) + self.__html_row(bridge, row_span, col_span, + self.__get_vlan_color(tag, bridge), bridge) + self.__html_row_close() + + # Third, the qvb (one part of eth-pair) devices + self.__html_row_open() + for vm in sorted(self.info['vms'].keys()): + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + if self.info['brctl'].has_key(bridge): + for dev in self.info['brctl'][bridge]['interfaces']: + if re.search('^qvb', dev): + tag = get_vlan_tag(self.info, bridge) + self.__html_row(dev, row_span, col_span, + self.__get_vlan_color(tag, dev), dev) + break + self.__html_row_close() + self.__cluster_close() + pass + + def __plot_br_int_compute(self): + br_int = self.info['bridges']['br-int'] + row_span = 1 + col_span = self.__get_total_vm_port_count() + + self.__cluster_open('compute_br_int') + self.__cluster_name('OVS br_int', col_span) + + # The qvo (pairs with qvb part in linuxbridge) devices + col_span = 1 + self.__html_row_open() + for vm in sorted(self.info['vms'].keys()): + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + qvo_port = bridge.replace('qbr', 'qvo') + if br_int['ports'].has_key(qvo_port): + port_id = '[' + br_int['ports'][qvo_port]['id'] + '] ' + tag = br_int['ports'][qvo_port]['tag'] + self.__html_row(port_id + qvo_port, row_span, col_span, + self.__get_vlan_color(tag, qvo_port), qvo_port) + self.__html_row_close() + + # The vlan tags for each of the devices + col_span = 1 + self.__html_row_open() + for vm in sorted(self.info['vms'].keys()): + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + qvo_port = bridge.replace('qbr', 'qvo') + if br_int['ports'].has_key(qvo_port): + tag = br_int['ports'][qvo_port]['tag'] + self.__html_row('VLAN tag:' + tag, row_span, col_span, + self.__get_vlan_color(tag), qvo_port + 'tag_' + tag) + self.__html_row_close() + + col_span = self.__get_total_vm_port_count() + # Display the patch-tun port + self.__html_row_open() + tun_port = 'patch-tun' + if br_int['ports'].has_key(tun_port): + port_id = '[' + br_int['ports'][tun_port]['id'] + '] ' + self.__html_row(port_id + tun_port, row_span, col_span, + self.__get_color('tun'), tun_port) + else: + self.__html_row(tun_port, row_span, col_span, + self.__get_color('error'), tun_port) + self.__html_row_close() + + self.__cluster_close() + pass + + # TODO + def __plot_br_ex_to_br_int(self): + namespaces = self.info['namespaces'] + + for nms in namespaces.keys(): + if not re.search('^qrouter-', nms): + continue + if not namespaces[nms].has_key('interfaces'): + warning('namespace %s does not have any interface' % nms) + continue + qg_intf = None + for intf in namespaces[nms]['interfaces'].keys(): + if re.search('^qg-', intf): + qg_intf = intf + break + + for intf in namespaces[nms]['interfaces'].keys(): + if re.search('^qr-', intf): + src_tag = 'br_ex:' + qg_intf + dst_tag = 'network_br_int:' + intf + self.__html_edge(src_tag, dst_tag, + self.__get_color('edge')) + pass + + def __plot_br_ex_network(self): + routers = self.info['routers'] + namespaces = self.info['namespaces'] + br_ex = self.info['bridges']['br-ex'] + + row_span = 1 + max_col_span = self.__get_total_port_count(port_type='qg') + + self.__cluster_open('br_ex') + self.__cluster_name('OVS br_ex', max_col_span) + + # Display the router name associated with each qg port + self.__html_row_open() + for router in sorted(routers.keys()): + col_span = self.__get_router_port_count(router, port_type='qg') + self.__html_row(router, row_span, col_span, + self.__get_color('routers'), router) + self.__html_row_close() + + # Display the ips for each qg port + self.__html_row_open() + for router in sorted(routers.keys()): + col_span = self.__get_router_port_count(router, port_type='qg') + qrouter = 'qrouter-' + routers[router]['id'] + for nms in namespaces.keys(): + if re.search('^' + qrouter, nms): + for intf in namespaces[nms]['interfaces'].keys(): + if re.search('^qg-', intf): + ip = namespaces[nms]['interfaces'][intf] + self.__html_row(ip, row_span, col_span, + self.__get_color('routers'), ip) + self.__html_row_close() + + # For each router, print the qg- interfaces + self.__html_row_open() + for router in sorted(routers.keys()): + col_span = self.__get_router_port_count(router, port_type='qg') + qrouter = 'qrouter-' + routers[router]['id'] + for nms in namespaces.keys(): + if re.search('^' + qrouter, nms): + for intf in namespaces[nms]['interfaces'].keys(): + if re.search('^qg-', intf): + port_id = '[' + br_ex['ports'][intf]['id'] + '] ' + self.__html_row(port_id + intf, row_span, col_span, + self.__get_color('routers'), intf) + self.__html_row_close() + + self.__cluster_close() + + def __plot_br_int_network(self): + routers = self.info['routers'] + namespaces = self.info['namespaces'] + br_int = self.info['bridges']['br-int'] + + row_span = 1 + # max_col_span = self.__get_total_port_count(port_type='qr') + \ + # self.__get_total_dhcp_port_count() + max_col_span = self.__get_total_port_count(port_type='qr') * 2 + col_span = max_col_span + + self.__cluster_open('network_br_int') + self.__cluster_name('OVS br_int', col_span) + + # For each router, print the qr- and tap (dhcp) interfaces + temp_info = [] + col_span = 1 + self.__html_row_open() + for router in sorted(routers.keys()): + qrouter = 'qrouter-' + routers[router]['id'] + for nms in namespaces.keys(): + if re.search('^' + qrouter, nms): + for intf in namespaces[nms]['interfaces'].keys(): + if re.search('^qr-', intf): + tag = br_int['ports'][intf]['tag'] + port_id = '[' + br_int['ports'][intf]['id'] + '] ' + color = self.__get_vlan_color(tag, intf) + self.__html_row( + port_id + intf, row_span, col_span, color, intf) + # now plot the corresponding tap interface + (tap_nms, tap) = self.__get_tap_interface(nms, intf) + tag = br_int['ports'][tap]['tag'] + color = self.__get_vlan_color(tag, tap) + port_id = '[' + br_int['ports'][tap]['id'] + '] ' + self.__html_row( + port_id + tap, row_span, col_span, color, tap) + + a = { + 'qr_intf': intf, + 'tap_intf': tap, + 'qr_ip': namespaces[nms]['interfaces'][intf], + 'tap_ip': namespaces[tap_nms]['interfaces'][tap], + } + temp_info.append(a) + self.__html_row_close() + + # The vlan tags for each of the qr- and tap ports + col_span = 1 + self.__html_row_open() + for entry in temp_info: + qr_intf = entry['qr_intf'] + tap_intf = entry['tap_intf'] + + tag = br_int['ports'][qr_intf]['tag'] + self.__html_row('VLAN tag:' + tag, row_span, col_span, + self.__get_vlan_color(tag), qr_intf + 'tag_' + tag) + + tag = br_int['ports'][tap_intf]['tag'] + self.__html_row('VLAN tag:' + tag, row_span, col_span, + self.__get_vlan_color(tag), tap_intf + 'tag_' + tag) + + self.__html_row_close() + + # Display the ips with each of the qr- and tap ports + self.__html_row_open() + for entry in temp_info: + qr_intf = entry['qr_intf'] + qr_ip = entry['qr_ip'] + tap_intf = entry['tap_intf'] + tap_ip = entry['tap_ip'] + + tag = br_int['ports'][qr_intf]['tag'] + self.__html_row(qr_ip, row_span, col_span, + self.__get_vlan_color(tag), + qr_intf + qr_ip) + + tag = br_int['ports'][tap_intf]['tag'] + self.__html_row(tap_ip, row_span, col_span, + self.__get_vlan_color(tag), + tap_intf + tap_ip) + + self.__html_row_close() + + # The network names (private1, private2, etc.) + col_span = 2 + self.__html_row_open() + for entry in temp_info: + network_name = self.__get_network_name(entry['qr_ip']) + tag = br_int['ports'][entry['qr_intf']]['tag'] + self.__html_row(network_name, row_span, col_span, + self.__get_vlan_color(tag), network_name) + self.__html_row_close() + + # The routers in the system + self.__html_row_open() + for router in sorted(self.info['routers'].keys()): + # For each qr port that is also a tap port (for dhcp) + col_span = self.__get_router_port_count(router, port_type='qr') * 2 + self.__html_row(router, row_span, col_span, + self.__get_color('routers'), router) + self.__html_row_close() + + # Display the patch-tun port + self.__html_row_open() + tun_port = 'patch-tun' + debug('max_col_span 2: ' + str(max_col_span)) + if br_int['ports'].has_key(tun_port): + port_id = '[' + br_int['ports'][tun_port]['id'] + '] ' + self.__html_row(port_id + tun_port, row_span, max_col_span, + self.__get_color('tun'), tun_port) + else: + self.__html_row(tun_port, row_span, max_col_span, + self.__get_color('error'), tun_port) + self.__html_row_close() + + self.__cluster_close() + + def __plot_br_tun(self, tag): + br_tun = self.info['bridges']['br-tun'] + row_span = 1 + col_span = self.__get_total_vm_port_count() + self.__cluster_open(tag + '_br_tun') + self.__cluster_name('OVS br_tun', col_span) + + # Display the patch-int port + col_span = self.__get_total_vm_port_count() + self.__html_row_open() + int_port = 'patch-int' + if br_tun['ports'].has_key(int_port): + port_id = '[' + br_tun['ports'][int_port]['id'] + '] ' + self.__html_row(port_id + int_port, row_span, + col_span, self.__get_color('int'), int_port) + else: + self.__html_row(int_port, row_span, col_span, + self.__get_color('error'), int_port) + self.__html_row_close() + + self.__cluster_close() + + def __plot_vms_to_linuxbridge(self): + brctl = self.info['brctl'] + for vm in sorted(self.info['vms'].keys()): + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + ip = get_intf_ip(self.info, bridge) + if brctl.has_key(bridge): + for dev in brctl[bridge]['interfaces']: + if re.search('^tap', dev): + src_tag = 'VMs:' + ip.replace('.', '') + dst_tag = 'LinuxBridge:' + dev + color = self.__get_edge_color(src_tag, dst_tag) + self.__html_edge(src_tag, dst_tag, color) + break + pass + + def __plot_linuxbridge_to_br_int(self): + brctl = self.info['brctl'] + br_int = self.info['bridges']['br-int'] + + for vm in sorted(self.info['vms'].keys()): + for bridge in sorted(self.info['vms'][vm]['src_bridge']): + if brctl.has_key(bridge): + for dev in brctl[bridge]['interfaces']: + if re.search('^qvb', dev): + qvo_port = bridge.replace('qbr', 'qvo') + if br_int['ports'].has_key(qvo_port): + src_tag = 'LinuxBridge:' + dev + dst_tag = 'compute_br_int:' + qvo_port + color = self.__get_edge_color(src_tag, dst_tag) + self.__html_edge(src_tag, dst_tag, color) + break + pass + + def __plot_br_int_to_br_tun(self, tag): + br_int = self.info['bridges']['br-int']['ports'] + br_tun = self.info['bridges']['br-tun']['ports'] + + tun_port = 'patch-tun' + int_port = 'patch-int' + if br_int.has_key(tun_port) and br_tun.has_key(int_port): + tun_peer = br_int[tun_port]['interfaces'][ + tun_port].get('options', None) + int_peer = br_tun[int_port]['interfaces'][ + int_port].get('options', None) + if tun_peer and int_peer: + if re.search('peer=' + int_port, tun_peer) and \ + re.search('peer=' + tun_port, int_peer): + src_tag = tag + '_br_int:' + tun_port + dst_tag = tag + '_br_tun:' + int_port + self.__html_edge(src_tag, dst_tag, + self.__get_color('edge')) + return + pass + + def plot_combined(self): + self.outfile = open(self.combined_dot_file, 'w') + sys.stdout = self.outfile + + tag = 'DON' + self.__digraph_open(tag) + + self.__cluster_open_plain('DONComputeNode') + self.plot_compute_node() + self.__cluster_close_plain() + + self.__cluster_open_plain('DONNetworkNode') + self.plot_network_node() + self.__cluster_close_plain() + + self.__digraph_close() + + self.outfile.close() + sys.stdout = sys.__stdout__ + + def plot_compute_node(self): + tag = 'compute' + redirected = False + if sys.stdout == sys.__stdout__: + self.outfile = open(self.compute_dot_file, "w") + sys.stdout = self.outfile + redirected = True + self.__digraph_open(tag) + + # Title + self.__cluster_open('ComputeNode', 'red') + self.__cluster_name('Compute Node', 1, 'yellow') + self.__cluster_close() + + # Plot nodes + self.__cluster_open_plain('Nova') + self.__plot_vms() + self.__plot_linux_bridge() + self.__cluster_close_plain() + + self.__cluster_open_plain('OVS') + self.__plot_br_int_compute() + self.__plot_br_tun(tag) + self.__cluster_close_plain() + + # Plot edges + self.__plot_title_edges(tag) + self.__plot_vms_to_linuxbridge() + self.__plot_linuxbridge_to_br_int() + self.__plot_br_int_to_br_tun(tag) + + if redirected: + self.__digraph_close() + self.outfile.close() + sys.stdout = sys.__stdout__ + + def generate_compute_svg(self): + cmd = ['/usr/bin/dot', '-Tsvg', self.compute_dot_file, + '-o', self.compute_svg_file] + debug(pprint.pformat(cmd)) + subprocess.call(cmd) + debug('Done generating compute SVG') + + def plot_network_node(self): + tag = 'network' + redirected = False + if sys.stdout == sys.__stdout__: + self.outfile = open(self.network_dot_file, "w") + sys.stdout = self.outfile + redirected = True + self.__digraph_open(tag) + + self.__cluster_open('NetworkNode', 'red') + self.__cluster_name('Network Node', 1, 'yellow') + self.__cluster_close() + + # Plot nodes + self.__cluster_open_plain('OVS') + self.__plot_br_ex_network() + self.__plot_br_int_network() + self.__plot_br_tun(tag) + self.__cluster_close_plain() + + # Plot edges + self.__plot_title_edges(tag) + self.__plot_br_int_to_br_tun(tag) + self.__plot_br_ex_to_br_int() + + if redirected: + self.__digraph_close() + self.outfile.close() + sys.stdout = sys.__stdout__ + + def generate_network_svg(self): + cmd = ['/usr/bin/dot', '-Tsvg', self.network_dot_file, + '-o', self.network_svg_file] + debug(pprint.pformat(cmd)) + subprocess.call(cmd) + debug('Done generating network SVG') + + def generate_combined_svg(self): + cmd = ['/usr/bin/dot', '-Tsvg', self.combined_dot_file, + '-o', self.combined_svg_file] + debug(pprint.pformat(cmd)) + subprocess.call(cmd) + debug('Done generating network SVG') + + +def check_args(): + parser = argparse.ArgumentParser(description='Plot the compute node network internals', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--debug', dest='debug', + help='Enable debugging', default=True, action='store_true') + parser.add_argument('--info_file', dest='info_file', + help='Info is read in JSON format in this file', default="don.json", type=str) + 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) + parser.add_argument('--network_file', dest='network_file', + help='[network_file].dot and [network_file].svg will be generated for network node', default="network", type=str) + parser.add_argument('--combined_file', dest='combined_file', + help='[combined_file].dot and [combined_file].svg will be generated', default="don", type=str) + parser.add_argument('--highlight_file', dest='highlight_file', + help='pass and fail node are specified in this file', default=None, type=str) + + args = parser.parse_args() + + settings['debug'] = args.debug + settings['info_file'] = args.info_file + settings['compute_dot_file'] = args.compute_file + '.dot' + settings['compute_svg_file'] = args.compute_file + '.svg' + settings['network_dot_file'] = args.network_file + '.dot' + settings['network_svg_file'] = args.network_file + '.svg' + settings['combined_dot_file'] = args.combined_file + '.dot' + settings['combined_svg_file'] = args.combined_file + '.svg' + settings['highlight_file'] = args.highlight_file + + +def main(): + check_args() + plotter = DotGenerator(settings['info_file'], + settings['compute_dot_file'], + settings['compute_svg_file'], + settings['network_dot_file'], + settings['network_svg_file'], + settings['combined_dot_file'], + settings['combined_svg_file'], + settings['highlight_file'], + ) + if not settings['highlight_file']: + plotter.plot_compute_node() + plotter.generate_compute_svg() + + plotter.plot_network_node() + plotter.generate_network_svg() + + plotter.plot_combined() + plotter.generate_combined_svg() + +if __name__ == "__main__": + main() diff --git a/openstack_dashboard/don/ovs/run_nms_cmd.py b/openstack_dashboard/don/ovs/run_nms_cmd.py new file mode 100644 index 0000000..2667090 --- /dev/null +++ b/openstack_dashboard/don/ovs/run_nms_cmd.py @@ -0,0 +1,92 @@ +# +# 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" +# +import argparse +import json +from common import connect_to_box, ssh_cmd +from common import settings + + +params = {} + +output_dict = { + 'comment': None, + 'pass': None, + 'command_list': [], + 'errors': [], +} + + +def run_nms_cmd(args): + global output_dict + host_ip = args['host_ip'] + username = args['username'] + passwd = args['passwd'] + cmd_to_run = args['cmd'] + + result = True + cmd_dict = {} + try: + ssh = connect_to_box(host_ip, username, passwd) + cmd_dict['cmd'] = 'ssh %s with provided username and passwd' % host_ip + if not ssh: + cmd_dict['output'] = 'Could not ssh to ' + host_ip + cmd_dict['pass'] = False + output_dict['command_list'].append(cmd_dict) + return False + else: + cmd_dict['pass'] = True + output_dict['command_list'].append(cmd_dict) + cmd_dict = {} + cmd = cmd_to_run + output = ssh_cmd(ssh, cmd).split('\n') + cmd_dict['cmd'] = cmd + cmd_dict['output'] = output + except (KeyboardInterrupt, SystemExit): + print '\nkeyboardinterrupt caught (again)' + print '\n...Program Stopped Manually!' + result = False + raise + cmd_dict['pass'] = result + output_dict['command_list'].append(cmd_dict) + return result + + +def check_args(): + global params + + parser = argparse.ArgumentParser( + description='Run command from inside nms', formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--debug', dest='debug', + help='Enable debugging', default=False, action='store_true') + parser.add_argument('--host_ip', dest='host_ip', + help='IP where the command will be run', type=str, required=True) + parser.add_argument('--username', dest='username', + help='SSH login username (required)', type=str, required=True) + parser.add_argument('--passwd', dest='passwd', + help='SSH login passwd (required)', type=str, required=True) + parser.add_argument('--cmd', dest='cmd', + help='cmd to be run', type=str, required=True) + args = parser.parse_args() + + settings['debug'] = args.debug + params['host_ip'] = args.host_ip + params['username'] = args.username + params['passwd'] = args.passwd + params['cmd'] = args.cmd + + +def main(): + global output_dict + + check_args() + + output_dict['pass'] = run_nms_cmd(params) + + a = json.dumps(output_dict, sort_keys=True, indent=4) + print a + +if __name__ == "__main__": + main() diff --git a/openstack_dashboard/don/ovs/static/CollapsibleLists.js b/openstack_dashboard/don/ovs/static/CollapsibleLists.js new file mode 100644 index 0000000..15e3e09 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/CollapsibleLists.js @@ -0,0 +1,152 @@ +/* + +CollapsibleLists.js + +An object allowing lists to dynamically expand and collapse + +Created by Stephen Morley - http://code.stephenmorley.org/ - and released under +the terms of the CC0 1.0 Universal legal code: + +http://creativecommons.org/publicdomain/zero/1.0/legalcode + +*/ + +// create the CollapsibleLists object +var CollapsibleLists = + new function(){ + + /* Makes all lists with the class 'collapsibleList' collapsible. The + * parameter is: + * + * doNotRecurse - true if sub-lists should not be made collapsible + */ + this.apply = function(doNotRecurse){ + + // loop over the unordered lists + var uls = document.getElementsByTagName('ul'); + for (var index = 0; index < uls.length; index ++){ + + // check whether this list should be made collapsible + if (uls[index].className.match(/(^| )collapsibleList( |$)/)){ + + // make this list collapsible + this.applyTo(uls[index], true); + + // check whether sub-lists should also be made collapsible + if (!doNotRecurse){ + + // add the collapsibleList class to the sub-lists + var subUls = uls[index].getElementsByTagName('ul'); + for (var subIndex = 0; subIndex < subUls.length; subIndex ++){ + subUls[subIndex].className += ' collapsibleList'; + } + + } + + } + + } + + }; + + /* Makes the specified list collapsible. The parameters are: + * + * node - the list element + * doNotRecurse - true if sub-lists should not be made collapsible + */ + this.applyTo = function(node, doNotRecurse){ + + // loop over the list items within this node + var lis = node.getElementsByTagName('li'); + for (var index = 0; index < lis.length; index ++){ + + // check whether this list item should be collapsible + if (!doNotRecurse || node == lis[index].parentNode){ + + // prevent text from being selected unintentionally + if (lis[index].addEventListener){ + lis[index].addEventListener( + 'mousedown', function (e){ e.preventDefault(); }, false); + }else{ + lis[index].attachEvent( + 'onselectstart', function(){ event.returnValue = false; }); + } + + // add the click listener + if (lis[index].addEventListener){ + lis[index].addEventListener( + 'click', createClickListener(lis[index]), false); + }else{ + lis[index].attachEvent( + 'onclick', createClickListener(lis[index])); + } + + // close the unordered lists within this list item + toggle(lis[index]); + + } + + } + + }; + + /* Returns a function that toggles the display status of any unordered + * list elements within the specified node. The parameter is: + * + * node - the node containing the unordered list elements + */ + function createClickListener(node){ + + // return the function + return function(e){ + + // ensure the event object is defined + if (!e) e = window.event; + + // find the list item containing the target of the event + var li = (e.target ? e.target : e.srcElement); + while (li.nodeName != 'LI') li = li.parentNode; + + // toggle the state of the node if it was the target of the event + if (li == node) toggle(node); + + }; + + } + + /* Opens or closes the unordered list elements directly within the + * specified node. The parameter is: + * + * node - the node containing the unordered list elements + */ + function toggle(node){ + + // determine whether to open or close the unordered lists + var open = node.className.match(/(^| )collapsibleListClosed( |$)/); + + // loop over the unordered list elements with the node + var uls = node.getElementsByTagName('ul'); + for (var index = 0; index < uls.length; index ++){ + + // find the parent list item of this unordered list + var li = uls[index]; + while (li.nodeName != 'LI') li = li.parentNode; + + // style the unordered list if it is directly within this node + if (li == node) uls[index].style.display = (open ? 'block' : 'none'); + + } + + // remove the current class from the node + node.className = + node.className.replace( + /(^| )collapsibleList(Open|Closed)( |$)/, ''); + + // if the node contains unordered lists, set its class + if (uls.length > 0){ + node.className += ' collapsibleList' + (open ? 'Open' : 'Closed'); + } + + } + + }(); diff --git a/openstack_dashboard/don/ovs/static/button-closed.png b/openstack_dashboard/don/ovs/static/button-closed.png new file mode 100644 index 0000000..417eb2f Binary files /dev/null and b/openstack_dashboard/don/ovs/static/button-closed.png differ diff --git a/openstack_dashboard/don/ovs/static/button-open.png b/openstack_dashboard/don/ovs/static/button-open.png new file mode 100644 index 0000000..ac4a6ef Binary files /dev/null and b/openstack_dashboard/don/ovs/static/button-open.png differ diff --git a/openstack_dashboard/don/ovs/static/button.png b/openstack_dashboard/don/ovs/static/button.png new file mode 100644 index 0000000..631d734 Binary files /dev/null and b/openstack_dashboard/don/ovs/static/button.png differ diff --git a/openstack_dashboard/don/ovs/static/compute.dot b/openstack_dashboard/don/ovs/static/compute.dot new file mode 100644 index 0000000..5d9ddfa --- /dev/null +++ b/openstack_dashboard/don/ovs/static/compute.dot @@ -0,0 +1,21 @@ +digraph DON_compute { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled diff --git a/openstack_dashboard/don/ovs/static/compute.svg b/openstack_dashboard/don/ovs/static/compute.svg new file mode 100644 index 0000000..5c253f5 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/compute.svg @@ -0,0 +1,111 @@ + + + + + + +DON_compute + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +vm1 + + +vm2 + + +private1 + + +private1 + + +10.10.0.3 + + +10.10.0.4 + + + +LinuxBridge +LinuxBridge + + +compute_br_int + + +OVS br_int + + +[6] qvo8aa60600-7b + + +[7] qvo71ac5bef-7c + + +VLAN tag:2 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvb8aa60600_7b:s->compute_br_int:qvo8aa60600_7b:n + + + +LinuxBridge:qvb71ac5bef_7c:s->compute_br_int:qvo71ac5bef_7c:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/ovs/static/dashboard/ovs/view.html b/openstack_dashboard/don/ovs/static/dashboard/ovs/view.html new file mode 100644 index 0000000..e2b9fd1 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/dashboard/ovs/view.html @@ -0,0 +1,31 @@ + + + + DON: Internal View + + + +
+ + + + + + + + +
+ DON: Internal View +
+ +     + +
+
+ + + diff --git a/openstack_dashboard/don/ovs/static/don.css b/openstack_dashboard/don/ovs/static/don.css new file mode 100644 index 0000000..c38967e --- /dev/null +++ b/openstack_dashboard/don/ovs/static/don.css @@ -0,0 +1,212 @@ +body { + font-family: "Courier New", Helvetica, "Times New Roman", + Times, serif; + color: black; + background-color: #ffffff +} + +pre { + width: 500px; /* specify width */ + white-space: pre-wrap; /* CSS3 browsers */ + white-space: -moz-pre-wrap !important; /* 1999+ Mozilla */ + white-space: -pre-wrap; /* Opera 4 thru 6 */ + white-space: -o-pre-wrap; /* Opera 7 and up */ + /* word-wrap: break-word; */ /* IE 5.5+ and up */ + font-family: "Courier New", monospace; + line-height: 1.6em; + margin: 1.7em 0 1.7em 0.3em; + font-size: 12px; + /* overflow-x: auto; */ /* Firefox 2 only */ + width: 90%; /* only if needed */ +} + +h2 { + font-family: Georgia, Helvetica,san-Serif; + font-weight: bold; + font-size: 20pt; + /*color:#fffff0;*/ + color: #2A4E68; + padding: 5px; + margin: 20px 0px 0px 0px; + /*background:#3399ff;*/ + /*border-radius: 20px;*/ + /* + font-family: "Helvetica Neue", Helvetica, Geneva, Arial, + SunSans-Regular, sans-serif; + padding: 0px; + font-weight: normal; + color: #2A4E68; + border-top: 1px solid black; + #border-bottom: 0.5px solid black; + #background: #ff9933; + #border-radius: 10px; + width:750px; + */ +} + +h3 { + font-family: "Helvetica Neue", Helvetica, Geneva, Arial, + SunSans-Regular, sans-serif; + margin: 0px 0px 0px 0px; + padding: 5px; + font-weight: normal; + font-style: italic; + color: #ff9933; + border-bottom: 0.5px solid black; + width:750px; /*Change this to whatever value that you want*/ +} + + + + +pre.pass { + border-left: 2px solid #009900; +} +pre.fail { + border-left: 2px solid #f00000; +} + +font.pass {color:#00cc00; text-decoration:none;} +font.fail {color:#f00000; font-weight:bold; text-decoration:none;} + +.collapsibleList li{ + list-style-image:url('button.png'); + cursor:auto; + margin:0 0 5px 0; +} + +li.collapsibleListOpen{ + list-style-image:url('button-open.png'); + cursor:pointer; + margin:0 0 5px 0; +} + +li.collapsibleListClosed{ + list-style-image:url('button-closed.png'); + cursor:pointer; +} + +.message { + position: absolute; + display: none; + border: 0px solid red; + width:80px; + height:80px; + padding: 0px; + float: top; + font-size: 8pt; +} + +#main { + width:1000px; + float:left; + border: 0px solid red; +} + +#internal { + width:1800px; + float:left; + border: 0px solid red; +} + + + +table +{ + font-family: Helvetica,san-Serif; + font-weight: normal; + font-size: large; + border: 0px solid blue; + padding: 0px; + border-spacing:0px; + border-collapse:collapse; +} + +th { + font-family: Georgia, Helvetica,san-Serif; + font-weight: bold; + font-size: 20pt; + /*color:#fffff0;*/ + color: #2A4E68; + padding: 5px; + /*background:#3399ff;*/ + /*border-radius: 20px;*/ +} + +tr { + font-family: Helvetica,san-Serif; + font-weight: normal; + font-size: large; + padding: 0px; + vertical-align: top; +} + +td { + font-family: Helvetica,san-Serif; + font-weight: normal; + font-size: large; + vertical-align: middle; + padding: 5px; + + border-style: solid; + border-width: 0px; + border-color: black; +} + +td.clickme { + font-family: Helvetica,san-Serif; + font-weight: normal; + font-size: large; + vertical-align: bottom; + padding: 20px; + /*background:#e5e5e5;*/ + background:#ffffff; + border-radius: 15px; + border-style: solid; + border-width: 0px; + border-color: black; + text-align: center; + } + +.myButton { + -moz-box-shadow: 0px 10px 14px -7px #3e7327; + -webkit-box-shadow: 0px 10px 14px -7px #3e7327; + box-shadow: 0px 10px 14px -7px #3e7327; + background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #77b55a), color-stop(1, #72b352)); + background:-moz-linear-gradient(top, #77b55a 5%, #72b352 100%); + background:-webkit-linear-gradient(top, #77b55a 5%, #72b352 100%); + background:-o-linear-gradient(top, #77b55a 5%, #72b352 100%); + background:-ms-linear-gradient(top, #77b55a 5%, #72b352 100%); + background:linear-gradient(to bottom, #77b55a 5%, #72b352 100%); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#77b55a', endColorstr='#72b352',GradientType=0); + background-color:#77b55a; + -moz-border-radius:4px; + -webkit-border-radius:4px; + border-radius:4px; + border:1px solid #4b8f29; + display:inline-block; + cursor:pointer; + color:#ffffff; + font-family:Arial; + font-size:20px; + font-weight:bold; + padding:6px 12px; + text-decoration:none; + text-shadow:0px 1px 0px #5b8a3c; +} +.myButton:hover { + background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #72b352), color-stop(1, #77b55a)); + background:-moz-linear-gradient(top, #72b352 5%, #77b55a 100%); + background:-webkit-linear-gradient(top, #72b352 5%, #77b55a 100%); + background:-o-linear-gradient(top, #72b352 5%, #77b55a 100%); + background:-ms-linear-gradient(top, #72b352 5%, #77b55a 100%); + background:linear-gradient(to bottom, #72b352 5%, #77b55a 100%); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#72b352', endColorstr='#77b55a',GradientType=0); + background-color:#72b352; +} +.myButton:active { + position:relative; + top:1px; +} + + diff --git a/openstack_dashboard/don/ovs/static/don.dot b/openstack_dashboard/don/ovs/static/don.dot new file mode 100644 index 0000000..edcc4aa --- /dev/null +++ b/openstack_dashboard/don/ovs/static/don.dot @@ -0,0 +1,198 @@ +digraph DON_DON { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_DONComputeNode { +style=filled +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled +subgraph cluster_VMs { +VMs [ shape = plaintext, label = < + + + + + + + + + + + + + + + + +
VMs
vm1vm2
private1private1
10.10.0.310.10.0.4
>]; + +} + +subgraph cluster_LinuxBridge { +LinuxBridge [ shape = plaintext, label = < + + + + + + + + + + + + + + +
Linux Bridge
qbr8aa60600-7bqbr71ac5bef-7c
qvb8aa60600-7bqvb71ac5bef-7c
>]; + +} + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_compute_br_int { +compute_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + +
OVS br_int
[6] qvo8aa60600-7b[7] qvo71ac5bef-7c
VLAN tag:2VLAN tag:2
[1] patch-tun
>]; + +} + +subgraph cluster_compute_br_tun { +compute_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +ComputeNode:s -> VMs:n [color = "#0066cc", penwidth = "4", style="invis"] +LinuxBridge:qvb8aa60600_7b:s -> compute_br_int:qvo8aa60600_7b:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb71ac5bef_7c:s -> compute_br_int:qvo71ac5bef_7c:n [color = "#0066cc", penwidth = "4"] +compute_br_int:patch_tun:s -> compute_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +} + +subgraph cluster_DONNetworkNode { +style=filled +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + +
OVS br_ex
router1
172.24.4.3/24
[2] qg-757bf552-73
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-43b83157-3b[5] tap59f90a3b-f5
VLAN tag:2VLAN tag:2
10.10.0.1/2410.10.0.2/24
private1
router1
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#0066cc", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_757bf552_73:s -> network_br_int:qr_43b83157_3b:n [color = "#0066cc", penwidth = "4"] +} + + +} + diff --git a/openstack_dashboard/don/ovs/static/don.html b/openstack_dashboard/don/ovs/static/don.html new file mode 100644 index 0000000..bd8beda --- /dev/null +++ b/openstack_dashboard/don/ovs/static/don.html @@ -0,0 +1,31 @@ + + + + DON: Diagnosing OpenStack Networking + + + +
+ + + + + + +
+ DON: Diagnosing OpenStack Networking +
+ +
+
+ + diff --git a/openstack_dashboard/don/ovs/static/don.report.html b/openstack_dashboard/don/ovs/static/don.report.html new file mode 100644 index 0000000..5322160 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/don.report.html @@ -0,0 +1,144 @@ + + + + +DON: Analysis Results + + +

OVS Test Results

+

OVS test between all pairs of ports using the same tag in br-int

+ +OVERALL RESULT: +

Ping Test Results

+

Ping test between all pairs of VMs

+ +OVERALL RESULT: + + diff --git a/openstack_dashboard/don/ovs/static/don.svg b/openstack_dashboard/don/ovs/static/don.svg new file mode 100644 index 0000000..7c024a7 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/don.svg @@ -0,0 +1,204 @@ + + + + + + +DON_DON + +cluster_DONComputeNode + + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + +cluster_DONNetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + +cluster_NetworkNode + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +vm1 + + +vm2 + + +private1 + + +private1 + + +10.10.0.3 + + +10.10.0.4 + + + +LinuxBridge +LinuxBridge + + +compute_br_int + + +OVS br_int + + +[6] qvo8aa60600-7b + + +[7] qvo71ac5bef-7c + + +VLAN tag:2 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvb8aa60600_7b:s->compute_br_int:qvo8aa60600_7b:n + + + +LinuxBridge:qvb71ac5bef_7c:s->compute_br_int:qvo71ac5bef_7c:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +172.24.4.3/24 + + +[2] qg-757bf552-73 + + + +network_br_int + + +OVS br_int + + +[8] qr-43b83157-3b + + +[5] tap59f90a3b-f5 + + +VLAN tag:2 + + +VLAN tag:2 + + +10.10.0.1/24 + + +10.10.0.2/24 + + +private1 + + +router1 + + +[1] patch-tun + + +br_ex:qg_757bf552_73:s->network_br_int:qr_43b83157_3b:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/ovs/static/don_analysis.png b/openstack_dashboard/don/ovs/static/don_analysis.png new file mode 100644 index 0000000..8d48b61 Binary files /dev/null and b/openstack_dashboard/don/ovs/static/don_analysis.png differ diff --git a/openstack_dashboard/don/ovs/static/don_internal.png b/openstack_dashboard/don/ovs/static/don_internal.png new file mode 100644 index 0000000..be3c0e0 Binary files /dev/null and b/openstack_dashboard/don/ovs/static/don_internal.png differ diff --git a/openstack_dashboard/don/ovs/static/don_ping_notworking.png b/openstack_dashboard/don/ovs/static/don_ping_notworking.png new file mode 100644 index 0000000..b7dfcfd Binary files /dev/null and b/openstack_dashboard/don/ovs/static/don_ping_notworking.png differ diff --git a/openstack_dashboard/don/ovs/static/don_test.dot b/openstack_dashboard/don/ovs/static/don_test.dot new file mode 100644 index 0000000..679745e --- /dev/null +++ b/openstack_dashboard/don/ovs/static/don_test.dot @@ -0,0 +1,278 @@ +digraph DON_DON { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_DONComputeNode { +style=filled +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled +subgraph cluster_VMs { +VMs [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VMs
VM1-1VM1-2VM3-1VM3-2VM4
private1private1private2private2private2publicprivate1
10.0.2.310.0.2.410.0.3.310.0.3.410.0.3.6172.24.4.510.0.2.6
>]; + +} + +subgraph cluster_LinuxBridge { +LinuxBridge [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Linux Bridge
tapba7460ea-c9tapa1eba09e-99tap95772001-99tapb0cb94f0-97tap58b49f9d-1dtap68484e46-cftap8f35a39f-2a
qbrba7460ea-c9qbra1eba09e-99qbr95772001-99qbrb0cb94f0-97qbr58b49f9d-1dqbr68484e46-cfqbr8f35a39f-2a
qvbba7460ea-c9qvba1eba09e-99qvb95772001-99qvbb0cb94f0-97qvb58b49f9d-1dqvb68484e46-cfqvb8f35a39f-2a
>]; + +} + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_compute_br_int { +compute_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[28] qvoba7460ea-c9[29] qvoa1eba09e-99[30] qvo95772001-99[31] qvob0cb94f0-97[33] qvo58b49f9d-1d[34] qvo68484e46-cf[32] qvo8f35a39f-2a
VLAN tag:2VLAN tag:2VLAN tag:3VLAN tag:3VLAN tag:3VLAN tag:4VLAN tag:2
[19] patch-tun
>]; + +} + +subgraph cluster_compute_br_tun { +compute_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +ComputeNode:s -> VMs:n [color = "#0066cc", penwidth = "4", style="invis"] +VMs:10023:s -> LinuxBridge:tapba7460ea_c9:n [color = "#0066cc", penwidth = "4"] +VMs:10024:s -> LinuxBridge:tapa1eba09e_99:n [color = "#0066cc", penwidth = "4"] +VMs:10033:s -> LinuxBridge:tap95772001_99:n [color = "#0066cc", penwidth = "4"] +VMs:10034:s -> LinuxBridge:tapb0cb94f0_97:n [color = "#0066cc", penwidth = "4"] +VMs:10036:s -> LinuxBridge:tap58b49f9d_1d:n [color = "#0066cc", penwidth = "4"] +VMs:1722445:s -> LinuxBridge:tap68484e46_cf:n [color = "#0066cc", penwidth = "4"] +VMs:10026:s -> LinuxBridge:tap8f35a39f_2a:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvbba7460ea_c9:s -> compute_br_int:qvoba7460ea_c9:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvba1eba09e_99:s -> compute_br_int:qvoa1eba09e_99:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb95772001_99:s -> compute_br_int:qvo95772001_99:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvbb0cb94f0_97:s -> compute_br_int:qvob0cb94f0_97:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb58b49f9d_1d:s -> compute_br_int:qvo58b49f9d_1d:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb68484e46_cf:s -> compute_br_int:qvo68484e46_cf:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb8f35a39f_2a:s -> compute_br_int:qvo8f35a39f_2a:n [color = "#0066cc", penwidth = "4"] +compute_br_int:patch_tun:s -> compute_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +} + +subgraph cluster_DONNetworkNode { +style=filled +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + + + + +
OVS br_ex
router1router2
172.24.4.3/24172.24.4.4/24
[6] qg-e6c19c2c-8c[7] qg-718e237a-3a
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[25] qr-04542d8f-13[23] tapfd625661-7e[24] qr-e503ba9c-89[22] tapb9083cd7-6f[27] qr-fb2585a7-bd[23] tapfd625661-7e[26] qr-5de9034a-db[22] tapb9083cd7-6f
VLAN tag:3VLAN tag:3VLAN tag:2VLAN tag:2VLAN tag:3VLAN tag:3VLAN tag:2VLAN tag:2
10.0.3.1/2410.0.3.2/2410.0.2.1/2410.0.2.2/2410.0.3.5/2410.0.3.2/2410.0.2.5/2410.0.2.2/24
private2private1private2private1
router1router2
[19] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#0066cc", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_e6c19c2c_8c:s -> network_br_int:qr_04542d8f_13:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_e6c19c2c_8c:s -> network_br_int:qr_e503ba9c_89:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_718e237a_3a:s -> network_br_int:qr_fb2585a7_bd:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_718e237a_3a:s -> network_br_int:qr_5de9034a_db:n [color = "#0066cc", penwidth = "4"] +} + + +} + diff --git a/openstack_dashboard/don/ovs/static/don_test.svg b/openstack_dashboard/don/ovs/static/don_test.svg new file mode 100644 index 0000000..6e41bf8 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/don_test.svg @@ -0,0 +1,473 @@ + + + + + + +DON_DON + +cluster_DONComputeNode + + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + +cluster_DONNetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + +cluster_NetworkNode + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +VM1-1 + + +VM1-2 + + +VM3-1 + + +VM3-2 + + +VM4 + + +private1 + + +private1 + + +private2 + + +private2 + + +private2 + + +public + + +private1 + + +10.0.2.3 + + +10.0.2.4 + + +10.0.3.3 + + +10.0.3.4 + + +10.0.3.6 + + +172.24.4.5 + + +10.0.2.6 + + + +LinuxBridge + + +Linux Bridge + + +tapba7460ea-c9 + + +tapa1eba09e-99 + + +tap95772001-99 + + +tapb0cb94f0-97 + + +tap58b49f9d-1d + + +tap68484e46-cf + + +tap8f35a39f-2a + + +qbrba7460ea-c9 + + +qbra1eba09e-99 + + +qbr95772001-99 + + +qbrb0cb94f0-97 + + +qbr58b49f9d-1d + + +qbr68484e46-cf + + +qbr8f35a39f-2a + + +qvbba7460ea-c9 + + +qvba1eba09e-99 + + +qvb95772001-99 + + +qvbb0cb94f0-97 + + +qvb58b49f9d-1d + + +qvb68484e46-cf + + +qvb8f35a39f-2a + + +VMs:10023:s->LinuxBridge:tapba7460ea_c9:n + + + +VMs:10024:s->LinuxBridge:tapa1eba09e_99:n + + + +VMs:10033:s->LinuxBridge:tap95772001_99:n + + + +VMs:10034:s->LinuxBridge:tapb0cb94f0_97:n + + + +VMs:10036:s->LinuxBridge:tap58b49f9d_1d:n + + + +VMs:1722445:s->LinuxBridge:tap68484e46_cf:n + + + +VMs:10026:s->LinuxBridge:tap8f35a39f_2a:n + + + +compute_br_int + + +OVS br_int + + +[28] qvoba7460ea-c9 + + +[29] qvoa1eba09e-99 + + +[30] qvo95772001-99 + + +[31] qvob0cb94f0-97 + + +[33] qvo58b49f9d-1d + + +[34] qvo68484e46-cf + + +[32] qvo8f35a39f-2a + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:4 + + +VLAN tag:2 + + +[19] patch-tun + + +LinuxBridge:qvbba7460ea_c9:s->compute_br_int:qvoba7460ea_c9:n + + + +LinuxBridge:qvba1eba09e_99:s->compute_br_int:qvoa1eba09e_99:n + + + +LinuxBridge:qvb95772001_99:s->compute_br_int:qvo95772001_99:n + + + +LinuxBridge:qvbb0cb94f0_97:s->compute_br_int:qvob0cb94f0_97:n + + + +LinuxBridge:qvb58b49f9d_1d:s->compute_br_int:qvo58b49f9d_1d:n + + + +LinuxBridge:qvb68484e46_cf:s->compute_br_int:qvo68484e46_cf:n + + + +LinuxBridge:qvb8f35a39f_2a:s->compute_br_int:qvo8f35a39f_2a:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +router2 + + +172.24.4.3/24 + + +172.24.4.4/24 + + +[6] qg-e6c19c2c-8c + + +[7] qg-718e237a-3a + + + +network_br_int + + +OVS br_int + + +[25] qr-04542d8f-13 + + +[23] tapfd625661-7e + + +[24] qr-e503ba9c-89 + + +[22] tapb9083cd7-6f + + +[27] qr-fb2585a7-bd + + +[23] tapfd625661-7e + + +[26] qr-5de9034a-db + + +[22] tapb9083cd7-6f + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:2 + + +VLAN tag:2 + + +10.0.3.1/24 + + +10.0.3.2/24 + + +10.0.2.1/24 + + +10.0.2.2/24 + + +10.0.3.5/24 + + +10.0.3.2/24 + + +10.0.2.5/24 + + +10.0.2.2/24 + + +private2 + + +private1 + + +private2 + + +private1 + + +router1 + + +router2 + + +[19] patch-tun + + +br_ex:qg_e6c19c2c_8c:s->network_br_int:qr_04542d8f_13:n + + + +br_ex:qg_e6c19c2c_8c:s->network_br_int:qr_e503ba9c_89:n + + + +br_ex:qg_718e237a_3a:s->network_br_int:qr_fb2585a7_bd:n + + + +br_ex:qg_718e237a_3a:s->network_br_int:qr_5de9034a_db:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/ovs/static/net_topology.png b/openstack_dashboard/don/ovs/static/net_topology.png new file mode 100644 index 0000000..c3c225a Binary files /dev/null and b/openstack_dashboard/don/ovs/static/net_topology.png differ diff --git a/openstack_dashboard/don/ovs/static/network.dot b/openstack_dashboard/don/ovs/static/network.dot new file mode 100644 index 0000000..72e7b48 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/network.dot @@ -0,0 +1,92 @@ +digraph DON_network { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + +
OVS br_ex
router1
172.24.4.3/24
[2] qg-757bf552-73
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-43b83157-3b[5] tap59f90a3b-f5
VLAN tag:2VLAN tag:2
10.10.0.1/2410.10.0.2/24
private1
router1
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#0066cc", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_757bf552_73:s -> network_br_int:qr_43b83157_3b:n [color = "#0066cc", penwidth = "4"] + +} + diff --git a/openstack_dashboard/don/ovs/static/network.svg b/openstack_dashboard/don/ovs/static/network.svg new file mode 100644 index 0000000..97a17ea --- /dev/null +++ b/openstack_dashboard/don/ovs/static/network.svg @@ -0,0 +1,100 @@ + + + + + + +DON_network + +cluster_NetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +172.24.4.3/24 + + +[2] qg-757bf552-73 + + + +network_br_int + + +OVS br_int + + +[8] qr-43b83157-3b + + +[5] tap59f90a3b-f5 + + +VLAN tag:2 + + +VLAN tag:2 + + +10.10.0.1/24 + + +10.10.0.2/24 + + +private1 + + +router1 + + +[1] patch-tun + + +br_ex:qg_757bf552_73:s->network_br_int:qr_43b83157_3b:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/ovs/static/openstack-cloud-software-vertical-web.png b/openstack_dashboard/don/ovs/static/openstack-cloud-software-vertical-web.png new file mode 100644 index 0000000..9ee6d77 Binary files /dev/null and b/openstack_dashboard/don/ovs/static/openstack-cloud-software-vertical-web.png differ diff --git a/openstack_dashboard/don/ovs/static/path.html b/openstack_dashboard/don/ovs/static/path.html new file mode 100644 index 0000000..bcd3c66 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/path.html @@ -0,0 +1,27 @@ + + + + DON: Ping Tracer + + + +
+ + + + + + + + +
+ DON: Ping Tracer +
+ +
+
+ + + diff --git a/openstack_dashboard/don/ovs/static/ping.dot b/openstack_dashboard/don/ovs/static/ping.dot new file mode 100644 index 0000000..ff9d77d --- /dev/null +++ b/openstack_dashboard/don/ovs/static/ping.dot @@ -0,0 +1,278 @@ +digraph DON_DON { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_DONComputeNode { +style=filled +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled +subgraph cluster_VMs { +VMs [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VMs
VM1-1VM1-2VM3-1VM3-2VM4
private1private1private2private2publicprivate2private1
10.0.2.310.0.2.410.0.3.310.0.3.4172.24.4.510.0.3.610.0.2.6
>]; + +} + +subgraph cluster_LinuxBridge { +LinuxBridge [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Linux Bridge
tape0d697f2-cbtapbd4f1f72-5ftapbd96ca7d-5etap4441e3a6-f2tapce3d7b20-1dtapf0841d56-02tapfbb76083-60
qbre0d697f2-cbqbrbd4f1f72-5fqbrbd96ca7d-5eqbr4441e3a6-f2qbrce3d7b20-1dqbrf0841d56-02qbrfbb76083-60
qvbe0d697f2-cbqvbbd4f1f72-5fqvbbd96ca7d-5eqvb4441e3a6-f2qvbce3d7b20-1dqvbf0841d56-02qvbfbb76083-60
>]; + +} + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_compute_br_int { +compute_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[9] qvoe0d697f2-cb[10] qvobd4f1f72-5f[11] qvobd96ca7d-5e[12] qvo4441e3a6-f2[17] qvoce3d7b20-1d[16] qvof0841d56-02[15] qvofbb76083-60
VLAN tag:2VLAN tag:2VLAN tag:3VLAN tag:3VLAN tag:4VLAN tag:3VLAN tag:2
[1] patch-tun
>]; + +} + +subgraph cluster_compute_br_tun { +compute_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +ComputeNode:s -> VMs:n [color = "#909090", penwidth = "4", style="invis"] +VMs:10023:s -> LinuxBridge:tape0d697f2_cb:n [color = "#009900", penwidth = "4"] +VMs:10024:s -> LinuxBridge:tapbd4f1f72_5f:n [color = "#909090", penwidth = "4"] +VMs:10033:s -> LinuxBridge:tapbd96ca7d_5e:n [color = "#009900", penwidth = "4"] +VMs:10034:s -> LinuxBridge:tap4441e3a6_f2:n [color = "#909090", penwidth = "4"] +VMs:1722445:s -> LinuxBridge:tapce3d7b20_1d:n [color = "#909090", penwidth = "4"] +VMs:10036:s -> LinuxBridge:tapf0841d56_02:n [color = "#909090", penwidth = "4"] +VMs:10026:s -> LinuxBridge:tapfbb76083_60:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbe0d697f2_cb:s -> compute_br_int:qvoe0d697f2_cb:n [color = "#009900", penwidth = "4"] +LinuxBridge:qvbbd4f1f72_5f:s -> compute_br_int:qvobd4f1f72_5f:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbbd96ca7d_5e:s -> compute_br_int:qvobd96ca7d_5e:n [color = "#009900", penwidth = "4"] +LinuxBridge:qvb4441e3a6_f2:s -> compute_br_int:qvo4441e3a6_f2:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbce3d7b20_1d:s -> compute_br_int:qvoce3d7b20_1d:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbf0841d56_02:s -> compute_br_int:qvof0841d56_02:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbfbb76083_60:s -> compute_br_int:qvofbb76083_60:n [color = "#909090", penwidth = "4"] +compute_br_int:patch_tun:s -> compute_br_tun:patch_int:n [color = "#909090", penwidth = "4"] +} + +subgraph cluster_DONNetworkNode { +style=filled +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + + + + +
OVS br_ex
router1router2
172.24.4.3/24172.24.4.4/24
[2] qg-eb8796fb-83[3] qg-e2b1b0d3-a8
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-09a15e37-ca[6] tapd0828ef0-eb[7] qr-622abba5-e2[5] tapd6f091a2-c0[13] qr-361be2af-e5[5] tapd6f091a2-c0[14] qr-b66b902a-36[6] tapd0828ef0-eb
VLAN tag:3VLAN tag:3VLAN tag:2VLAN tag:2VLAN tag:2VLAN tag:2VLAN tag:3VLAN tag:3
10.0.3.1/2410.0.3.2/2410.0.2.1/2410.0.2.2/2410.0.2.5/2410.0.2.2/2410.0.3.5/2410.0.3.2/24
private2private1private1private2
router1router2
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#909090", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#909090", penwidth = "4"] +br_ex:qg_e2b1b0d3_a8:s -> network_br_int:qr_361be2af_e5:n [color = "#909090", penwidth = "4"] +br_ex:qg_e2b1b0d3_a8:s -> network_br_int:qr_b66b902a_36:n [color = "#909090", penwidth = "4"] +br_ex:qg_eb8796fb_83:s -> network_br_int:qr_09a15e37_ca:n [color = "#909090", penwidth = "4"] +br_ex:qg_eb8796fb_83:s -> network_br_int:qr_622abba5_e2:n [color = "#909090", penwidth = "4"] +} + + +} + diff --git a/openstack_dashboard/don/ovs/static/ping.html b/openstack_dashboard/don/ovs/static/ping.html new file mode 100644 index 0000000..6c3106b --- /dev/null +++ b/openstack_dashboard/don/ovs/static/ping.html @@ -0,0 +1 @@ +{"src": "10.0.2.3", "dst_info": {"ip": "10.0.3.3", "ports": {"brctl": "qbrbd96ca7d-5e", "qvo": "qvobd96ca7d-5e", "tap": "tapbd96ca7d-5e", "qvb": "qvbbd96ca7d-5e"}, "tag": "3", "pass": ["qbrbd96ca7d-5e", "qvobd96ca7d-5e", "tapbd96ca7d-5e", "qvbbd96ca7d-5e"], "fail": ["qbrbd96ca7d-5e", "qvobd96ca7d-5e", "tapbd96ca7d-5e", "qvbbd96ca7d-5e"], "counts": {"brctl": 2, "qvo": 2, "tap": 2, "qvb": 2}, "pids": [19059, 19060, 19064, 19068]}, "dst": "10.0.3.3", "ping_pass": true, "error": "", "net_info": {"hops": [{"ip": "10.0.2.1", "nms": "qrouter-dc5f8d60-ae94-4879-b8ff-ec5daa33a66a", "dev": "qr-622abba5-e2"}, {"nms": "qrouter-dc5f8d60-ae94-4879-b8ff-ec5daa33a66a", "dev": "qr-09a15e37-ca"}], "fail": ["qr-622abba5-e2", "qr-09a15e37-ca"], "pids": [19072, 19075], "counts": {"qr-09a15e37-ca": 2, "qr-622abba5-e2": 2}, "pass": ["qr-622abba5-e2", "qr-09a15e37-ca"]}, "src_info": {"ip": "10.0.2.3", "ports": {"brctl": "qbre0d697f2-cb", "qvo": "qvoe0d697f2-cb", "tap": "tape0d697f2-cb", "qvb": "qvbe0d697f2-cb"}, "tag": "2", "pass": ["qbre0d697f2-cb", "qvoe0d697f2-cb", "tape0d697f2-cb", "qvbe0d697f2-cb"], "fail": ["qbre0d697f2-cb", "qvoe0d697f2-cb", "tape0d697f2-cb", "qvbe0d697f2-cb"], "counts": {"brctl": 2, "qvo": 2, "tap": 2, "qvb": 2}, "pids": [19049, 19051, 19053, 19054]}} \ No newline at end of file diff --git a/openstack_dashboard/don/ovs/static/ping.svg b/openstack_dashboard/don/ovs/static/ping.svg new file mode 100644 index 0000000..9fa6752 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/ping.svg @@ -0,0 +1,473 @@ + + + + + + +DON_DON + +cluster_DONComputeNode + + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + +cluster_DONNetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + +cluster_NetworkNode + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +VM1-1 + + +VM1-2 + + +VM3-1 + + +VM3-2 + + +VM4 + + +private1 + + +private1 + + +private2 + + +private2 + + +public + + +private2 + + +private1 + + +10.0.2.3 + + +10.0.2.4 + + +10.0.3.3 + + +10.0.3.4 + + +172.24.4.5 + + +10.0.3.6 + + +10.0.2.6 + + + +LinuxBridge + + +Linux Bridge + + +tape0d697f2-cb + + +tapbd4f1f72-5f + + +tapbd96ca7d-5e + + +tap4441e3a6-f2 + + +tapce3d7b20-1d + + +tapf0841d56-02 + + +tapfbb76083-60 + + +qbre0d697f2-cb + + +qbrbd4f1f72-5f + + +qbrbd96ca7d-5e + + +qbr4441e3a6-f2 + + +qbrce3d7b20-1d + + +qbrf0841d56-02 + + +qbrfbb76083-60 + + +qvbe0d697f2-cb + + +qvbbd4f1f72-5f + + +qvbbd96ca7d-5e + + +qvb4441e3a6-f2 + + +qvbce3d7b20-1d + + +qvbf0841d56-02 + + +qvbfbb76083-60 + + +VMs:10023:s->LinuxBridge:tape0d697f2_cb:n + + + +VMs:10024:s->LinuxBridge:tapbd4f1f72_5f:n + + + +VMs:10033:s->LinuxBridge:tapbd96ca7d_5e:n + + + +VMs:10034:s->LinuxBridge:tap4441e3a6_f2:n + + + +VMs:1722445:s->LinuxBridge:tapce3d7b20_1d:n + + + +VMs:10036:s->LinuxBridge:tapf0841d56_02:n + + + +VMs:10026:s->LinuxBridge:tapfbb76083_60:n + + + +compute_br_int + + +OVS br_int + + +[9] qvoe0d697f2-cb + + +[10] qvobd4f1f72-5f + + +[11] qvobd96ca7d-5e + + +[12] qvo4441e3a6-f2 + + +[17] qvoce3d7b20-1d + + +[16] qvof0841d56-02 + + +[15] qvofbb76083-60 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:4 + + +VLAN tag:3 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvbe0d697f2_cb:s->compute_br_int:qvoe0d697f2_cb:n + + + +LinuxBridge:qvbbd4f1f72_5f:s->compute_br_int:qvobd4f1f72_5f:n + + + +LinuxBridge:qvbbd96ca7d_5e:s->compute_br_int:qvobd96ca7d_5e:n + + + +LinuxBridge:qvb4441e3a6_f2:s->compute_br_int:qvo4441e3a6_f2:n + + + +LinuxBridge:qvbce3d7b20_1d:s->compute_br_int:qvoce3d7b20_1d:n + + + +LinuxBridge:qvbf0841d56_02:s->compute_br_int:qvof0841d56_02:n + + + +LinuxBridge:qvbfbb76083_60:s->compute_br_int:qvofbb76083_60:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +router2 + + +172.24.4.3/24 + + +172.24.4.4/24 + + +[2] qg-eb8796fb-83 + + +[3] qg-e2b1b0d3-a8 + + + +network_br_int + + +OVS br_int + + +[8] qr-09a15e37-ca + + +[6] tapd0828ef0-eb + + +[7] qr-622abba5-e2 + + +[5] tapd6f091a2-c0 + + +[13] qr-361be2af-e5 + + +[5] tapd6f091a2-c0 + + +[14] qr-b66b902a-36 + + +[6] tapd0828ef0-eb + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:3 + + +VLAN tag:3 + + +10.0.3.1/24 + + +10.0.3.2/24 + + +10.0.2.1/24 + + +10.0.2.2/24 + + +10.0.2.5/24 + + +10.0.2.2/24 + + +10.0.3.5/24 + + +10.0.3.2/24 + + +private2 + + +private1 + + +private1 + + +private2 + + +router1 + + +router2 + + +[1] patch-tun + + +br_ex:qg_e2b1b0d3_a8:s->network_br_int:qr_361be2af_e5:n + + + +br_ex:qg_e2b1b0d3_a8:s->network_br_int:qr_b66b902a_36:n + + + +br_ex:qg_eb8796fb_83:s->network_br_int:qr_09a15e37_ca:n + + + +br_ex:qg_eb8796fb_83:s->network_br_int:qr_622abba5_e2:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/ovs/static/view.html b/openstack_dashboard/don/ovs/static/view.html new file mode 100644 index 0000000..e2b9fd1 --- /dev/null +++ b/openstack_dashboard/don/ovs/static/view.html @@ -0,0 +1,31 @@ + + + + DON: Internal View + + + +
+ + + + + + + + +
+ DON: Internal View +
+ +     + +
+
+ + + diff --git a/openstack_dashboard/don/ovs/templates/ovs/analyze.html b/openstack_dashboard/don/ovs/templates/ovs/analyze.html new file mode 100644 index 0000000..9e75f8b --- /dev/null +++ b/openstack_dashboard/don/ovs/templates/ovs/analyze.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "DON" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Diagnosing OpenVswitch") %} +{% endblock page_header %} + +{% block main %} + + {% include "don/don.report.html" %} +{% endblock %} + + + diff --git a/openstack_dashboard/don/ovs/templates/ovs/index.html b/openstack_dashboard/don/ovs/templates/ovs/index.html new file mode 100644 index 0000000..14d668e --- /dev/null +++ b/openstack_dashboard/don/ovs/templates/ovs/index.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "DON" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Diagnosing OpenVswitch") %} +{% endblock page_header %} + +{% block main %} + + {% include "don/don.html" %} +{% endblock %} + + + diff --git a/openstack_dashboard/don/ovs/templates/ovs/path.html b/openstack_dashboard/don/ovs/templates/ovs/path.html new file mode 100644 index 0000000..add5567 --- /dev/null +++ b/openstack_dashboard/don/ovs/templates/ovs/path.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "DON" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Diagnosing OpenVswitch") %} +{% endblock page_header %} + +{% block main %} + + {% include "don/path.html" %} +{% endblock %} + + + diff --git a/openstack_dashboard/don/ovs/templates/ovs/ping.html b/openstack_dashboard/don/ovs/templates/ovs/ping.html new file mode 100644 index 0000000..27adf7c --- /dev/null +++ b/openstack_dashboard/don/ovs/templates/ovs/ping.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "DON" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Diagnosing OpenVswitch") %} +{% endblock page_header %} + +{% block main %} + + {% include "don/ping.html" %} +{% endblock %} + + + diff --git a/openstack_dashboard/don/ovs/templates/ovs/views.html b/openstack_dashboard/don/ovs/templates/ovs/views.html new file mode 100644 index 0000000..b1519c3 --- /dev/null +++ b/openstack_dashboard/don/ovs/templates/ovs/views.html @@ -0,0 +1,15 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "DON" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Diagnosing OpenVswitch") %} +{% endblock page_header %} + +{% block main %} + + {% include "don/view.html" %} +{% endblock %} + + + diff --git a/openstack_dashboard/don/ovs/urls.py b/openstack_dashboard/don/ovs/urls.py new file mode 100644 index 0000000..69e0e2e --- /dev/null +++ b/openstack_dashboard/don/ovs/urls.py @@ -0,0 +1,18 @@ +from django.conf.urls import patterns +from django.conf.urls import url + +from don.ovs.views \ + import IndexView + +from . import views + + +urlpatterns = patterns( + '', + url(r'^view/', views.view, name='view'), + url(r'^collect/', views.collect, name='collect'), + url(r'^ping/', views.ping, name='ping'), + url(r'^analyze/', views.analyze, name='analyze'), + url(r'^status/', views.get_status, name='get_status'), + url(r'^$', IndexView.as_view(), name='index'), +) diff --git a/openstack_dashboard/don/ovs/views.py b/openstack_dashboard/don/ovs/views.py new file mode 100644 index 0000000..e6fed13 --- /dev/null +++ b/openstack_dashboard/don/ovs/views.py @@ -0,0 +1,233 @@ +from horizon import views +from django.http import HttpResponse +from django.conf import settings +from plot import DotGenerator +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 + + +class IndexView(views.APIView): + # A very simple class-based view... + template_name = 'don/ovs/index.html' + + def get_data(self, request, context, *args, **kwargs): + # Add data to the context here... + return context + + +def index(request): + return HttpResponse('I am DON') + + +def view(request): + # import pdb + # pdb.set_trace() + pwd = settings.ROOT_PATH # +'/openstack_dashboard/dashboards/admin/don/' + + JSON_FILE = pwd + '/don/ovs/don.json' + static_path = settings.STATIC_ROOT + ''' + COMPUTE_DOT_FILE = pwd + '/don/ovs/static/compute.dot' + COMPUTE_SVG_FILE = pwd + '/don/ovs/static/compute.svg' + NETWORK_DOT_FILE = pwd + '/don/ovs/static/network.dot' + NETWORK_SVG_FILE = pwd + '/don/ovs/static/network.svg' + COMBINED_DOT_FILE = pwd + '/don/ovs/static/don.dot' + COMBINED_SVG_FILE = pwd + '/don/ovs/static/don.svg' + ''' + COMPUTE_DOT_FILE = static_path + '/don/compute.dot' + COMPUTE_SVG_FILE = static_path + '/don/compute.svg' + NETWORK_DOT_FILE = static_path + '/don/network.dot' + NETWORK_SVG_FILE = static_path + '/don/network.svg' + COMBINED_DOT_FILE = static_path + '/don/don.dot' + COMBINED_SVG_FILE = static_path + '/don/don.svg' + + macro = {} + # return HttpResponseRedirect('static/view.html') + + plotter = DotGenerator(JSON_FILE, + COMPUTE_DOT_FILE, + COMPUTE_SVG_FILE, + NETWORK_DOT_FILE, + NETWORK_SVG_FILE, + COMBINED_DOT_FILE, + COMBINED_SVG_FILE, + None + ) + plotter.plot_compute_node() + plotter.generate_compute_svg() + + plotter.plot_network_node() + plotter.generate_network_svg() + + plotter.plot_combined() + plotter.generate_combined_svg() + # return HttpResponseRedirect('static/view.html') + return render(request, "don/ovs/views.html", macro) + + +def analyze(request): + # pwd = settings.BASE_DIR + pwd = settings.ROOT_PATH + JSON_FILE = pwd + '/don/ovs/don.json' + + params = { + 'error_file': pwd + '/don/templates/don/don.error.txt', + 'test:all': True, + 'test:ping': False, + 'test:ping_count': 1, + 'test:ovs': True, + 'test:report_file': pwd + '/don/templates/don/don.report.html', + } + print "params ====> ", params + analyzer.analyze(JSON_FILE, params) + # output = analyzer.analyze(JSON_FILE, params) + # html = 'Output: %s' % output + # return HttpResponse(html) + # return HttpResponseRedirect('/static/don.report.html') + return render(request, "don/ovs/analyze.html") + # return render_to_response('don/ovs/analyze.html') + + +def test(request): + return HttpResponse('Testing the setup') + + +def ping(request): + # if this is a POST request we need to process the form data + if request.method == 'POST': + # create a form instance and populate it with data from the request: + form = PingForm(request.POST) + # check whether it's valid: + if form.is_valid(): + # process the data in form.cleaned_data as required + # ... + # redirect to a new URL: + src_ip = form.cleaned_data['src_ip'] + dst_ip = form.cleaned_data['dst_ip'] + router = form.cleaned_data['router'] + # html = 'SIP: %s DIP: %s router: %s' % (src_ip, dst_ip, router) + # return HttpResponse(html) + static_path = settings.STATIC_ROOT + pwd = settings.ROOT_PATH + JSON_FILE = pwd + '/don/ovs/don.json' + + params = { + 'json_file': pwd + '/don/ovs/don.json', + 'src_ip': src_ip, + 'dst_ip': dst_ip, + 'router': router, + 'path_file': static_path + '/don/ping.html', + 'username': 'cirros', + 'passwd': 'cubswin:)', + 'count': 2, + 'timeout': 2, + 'debug': True, + 'plot': False, + } + response = path.path(params) + if response: + error_text = response + messages.error(request, error_text) + return render(request, 'don/ovs/ping.html', {'form': form}) + + JSON_FILE = pwd + '/don/ovs/don.json' + COMPUTE_DOT_FILE = None + COMPUTE_SVG_FILE = None + NETWORK_DOT_FILE = None + NETWORK_SVG_FILE = None + COMBINED_DOT_FILE = static_path + '/don/ping.dot' + COMBINED_SVG_FILE = static_path + '/don/ping.svg' + # HIGHLIGHT_FILE = pwd + '/don/ovs/static/ping.html' + HIGHLIGHT_FILE = static_path + '/don/ping.html' + + plotter = DotGenerator(JSON_FILE, + COMPUTE_DOT_FILE, + COMPUTE_SVG_FILE, + NETWORK_DOT_FILE, + NETWORK_SVG_FILE, + COMBINED_DOT_FILE, + COMBINED_SVG_FILE, + HIGHLIGHT_FILE, + ) + plotter.plot_combined() + plotter.generate_combined_svg() + + # return HttpResponseRedirect('/static/path.html') + return render(request, 'don/ovs/path.html') + + # if a GET (or any other method) we'll create a blank form + else: + form = PingForm() + BASE_DIR = settings.ROOT_PATH + '/don/ovs/' + myenv = os.environ.copy() + myenv.update(get_env(BASE_DIR + 'admin-openrc.sh')) + output = execute_cmd(['nova', 'list'], sudo=False, + shell=False, env=myenv).split('\n') + ip_list = get_instance_ips(output) + ip_list.sort() + router_op = execute_cmd( + ['neutron', 'router-list'], sudo=False, shell=False, env=myenv).split('\n') + router_list = get_router_names(router_op) + router_list.sort() + # insert first value of select menu + ip_opt = zip(ip_list, ip_list) + router_opt = zip(router_list, router_list) + # ip_opt.insert(0,('','Select IP address')) + # router_opt.insert(0,('','Select Router')) + form.fields['src_ip'].widget.choices = ip_opt + form.fields['dst_ip'].widget.choices = ip_opt + form.fields['router'].widget.choices = router_opt + + return render(request, 'don/ovs/ping.html', {'form': form}) + + +def collect(request): + macro = {'collect_status': 'Collection failed'} + status = 0 + + BASE_DIR = settings.ROOT_PATH + # CUR_DIR = os.getcwd() + os.chdir(BASE_DIR + '/don/ovs') + cmd = 'sudo python collector.py' + for line in run_command(cmd): + if line.startswith('STATUS:') and line.find('Writing collected info') != -1: + status = 1 + macro['collect_status'] = \ + "Collecton successful. Click visualize to display" + # res = collector.main() + os.chdir(BASE_DIR) + if status: + messages.success(request, macro['collect_status']) + else: + messages.error(request, macro['collect_status']) + resp = HttpResponse(json.dumps(macro), content_type="application/json") + return resp + + +def run_command(cmd): + ps = subprocess.Popen(shlex.split( + cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + while(True): + ret = ps.poll() # returns None while subprocess is running + line = ps.stdout.readline() + yield line + if(ret is not None): + break + + +def get_status(request): + BASE_DIR = settings.ROOT_PATH + '/don/ovs/' + status = open(BASE_DIR + 'collector_log.txt', 'r').readline() + if status != " " and status != '\n': + return HttpResponse(json.dumps({'status': status}), content_type="application/json") diff --git a/openstack_dashboard/don/static/dashboard/don/ovs/view.html b/openstack_dashboard/don/static/dashboard/don/ovs/view.html new file mode 100644 index 0000000..e2b9fd1 --- /dev/null +++ b/openstack_dashboard/don/static/dashboard/don/ovs/view.html @@ -0,0 +1,31 @@ + + + + DON: Internal View + + + +
+ + + + + + + + +
+ DON: Internal View +
+ +     + +
+
+ + + diff --git a/openstack_dashboard/don/static/don/ovs/compute.dot b/openstack_dashboard/don/static/don/ovs/compute.dot new file mode 100644 index 0000000..5d9ddfa --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/compute.dot @@ -0,0 +1,21 @@ +digraph DON_compute { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled diff --git a/openstack_dashboard/don/static/don/ovs/compute.svg b/openstack_dashboard/don/static/don/ovs/compute.svg new file mode 100644 index 0000000..5c253f5 --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/compute.svg @@ -0,0 +1,111 @@ + + + + + + +DON_compute + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +vm1 + + +vm2 + + +private1 + + +private1 + + +10.10.0.3 + + +10.10.0.4 + + + +LinuxBridge +LinuxBridge + + +compute_br_int + + +OVS br_int + + +[6] qvo8aa60600-7b + + +[7] qvo71ac5bef-7c + + +VLAN tag:2 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvb8aa60600_7b:s->compute_br_int:qvo8aa60600_7b:n + + + +LinuxBridge:qvb71ac5bef_7c:s->compute_br_int:qvo71ac5bef_7c:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/static/don/ovs/don.dot b/openstack_dashboard/don/static/don/ovs/don.dot new file mode 100644 index 0000000..edcc4aa --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/don.dot @@ -0,0 +1,198 @@ +digraph DON_DON { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_DONComputeNode { +style=filled +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled +subgraph cluster_VMs { +VMs [ shape = plaintext, label = < + + + + + + + + + + + + + + + + +
VMs
vm1vm2
private1private1
10.10.0.310.10.0.4
>]; + +} + +subgraph cluster_LinuxBridge { +LinuxBridge [ shape = plaintext, label = < + + + + + + + + + + + + + + +
Linux Bridge
qbr8aa60600-7bqbr71ac5bef-7c
qvb8aa60600-7bqvb71ac5bef-7c
>]; + +} + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_compute_br_int { +compute_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + +
OVS br_int
[6] qvo8aa60600-7b[7] qvo71ac5bef-7c
VLAN tag:2VLAN tag:2
[1] patch-tun
>]; + +} + +subgraph cluster_compute_br_tun { +compute_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +ComputeNode:s -> VMs:n [color = "#0066cc", penwidth = "4", style="invis"] +LinuxBridge:qvb8aa60600_7b:s -> compute_br_int:qvo8aa60600_7b:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb71ac5bef_7c:s -> compute_br_int:qvo71ac5bef_7c:n [color = "#0066cc", penwidth = "4"] +compute_br_int:patch_tun:s -> compute_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +} + +subgraph cluster_DONNetworkNode { +style=filled +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + +
OVS br_ex
router1
172.24.4.3/24
[2] qg-757bf552-73
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-43b83157-3b[5] tap59f90a3b-f5
VLAN tag:2VLAN tag:2
10.10.0.1/2410.10.0.2/24
private1
router1
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#0066cc", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_757bf552_73:s -> network_br_int:qr_43b83157_3b:n [color = "#0066cc", penwidth = "4"] +} + + +} + diff --git a/openstack_dashboard/don/static/don/ovs/don.svg b/openstack_dashboard/don/static/don/ovs/don.svg new file mode 100644 index 0000000..7c024a7 --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/don.svg @@ -0,0 +1,204 @@ + + + + + + +DON_DON + +cluster_DONComputeNode + + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + +cluster_DONNetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + +cluster_NetworkNode + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +vm1 + + +vm2 + + +private1 + + +private1 + + +10.10.0.3 + + +10.10.0.4 + + + +LinuxBridge +LinuxBridge + + +compute_br_int + + +OVS br_int + + +[6] qvo8aa60600-7b + + +[7] qvo71ac5bef-7c + + +VLAN tag:2 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvb8aa60600_7b:s->compute_br_int:qvo8aa60600_7b:n + + + +LinuxBridge:qvb71ac5bef_7c:s->compute_br_int:qvo71ac5bef_7c:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +172.24.4.3/24 + + +[2] qg-757bf552-73 + + + +network_br_int + + +OVS br_int + + +[8] qr-43b83157-3b + + +[5] tap59f90a3b-f5 + + +VLAN tag:2 + + +VLAN tag:2 + + +10.10.0.1/24 + + +10.10.0.2/24 + + +private1 + + +router1 + + +[1] patch-tun + + +br_ex:qg_757bf552_73:s->network_br_int:qr_43b83157_3b:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/static/don/ovs/network.dot b/openstack_dashboard/don/static/don/ovs/network.dot new file mode 100644 index 0000000..72e7b48 --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/network.dot @@ -0,0 +1,92 @@ +digraph DON_network { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + +
OVS br_ex
router1
172.24.4.3/24
[2] qg-757bf552-73
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-43b83157-3b[5] tap59f90a3b-f5
VLAN tag:2VLAN tag:2
10.10.0.1/2410.10.0.2/24
private1
router1
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#0066cc", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_757bf552_73:s -> network_br_int:qr_43b83157_3b:n [color = "#0066cc", penwidth = "4"] + +} + diff --git a/openstack_dashboard/don/static/don/ovs/network.svg b/openstack_dashboard/don/static/don/ovs/network.svg new file mode 100644 index 0000000..97a17ea --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/network.svg @@ -0,0 +1,100 @@ + + + + + + +DON_network + +cluster_NetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +172.24.4.3/24 + + +[2] qg-757bf552-73 + + + +network_br_int + + +OVS br_int + + +[8] qr-43b83157-3b + + +[5] tap59f90a3b-f5 + + +VLAN tag:2 + + +VLAN tag:2 + + +10.10.0.1/24 + + +10.10.0.2/24 + + +private1 + + +router1 + + +[1] patch-tun + + +br_ex:qg_757bf552_73:s->network_br_int:qr_43b83157_3b:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/static/don/ovs/ping.dot b/openstack_dashboard/don/static/don/ovs/ping.dot new file mode 100644 index 0000000..ff9d77d --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/ping.dot @@ -0,0 +1,278 @@ +digraph DON_DON { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_DONComputeNode { +style=filled +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled +subgraph cluster_VMs { +VMs [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VMs
VM1-1VM1-2VM3-1VM3-2VM4
private1private1private2private2publicprivate2private1
10.0.2.310.0.2.410.0.3.310.0.3.4172.24.4.510.0.3.610.0.2.6
>]; + +} + +subgraph cluster_LinuxBridge { +LinuxBridge [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Linux Bridge
tape0d697f2-cbtapbd4f1f72-5ftapbd96ca7d-5etap4441e3a6-f2tapce3d7b20-1dtapf0841d56-02tapfbb76083-60
qbre0d697f2-cbqbrbd4f1f72-5fqbrbd96ca7d-5eqbr4441e3a6-f2qbrce3d7b20-1dqbrf0841d56-02qbrfbb76083-60
qvbe0d697f2-cbqvbbd4f1f72-5fqvbbd96ca7d-5eqvb4441e3a6-f2qvbce3d7b20-1dqvbf0841d56-02qvbfbb76083-60
>]; + +} + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_compute_br_int { +compute_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[9] qvoe0d697f2-cb[10] qvobd4f1f72-5f[11] qvobd96ca7d-5e[12] qvo4441e3a6-f2[17] qvoce3d7b20-1d[16] qvof0841d56-02[15] qvofbb76083-60
VLAN tag:2VLAN tag:2VLAN tag:3VLAN tag:3VLAN tag:4VLAN tag:3VLAN tag:2
[1] patch-tun
>]; + +} + +subgraph cluster_compute_br_tun { +compute_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +ComputeNode:s -> VMs:n [color = "#909090", penwidth = "4", style="invis"] +VMs:10023:s -> LinuxBridge:tape0d697f2_cb:n [color = "#009900", penwidth = "4"] +VMs:10024:s -> LinuxBridge:tapbd4f1f72_5f:n [color = "#909090", penwidth = "4"] +VMs:10033:s -> LinuxBridge:tapbd96ca7d_5e:n [color = "#009900", penwidth = "4"] +VMs:10034:s -> LinuxBridge:tap4441e3a6_f2:n [color = "#909090", penwidth = "4"] +VMs:1722445:s -> LinuxBridge:tapce3d7b20_1d:n [color = "#909090", penwidth = "4"] +VMs:10036:s -> LinuxBridge:tapf0841d56_02:n [color = "#909090", penwidth = "4"] +VMs:10026:s -> LinuxBridge:tapfbb76083_60:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbe0d697f2_cb:s -> compute_br_int:qvoe0d697f2_cb:n [color = "#009900", penwidth = "4"] +LinuxBridge:qvbbd4f1f72_5f:s -> compute_br_int:qvobd4f1f72_5f:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbbd96ca7d_5e:s -> compute_br_int:qvobd96ca7d_5e:n [color = "#009900", penwidth = "4"] +LinuxBridge:qvb4441e3a6_f2:s -> compute_br_int:qvo4441e3a6_f2:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbce3d7b20_1d:s -> compute_br_int:qvoce3d7b20_1d:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbf0841d56_02:s -> compute_br_int:qvof0841d56_02:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbfbb76083_60:s -> compute_br_int:qvofbb76083_60:n [color = "#909090", penwidth = "4"] +compute_br_int:patch_tun:s -> compute_br_tun:patch_int:n [color = "#909090", penwidth = "4"] +} + +subgraph cluster_DONNetworkNode { +style=filled +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + + + + +
OVS br_ex
router1router2
172.24.4.3/24172.24.4.4/24
[2] qg-eb8796fb-83[3] qg-e2b1b0d3-a8
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-09a15e37-ca[6] tapd0828ef0-eb[7] qr-622abba5-e2[5] tapd6f091a2-c0[13] qr-361be2af-e5[5] tapd6f091a2-c0[14] qr-b66b902a-36[6] tapd0828ef0-eb
VLAN tag:3VLAN tag:3VLAN tag:2VLAN tag:2VLAN tag:2VLAN tag:2VLAN tag:3VLAN tag:3
10.0.3.1/2410.0.3.2/2410.0.2.1/2410.0.2.2/2410.0.2.5/2410.0.2.2/2410.0.3.5/2410.0.3.2/24
private2private1private1private2
router1router2
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#909090", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#909090", penwidth = "4"] +br_ex:qg_e2b1b0d3_a8:s -> network_br_int:qr_361be2af_e5:n [color = "#909090", penwidth = "4"] +br_ex:qg_e2b1b0d3_a8:s -> network_br_int:qr_b66b902a_36:n [color = "#909090", penwidth = "4"] +br_ex:qg_eb8796fb_83:s -> network_br_int:qr_09a15e37_ca:n [color = "#909090", penwidth = "4"] +br_ex:qg_eb8796fb_83:s -> network_br_int:qr_622abba5_e2:n [color = "#909090", penwidth = "4"] +} + + +} + diff --git a/openstack_dashboard/don/static/don/ovs/ping.svg b/openstack_dashboard/don/static/don/ovs/ping.svg new file mode 100644 index 0000000..9fa6752 --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/ping.svg @@ -0,0 +1,473 @@ + + + + + + +DON_DON + +cluster_DONComputeNode + + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + +cluster_DONNetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + +cluster_NetworkNode + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +VM1-1 + + +VM1-2 + + +VM3-1 + + +VM3-2 + + +VM4 + + +private1 + + +private1 + + +private2 + + +private2 + + +public + + +private2 + + +private1 + + +10.0.2.3 + + +10.0.2.4 + + +10.0.3.3 + + +10.0.3.4 + + +172.24.4.5 + + +10.0.3.6 + + +10.0.2.6 + + + +LinuxBridge + + +Linux Bridge + + +tape0d697f2-cb + + +tapbd4f1f72-5f + + +tapbd96ca7d-5e + + +tap4441e3a6-f2 + + +tapce3d7b20-1d + + +tapf0841d56-02 + + +tapfbb76083-60 + + +qbre0d697f2-cb + + +qbrbd4f1f72-5f + + +qbrbd96ca7d-5e + + +qbr4441e3a6-f2 + + +qbrce3d7b20-1d + + +qbrf0841d56-02 + + +qbrfbb76083-60 + + +qvbe0d697f2-cb + + +qvbbd4f1f72-5f + + +qvbbd96ca7d-5e + + +qvb4441e3a6-f2 + + +qvbce3d7b20-1d + + +qvbf0841d56-02 + + +qvbfbb76083-60 + + +VMs:10023:s->LinuxBridge:tape0d697f2_cb:n + + + +VMs:10024:s->LinuxBridge:tapbd4f1f72_5f:n + + + +VMs:10033:s->LinuxBridge:tapbd96ca7d_5e:n + + + +VMs:10034:s->LinuxBridge:tap4441e3a6_f2:n + + + +VMs:1722445:s->LinuxBridge:tapce3d7b20_1d:n + + + +VMs:10036:s->LinuxBridge:tapf0841d56_02:n + + + +VMs:10026:s->LinuxBridge:tapfbb76083_60:n + + + +compute_br_int + + +OVS br_int + + +[9] qvoe0d697f2-cb + + +[10] qvobd4f1f72-5f + + +[11] qvobd96ca7d-5e + + +[12] qvo4441e3a6-f2 + + +[17] qvoce3d7b20-1d + + +[16] qvof0841d56-02 + + +[15] qvofbb76083-60 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:4 + + +VLAN tag:3 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvbe0d697f2_cb:s->compute_br_int:qvoe0d697f2_cb:n + + + +LinuxBridge:qvbbd4f1f72_5f:s->compute_br_int:qvobd4f1f72_5f:n + + + +LinuxBridge:qvbbd96ca7d_5e:s->compute_br_int:qvobd96ca7d_5e:n + + + +LinuxBridge:qvb4441e3a6_f2:s->compute_br_int:qvo4441e3a6_f2:n + + + +LinuxBridge:qvbce3d7b20_1d:s->compute_br_int:qvoce3d7b20_1d:n + + + +LinuxBridge:qvbf0841d56_02:s->compute_br_int:qvof0841d56_02:n + + + +LinuxBridge:qvbfbb76083_60:s->compute_br_int:qvofbb76083_60:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +router2 + + +172.24.4.3/24 + + +172.24.4.4/24 + + +[2] qg-eb8796fb-83 + + +[3] qg-e2b1b0d3-a8 + + + +network_br_int + + +OVS br_int + + +[8] qr-09a15e37-ca + + +[6] tapd0828ef0-eb + + +[7] qr-622abba5-e2 + + +[5] tapd6f091a2-c0 + + +[13] qr-361be2af-e5 + + +[5] tapd6f091a2-c0 + + +[14] qr-b66b902a-36 + + +[6] tapd0828ef0-eb + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:3 + + +VLAN tag:3 + + +10.0.3.1/24 + + +10.0.3.2/24 + + +10.0.2.1/24 + + +10.0.2.2/24 + + +10.0.2.5/24 + + +10.0.2.2/24 + + +10.0.3.5/24 + + +10.0.3.2/24 + + +private2 + + +private1 + + +private1 + + +private2 + + +router1 + + +router2 + + +[1] patch-tun + + +br_ex:qg_e2b1b0d3_a8:s->network_br_int:qr_361be2af_e5:n + + + +br_ex:qg_e2b1b0d3_a8:s->network_br_int:qr_b66b902a_36:n + + + +br_ex:qg_eb8796fb_83:s->network_br_int:qr_09a15e37_ca:n + + + +br_ex:qg_eb8796fb_83:s->network_br_int:qr_622abba5_e2:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/static/don/ovs/view.html b/openstack_dashboard/don/static/don/ovs/view.html new file mode 100644 index 0000000..e2b9fd1 --- /dev/null +++ b/openstack_dashboard/don/static/don/ovs/view.html @@ -0,0 +1,31 @@ + + + + DON: Internal View + + + +
+ + + + + + + + +
+ DON: Internal View +
+ +     + +
+
+ + + diff --git a/openstack_dashboard/don/tables.py b/openstack_dashboard/don/tables.py new file mode 100644 index 0000000..2f645a5 --- /dev/null +++ b/openstack_dashboard/don/tables.py @@ -0,0 +1,36 @@ +from horizon import tables +from django.utils.translation import ugettext_lazy as _ +from django.core.urlresolvers import reverse +from don import api +from django.utils.http import urlencode + + +class ViewCollection(tables.LinkAction): + name = 'view' + verbose_name = _('View') + + def get_link_url(self, datum): + base_url = reverse('horizon:don:archive:dbview') + params = urlencode({"id": self.table.get_object_id(datum)}) + return "?".join([base_url, params]) + + +class DeleteCollection(tables.DeleteAction): + name = 'delete' + verbose_name = _('Delete') + data_type_singular = _('Collection') + + def delete(self, request, obj_id): + return api.remove_collection(request, obj_id) + + +class CollectionTable(tables.DataTable): + name = tables.Column('timestamp', verbose_name=_('Generated Time')) + + def get_object_id(self, datum): + return datum['id'] + + class Meta: + # table_actions = (,) + row_actions = (ViewCollection, DeleteCollection,) + name = 'collection' diff --git a/openstack_dashboard/don/templates/don/base.html b/openstack_dashboard/don/templates/don/base.html new file mode 100644 index 0000000..b28b819 --- /dev/null +++ b/openstack_dashboard/don/templates/don/base.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} + +{% block sidebar %} + {% include 'horizon/common/_sidebar.html' %} +{% endblock %} + +{% block main %} + {% include "horizon/_messages.html" %} + {% block mydashboard_main %}{% endblock %} +{% endblock %} + diff --git a/openstack_dashboard/don/templates/don/don.html b/openstack_dashboard/don/templates/don/don.html new file mode 100644 index 0000000..7e40413 --- /dev/null +++ b/openstack_dashboard/don/templates/don/don.html @@ -0,0 +1,93 @@ + + + + DON: Diagnosing OpenStack Networking + + + + +
+ + + + + + + + + + + + + +
+ DON: Diagnosing OpenStack Networking +

+ Step 1 Collect + Step 2 Visualize + Step 3 Analyze or Ping Trace
+ + + +
+


+ +
+
+ + + + diff --git a/openstack_dashboard/don/templates/don/don.report.html b/openstack_dashboard/don/templates/don/don.report.html new file mode 100644 index 0000000..7e4e44f --- /dev/null +++ b/openstack_dashboard/don/templates/don/don.report.html @@ -0,0 +1,144 @@ + + + + +DON: Analysis Results + + +

OVS Test Results

+

OVS test between all pairs of ports using the same tag in br-int

+ +OVERALL RESULT: +

Ping Test Results

+

Ping test between all pairs of VMs

+ +OVERALL RESULT: + + diff --git a/openstack_dashboard/don/templates/don/don.svg b/openstack_dashboard/don/templates/don/don.svg new file mode 100644 index 0000000..7c024a7 --- /dev/null +++ b/openstack_dashboard/don/templates/don/don.svg @@ -0,0 +1,204 @@ + + + + + + +DON_DON + +cluster_DONComputeNode + + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + +cluster_DONNetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + +cluster_NetworkNode + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +vm1 + + +vm2 + + +private1 + + +private1 + + +10.10.0.3 + + +10.10.0.4 + + + +LinuxBridge +LinuxBridge + + +compute_br_int + + +OVS br_int + + +[6] qvo8aa60600-7b + + +[7] qvo71ac5bef-7c + + +VLAN tag:2 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvb8aa60600_7b:s->compute_br_int:qvo8aa60600_7b:n + + + +LinuxBridge:qvb71ac5bef_7c:s->compute_br_int:qvo71ac5bef_7c:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +172.24.4.3/24 + + +[2] qg-757bf552-73 + + + +network_br_int + + +OVS br_int + + +[8] qr-43b83157-3b + + +[5] tap59f90a3b-f5 + + +VLAN tag:2 + + +VLAN tag:2 + + +10.10.0.1/24 + + +10.10.0.2/24 + + +private1 + + +router1 + + +[1] patch-tun + + +br_ex:qg_757bf552_73:s->network_br_int:qr_43b83157_3b:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/openstack_dashboard/don/templates/don/path.html b/openstack_dashboard/don/templates/don/path.html new file mode 100644 index 0000000..84cf06a --- /dev/null +++ b/openstack_dashboard/don/templates/don/path.html @@ -0,0 +1,27 @@ + + + + DON: Ping Tracer + + + +
+ + + + + + + + +
+ DON: Ping Tracer +
+ +
+
+ + + diff --git a/openstack_dashboard/don/templates/don/ping.html b/openstack_dashboard/don/templates/don/ping.html new file mode 100644 index 0000000..cb2ea35 --- /dev/null +++ b/openstack_dashboard/don/templates/don/ping.html @@ -0,0 +1,14 @@ +
+
+
+
+ {% csrf_token %} + DON: Ping Tracer +
+ {{ form }} +
+ +
+
+
+
diff --git a/openstack_dashboard/don/templates/don/test.png b/openstack_dashboard/don/templates/don/test.png new file mode 100644 index 0000000..9a0d49b Binary files /dev/null and b/openstack_dashboard/don/templates/don/test.png differ diff --git a/openstack_dashboard/don/templates/don/view.html b/openstack_dashboard/don/templates/don/view.html new file mode 100644 index 0000000..e22163d --- /dev/null +++ b/openstack_dashboard/don/templates/don/view.html @@ -0,0 +1,47 @@ + + + + DON: Internal View + + + + +
+ + + + + + + + + +
+ DON: Internal View +
+ +     + +     + +
+
+ + + diff --git a/openstack_dashboard/local/enabled/_7000_don.py b/openstack_dashboard/local/enabled/_7000_don.py new file mode 100644 index 0000000..a13d101 --- /dev/null +++ b/openstack_dashboard/local/enabled/_7000_don.py @@ -0,0 +1,29 @@ +# 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. + +# The slug of the dashboard to be added to HORIZON['dashboards']. Required. +DASHBOARD = 'don' +# If set to True, this dashboard will be set as the default dashboard. +DEFAULT = False +# A dictionary of exception classes to be added to HORIZON['exceptions']. +ADD_EXCEPTIONS = {} +# A list of applications to be added to INSTALLED_APPS. +ADD_INSTALLED_APPS = ['don'] + +ADD_ANGULAR_MODULES = [ + # 'horizon.dashboard.don', +] + +AUTO_DISCOVER_STATIC_FILES = True + + +# WEBROOT = '/' diff --git a/requirements.txt b/requirements.txt index 30806d5..6a2f55b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ # process, which may cause wedges in the gate later. pbr>=1.6 +Django<1.9,>=1.8 diff --git a/static/don/CollapsibleLists.js b/static/don/CollapsibleLists.js new file mode 100644 index 0000000..15e3e09 --- /dev/null +++ b/static/don/CollapsibleLists.js @@ -0,0 +1,152 @@ +/* + +CollapsibleLists.js + +An object allowing lists to dynamically expand and collapse + +Created by Stephen Morley - http://code.stephenmorley.org/ - and released under +the terms of the CC0 1.0 Universal legal code: + +http://creativecommons.org/publicdomain/zero/1.0/legalcode + +*/ + +// create the CollapsibleLists object +var CollapsibleLists = + new function(){ + + /* Makes all lists with the class 'collapsibleList' collapsible. The + * parameter is: + * + * doNotRecurse - true if sub-lists should not be made collapsible + */ + this.apply = function(doNotRecurse){ + + // loop over the unordered lists + var uls = document.getElementsByTagName('ul'); + for (var index = 0; index < uls.length; index ++){ + + // check whether this list should be made collapsible + if (uls[index].className.match(/(^| )collapsibleList( |$)/)){ + + // make this list collapsible + this.applyTo(uls[index], true); + + // check whether sub-lists should also be made collapsible + if (!doNotRecurse){ + + // add the collapsibleList class to the sub-lists + var subUls = uls[index].getElementsByTagName('ul'); + for (var subIndex = 0; subIndex < subUls.length; subIndex ++){ + subUls[subIndex].className += ' collapsibleList'; + } + + } + + } + + } + + }; + + /* Makes the specified list collapsible. The parameters are: + * + * node - the list element + * doNotRecurse - true if sub-lists should not be made collapsible + */ + this.applyTo = function(node, doNotRecurse){ + + // loop over the list items within this node + var lis = node.getElementsByTagName('li'); + for (var index = 0; index < lis.length; index ++){ + + // check whether this list item should be collapsible + if (!doNotRecurse || node == lis[index].parentNode){ + + // prevent text from being selected unintentionally + if (lis[index].addEventListener){ + lis[index].addEventListener( + 'mousedown', function (e){ e.preventDefault(); }, false); + }else{ + lis[index].attachEvent( + 'onselectstart', function(){ event.returnValue = false; }); + } + + // add the click listener + if (lis[index].addEventListener){ + lis[index].addEventListener( + 'click', createClickListener(lis[index]), false); + }else{ + lis[index].attachEvent( + 'onclick', createClickListener(lis[index])); + } + + // close the unordered lists within this list item + toggle(lis[index]); + + } + + } + + }; + + /* Returns a function that toggles the display status of any unordered + * list elements within the specified node. The parameter is: + * + * node - the node containing the unordered list elements + */ + function createClickListener(node){ + + // return the function + return function(e){ + + // ensure the event object is defined + if (!e) e = window.event; + + // find the list item containing the target of the event + var li = (e.target ? e.target : e.srcElement); + while (li.nodeName != 'LI') li = li.parentNode; + + // toggle the state of the node if it was the target of the event + if (li == node) toggle(node); + + }; + + } + + /* Opens or closes the unordered list elements directly within the + * specified node. The parameter is: + * + * node - the node containing the unordered list elements + */ + function toggle(node){ + + // determine whether to open or close the unordered lists + var open = node.className.match(/(^| )collapsibleListClosed( |$)/); + + // loop over the unordered list elements with the node + var uls = node.getElementsByTagName('ul'); + for (var index = 0; index < uls.length; index ++){ + + // find the parent list item of this unordered list + var li = uls[index]; + while (li.nodeName != 'LI') li = li.parentNode; + + // style the unordered list if it is directly within this node + if (li == node) uls[index].style.display = (open ? 'block' : 'none'); + + } + + // remove the current class from the node + node.className = + node.className.replace( + /(^| )collapsibleList(Open|Closed)( |$)/, ''); + + // if the node contains unordered lists, set its class + if (uls.length > 0){ + node.className += ' collapsibleList' + (open ? 'Open' : 'Closed'); + } + + } + + }(); diff --git a/static/don/button-closed.png b/static/don/button-closed.png new file mode 100644 index 0000000..417eb2f Binary files /dev/null and b/static/don/button-closed.png differ diff --git a/static/don/button-open.png b/static/don/button-open.png new file mode 100644 index 0000000..ac4a6ef Binary files /dev/null and b/static/don/button-open.png differ diff --git a/static/don/button.png b/static/don/button.png new file mode 100644 index 0000000..631d734 Binary files /dev/null and b/static/don/button.png differ diff --git a/static/don/compute.dot b/static/don/compute.dot new file mode 100644 index 0000000..9c1414d --- /dev/null +++ b/static/don/compute.dot @@ -0,0 +1,112 @@ +digraph DON_compute { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled +subgraph cluster_VMs { +VMs [ shape = plaintext, label = < + + + + + + + + + + + + + + + + +
VMs
vm1vm2
private1private1
10.10.0.310.10.0.4
>]; + +} + +subgraph cluster_LinuxBridge { +LinuxBridge [ shape = plaintext, label = < + + + + + + + + + + + + + + + +
Linux Bridge
tap71ac5bef-7c
qbr8aa60600-7bqbr71ac5bef-7c
qvb8aa60600-7bqvb71ac5bef-7c
>]; + +} + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_compute_br_int { +compute_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + +
OVS br_int
[6] qvo8aa60600-7b[7] qvo71ac5bef-7c
VLAN tag:2VLAN tag:2
[1] patch-tun
>]; + +} + +subgraph cluster_compute_br_tun { +compute_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +ComputeNode:s -> VMs:n [color = "#0066cc", penwidth = "4", style="invis"] +VMs:101004:s -> LinuxBridge:tap71ac5bef_7c:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb8aa60600_7b:s -> compute_br_int:qvo8aa60600_7b:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb71ac5bef_7c:s -> compute_br_int:qvo71ac5bef_7c:n [color = "#0066cc", penwidth = "4"] +compute_br_int:patch_tun:s -> compute_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] + +} + diff --git a/static/don/compute.svg b/static/don/compute.svg new file mode 100644 index 0000000..e8d3f20 --- /dev/null +++ b/static/don/compute.svg @@ -0,0 +1,132 @@ + + + + + + +DON_compute + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +vm1 + + +vm2 + + +private1 + + +private1 + + +10.10.0.3 + + +10.10.0.4 + + + +LinuxBridge + + +Linux Bridge + + +tap71ac5bef-7c + + +qbr8aa60600-7b + + +qbr71ac5bef-7c + + +qvb8aa60600-7b + + +qvb71ac5bef-7c + + +VMs:101004:s->LinuxBridge:tap71ac5bef_7c:n + + + +compute_br_int + + +OVS br_int + + +[6] qvo8aa60600-7b + + +[7] qvo71ac5bef-7c + + +VLAN tag:2 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvb8aa60600_7b:s->compute_br_int:qvo8aa60600_7b:n + + + +LinuxBridge:qvb71ac5bef_7c:s->compute_br_int:qvo71ac5bef_7c:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + + diff --git a/static/don/don.css b/static/don/don.css new file mode 100644 index 0000000..57f264f --- /dev/null +++ b/static/don/don.css @@ -0,0 +1,211 @@ +body { + font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; + color: black; + background-color: #ffffff +} + +pre { + width: 500px; /* specify width */ + white-space: pre-wrap; /* CSS3 browsers */ + white-space: -moz-pre-wrap !important; /* 1999+ Mozilla */ + white-space: -pre-wrap; /* Opera 4 thru 6 */ + white-space: -o-pre-wrap; /* Opera 7 and up */ + /* word-wrap: break-word; */ /* IE 5.5+ and up */ + font-family: "Courier New", monospace; + line-height: 1.6em; + margin: 1.7em 0 1.7em 0.3em; + font-size: 12px; + /* overflow-x: auto; */ /* Firefox 2 only */ + width: 90%; /* only if needed */ +} + +h2 { + font-family: Georgia, Helvetica,san-Serif; + font-weight: bold; + font-size: 20pt; + /*color:#fffff0;*/ + color: #2A4E68; + padding: 5px; + margin: 20px 0px 0px 0px; + /*background:#3399ff;*/ + /*border-radius: 20px;*/ + /* + font-family: "Helvetica Neue", Helvetica, Geneva, Arial, + SunSans-Regular, sans-serif; + padding: 0px; + font-weight: normal; + color: #2A4E68; + border-top: 1px solid black; + #border-bottom: 0.5px solid black; + #background: #ff9933; + #border-radius: 10px; + width:750px; + */ +} + +h3 { + font-family: "Helvetica Neue", Helvetica, Geneva, Arial, + SunSans-Regular, sans-serif; + margin: 0px 0px 0px 0px; + padding: 5px; + font-weight: normal; + font-style: italic; + color: #ff9933; + border-bottom: 0.5px solid black; + width:750px; /*Change this to whatever value that you want*/ +} + + + + +pre.pass { + border-left: 2px solid #009900; +} +pre.fail { + border-left: 2px solid #f00000; +} + +font.pass {color:#00cc00; text-decoration:none;} +font.fail {color:#f00000; font-weight:bold; text-decoration:none;} + +.collapsibleList li{ + list-style-image:url('button.png'); + cursor:auto; + margin:0 0 5px 0; +} + +li.collapsibleListOpen{ + list-style-image:url('button-open.png'); + cursor:pointer; + margin:0 0 5px 0; +} + +li.collapsibleListClosed{ + list-style-image:url('button-closed.png'); + cursor:pointer; +} + +.message { + position: absolute; + display: none; + border: 0px solid red; + width:80px; + height:80px; + padding: 0px; + float: top; + font-size: 8pt; +} + +#main { + width:1000px; + float:left; + border: 0px solid red; +} + +#internal { + width:1800px; + float:left; + border: 0px solid red; +} + + + +table +{ + font-family: Helvetica,san-Serif; + font-weight: normal; + font-size: large; + border: 0px solid blue; + padding: 0px; + border-spacing:0px; + border-collapse:collapse; +} + +th { + font-family: Georgia, Helvetica,san-Serif; + font-weight: bold; + font-size: 20pt; + /*color:#fffff0;*/ + color: #2A4E68; + padding: 5px; + /*background:#3399ff;*/ + /*border-radius: 20px;*/ +} + +tr { + font-family: Helvetica,san-Serif; + font-weight: normal; + font-size: large; + padding: 0px; + vertical-align: top; +} + +td { + font-family: Helvetica,san-Serif; + font-weight: normal; + font-size: large; + vertical-align: middle; + padding: 5px; + + border-style: solid; + border-width: 0px; + border-color: black; +} + +td.clickme { + font-family: Helvetica,san-Serif; + font-weight: normal; + font-size: large; + vertical-align: bottom; + padding: 20px; + /*background:#e5e5e5;*/ + background:#ffffff; + border-radius: 15px; + border-style: solid; + border-width: 0px; + border-color: black; + text-align: center; + } + +.myButton { + -moz-box-shadow: 0px 10px 14px -7px #3e7327; + -webkit-box-shadow: 0px 10px 14px -7px #3e7327; + box-shadow: 0px 10px 14px -7px #3e7327; + background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #77b55a), color-stop(1, #72b352)); + background:-moz-linear-gradient(top, #77b55a 5%, #72b352 100%); + background:-webkit-linear-gradient(top, #77b55a 5%, #72b352 100%); + background:-o-linear-gradient(top, #77b55a 5%, #72b352 100%); + background:-ms-linear-gradient(top, #77b55a 5%, #72b352 100%); + background:linear-gradient(to bottom, #77b55a 5%, #72b352 100%); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#77b55a', endColorstr='#72b352',GradientType=0); + background-color:#77b55a; + -moz-border-radius:4px; + -webkit-border-radius:4px; + border-radius:4px; + border:1px solid #4b8f29; + display:inline-block; + cursor:pointer; + color:#ffffff; + font-family:Arial; + font-size:20px; + font-weight:bold; + padding:6px 12px; + text-decoration:none; + text-shadow:0px 1px 0px #5b8a3c; +} +.myButton:hover { + background:-webkit-gradient(linear, left top, left bottom, color-stop(0.05, #72b352), color-stop(1, #77b55a)); + background:-moz-linear-gradient(top, #72b352 5%, #77b55a 100%); + background:-webkit-linear-gradient(top, #72b352 5%, #77b55a 100%); + background:-o-linear-gradient(top, #72b352 5%, #77b55a 100%); + background:-ms-linear-gradient(top, #72b352 5%, #77b55a 100%); + background:linear-gradient(to bottom, #72b352 5%, #77b55a 100%); + filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#72b352', endColorstr='#77b55a',GradientType=0); + background-color:#72b352; +} +.myButton:active { + position:relative; + top:1px; +} + + diff --git a/static/don/don.dot b/static/don/don.dot new file mode 100644 index 0000000..7805fde --- /dev/null +++ b/static/don/don.dot @@ -0,0 +1,200 @@ +digraph DON_DON { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_DONComputeNode { +style=filled +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled +subgraph cluster_VMs { +VMs [ shape = plaintext, label = < + + + + + + + + + + + + + + + + +
VMs
vm1vm2
private1private1
10.10.0.310.10.0.4
>]; + +} + +subgraph cluster_LinuxBridge { +LinuxBridge [ shape = plaintext, label = < + + + + + + + + + + + + + + + +
Linux Bridge
tap71ac5bef-7c
qbr8aa60600-7bqbr71ac5bef-7c
qvb8aa60600-7bqvb71ac5bef-7c
>]; + +} + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_compute_br_int { +compute_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + +
OVS br_int
[6] qvo8aa60600-7b[7] qvo71ac5bef-7c
VLAN tag:2VLAN tag:2
[1] patch-tun
>]; + +} + +subgraph cluster_compute_br_tun { +compute_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +ComputeNode:s -> VMs:n [color = "#0066cc", penwidth = "4", style="invis"] +VMs:101004:s -> LinuxBridge:tap71ac5bef_7c:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb8aa60600_7b:s -> compute_br_int:qvo8aa60600_7b:n [color = "#0066cc", penwidth = "4"] +LinuxBridge:qvb71ac5bef_7c:s -> compute_br_int:qvo71ac5bef_7c:n [color = "#0066cc", penwidth = "4"] +compute_br_int:patch_tun:s -> compute_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +} + +subgraph cluster_DONNetworkNode { +style=filled +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + +
OVS br_ex
router1
172.24.4.3/24
[2] qg-757bf552-73
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-43b83157-3b[10] tap1e1c73c9-35
VLAN tag:2VLAN tag:2
10.10.0.1/2410.10.0.5/24
private1
router1
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#0066cc", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_757bf552_73:s -> network_br_int:qr_43b83157_3b:n [color = "#0066cc", penwidth = "4"] +} + + +} + diff --git a/static/don/don.report.html b/static/don/don.report.html new file mode 100644 index 0000000..c299da7 --- /dev/null +++ b/static/don/don.report.html @@ -0,0 +1,144 @@ + + + + +DON: Analysis Results + + +

OVS Test Results

+

OVS test between all pairs of ports using the same tag in br-int

+ +OVERALL RESULT: +

Ping Test Results

+

Ping test between all pairs of VMs

+ +OVERALL RESULT: + + diff --git a/static/don/don.svg b/static/don/don.svg new file mode 100644 index 0000000..c3313dc --- /dev/null +++ b/static/don/don.svg @@ -0,0 +1,225 @@ + + + + + + +DON_DON + +cluster_DONComputeNode + + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + +cluster_DONNetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + +cluster_NetworkNode + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +vm1 + + +vm2 + + +private1 + + +private1 + + +10.10.0.3 + + +10.10.0.4 + + + +LinuxBridge + + +Linux Bridge + + +tap71ac5bef-7c + + +qbr8aa60600-7b + + +qbr71ac5bef-7c + + +qvb8aa60600-7b + + +qvb71ac5bef-7c + + +VMs:101004:s->LinuxBridge:tap71ac5bef_7c:n + + + +compute_br_int + + +OVS br_int + + +[6] qvo8aa60600-7b + + +[7] qvo71ac5bef-7c + + +VLAN tag:2 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvb8aa60600_7b:s->compute_br_int:qvo8aa60600_7b:n + + + +LinuxBridge:qvb71ac5bef_7c:s->compute_br_int:qvo71ac5bef_7c:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +172.24.4.3/24 + + +[2] qg-757bf552-73 + + + +network_br_int + + +OVS br_int + + +[8] qr-43b83157-3b + + +[10] tap1e1c73c9-35 + + +VLAN tag:2 + + +VLAN tag:2 + + +10.10.0.1/24 + + +10.10.0.5/24 + + +private1 + + +router1 + + +[1] patch-tun + + +br_ex:qg_757bf552_73:s->network_br_int:qr_43b83157_3b:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/static/don/network.dot b/static/don/network.dot new file mode 100644 index 0000000..64a8d1f --- /dev/null +++ b/static/don/network.dot @@ -0,0 +1,92 @@ +digraph DON_network { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + +
OVS br_ex
router1
172.24.4.3/24
[2] qg-757bf552-73
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-43b83157-3b[10] tap1e1c73c9-35
VLAN tag:2VLAN tag:2
10.10.0.1/2410.10.0.5/24
private1
router1
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#0066cc", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#0066cc", penwidth = "4"] +br_ex:qg_757bf552_73:s -> network_br_int:qr_43b83157_3b:n [color = "#0066cc", penwidth = "4"] + +} + diff --git a/static/don/network.svg b/static/don/network.svg new file mode 100644 index 0000000..2f68000 --- /dev/null +++ b/static/don/network.svg @@ -0,0 +1,100 @@ + + + + + + +DON_network + +cluster_NetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +172.24.4.3/24 + + +[2] qg-757bf552-73 + + + +network_br_int + + +OVS br_int + + +[8] qr-43b83157-3b + + +[10] tap1e1c73c9-35 + + +VLAN tag:2 + + +VLAN tag:2 + + +10.10.0.1/24 + + +10.10.0.5/24 + + +private1 + + +router1 + + +[1] patch-tun + + +br_ex:qg_757bf552_73:s->network_br_int:qr_43b83157_3b:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/static/don/ping.dot b/static/don/ping.dot new file mode 100644 index 0000000..ff9d77d --- /dev/null +++ b/static/don/ping.dot @@ -0,0 +1,278 @@ +digraph DON_DON { +graph [fontsize=10 fontname="Helvetica"]; +node [fontsize=10 fontname="Helvetica"]; +rankdir = TB; +ranksep = 1; +concentrate = true; +compound = true; +edge [dir=none] + +subgraph cluster_DONComputeNode { +style=filled +subgraph cluster_ComputeNode { +ComputeNode [ shape = plaintext, label = < + + + + +
Compute Node
>]; + +} + +subgraph cluster_Nova { +style=filled +subgraph cluster_VMs { +VMs [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VMs
VM1-1VM1-2VM3-1VM3-2VM4
private1private1private2private2publicprivate2private1
10.0.2.310.0.2.410.0.3.310.0.3.4172.24.4.510.0.3.610.0.2.6
>]; + +} + +subgraph cluster_LinuxBridge { +LinuxBridge [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Linux Bridge
tape0d697f2-cbtapbd4f1f72-5ftapbd96ca7d-5etap4441e3a6-f2tapce3d7b20-1dtapf0841d56-02tapfbb76083-60
qbre0d697f2-cbqbrbd4f1f72-5fqbrbd96ca7d-5eqbr4441e3a6-f2qbrce3d7b20-1dqbrf0841d56-02qbrfbb76083-60
qvbe0d697f2-cbqvbbd4f1f72-5fqvbbd96ca7d-5eqvb4441e3a6-f2qvbce3d7b20-1dqvbf0841d56-02qvbfbb76083-60
>]; + +} + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_compute_br_int { +compute_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[9] qvoe0d697f2-cb[10] qvobd4f1f72-5f[11] qvobd96ca7d-5e[12] qvo4441e3a6-f2[17] qvoce3d7b20-1d[16] qvof0841d56-02[15] qvofbb76083-60
VLAN tag:2VLAN tag:2VLAN tag:3VLAN tag:3VLAN tag:4VLAN tag:3VLAN tag:2
[1] patch-tun
>]; + +} + +subgraph cluster_compute_br_tun { +compute_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +ComputeNode:s -> VMs:n [color = "#909090", penwidth = "4", style="invis"] +VMs:10023:s -> LinuxBridge:tape0d697f2_cb:n [color = "#009900", penwidth = "4"] +VMs:10024:s -> LinuxBridge:tapbd4f1f72_5f:n [color = "#909090", penwidth = "4"] +VMs:10033:s -> LinuxBridge:tapbd96ca7d_5e:n [color = "#009900", penwidth = "4"] +VMs:10034:s -> LinuxBridge:tap4441e3a6_f2:n [color = "#909090", penwidth = "4"] +VMs:1722445:s -> LinuxBridge:tapce3d7b20_1d:n [color = "#909090", penwidth = "4"] +VMs:10036:s -> LinuxBridge:tapf0841d56_02:n [color = "#909090", penwidth = "4"] +VMs:10026:s -> LinuxBridge:tapfbb76083_60:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbe0d697f2_cb:s -> compute_br_int:qvoe0d697f2_cb:n [color = "#009900", penwidth = "4"] +LinuxBridge:qvbbd4f1f72_5f:s -> compute_br_int:qvobd4f1f72_5f:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbbd96ca7d_5e:s -> compute_br_int:qvobd96ca7d_5e:n [color = "#009900", penwidth = "4"] +LinuxBridge:qvb4441e3a6_f2:s -> compute_br_int:qvo4441e3a6_f2:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbce3d7b20_1d:s -> compute_br_int:qvoce3d7b20_1d:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbf0841d56_02:s -> compute_br_int:qvof0841d56_02:n [color = "#909090", penwidth = "4"] +LinuxBridge:qvbfbb76083_60:s -> compute_br_int:qvofbb76083_60:n [color = "#909090", penwidth = "4"] +compute_br_int:patch_tun:s -> compute_br_tun:patch_int:n [color = "#909090", penwidth = "4"] +} + +subgraph cluster_DONNetworkNode { +style=filled +subgraph cluster_NetworkNode { +NetworkNode [ shape = plaintext, label = < + + + + +
Network Node
>]; + +} + +subgraph cluster_OVS { +style=filled +subgraph cluster_br_ex { +br_ex [ shape = plaintext, label = < + + + + + + + + + + + + + + + + +
OVS br_ex
router1router2
172.24.4.3/24172.24.4.4/24
[2] qg-eb8796fb-83[3] qg-e2b1b0d3-a8
>]; + +} + +subgraph cluster_network_br_int { +network_br_int [ shape = plaintext, label = < + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OVS br_int
[8] qr-09a15e37-ca[6] tapd0828ef0-eb[7] qr-622abba5-e2[5] tapd6f091a2-c0[13] qr-361be2af-e5[5] tapd6f091a2-c0[14] qr-b66b902a-36[6] tapd0828ef0-eb
VLAN tag:3VLAN tag:3VLAN tag:2VLAN tag:2VLAN tag:2VLAN tag:2VLAN tag:3VLAN tag:3
10.0.3.1/2410.0.3.2/2410.0.2.1/2410.0.2.2/2410.0.2.5/2410.0.2.2/2410.0.3.5/2410.0.3.2/24
private2private1private1private2
router1router2
[1] patch-tun
>]; + +} + +subgraph cluster_network_br_tun { +network_br_tun [ shape = plaintext, label = < + + + + + + + +
OVS br_tun
[1] patch-int
>]; + +} + +} + +NetworkNode:s -> br_ex:n [color = "#909090", penwidth = "4", style="invis"] +network_br_int:patch_tun:s -> network_br_tun:patch_int:n [color = "#909090", penwidth = "4"] +br_ex:qg_e2b1b0d3_a8:s -> network_br_int:qr_361be2af_e5:n [color = "#909090", penwidth = "4"] +br_ex:qg_e2b1b0d3_a8:s -> network_br_int:qr_b66b902a_36:n [color = "#909090", penwidth = "4"] +br_ex:qg_eb8796fb_83:s -> network_br_int:qr_09a15e37_ca:n [color = "#909090", penwidth = "4"] +br_ex:qg_eb8796fb_83:s -> network_br_int:qr_622abba5_e2:n [color = "#909090", penwidth = "4"] +} + + +} + diff --git a/static/don/ping.svg b/static/don/ping.svg new file mode 100644 index 0000000..9fa6752 --- /dev/null +++ b/static/don/ping.svg @@ -0,0 +1,473 @@ + + + + + + +DON_DON + +cluster_DONComputeNode + + +cluster_ComputeNode + + +cluster_Nova + + +cluster_VMs + + +cluster_LinuxBridge + + +cluster_OVS + + +cluster_compute_br_int + + +cluster_compute_br_tun + + +cluster_DONNetworkNode + + +cluster_OVS + + +cluster_br_ex + + +cluster_network_br_int + + +cluster_network_br_tun + + +cluster_NetworkNode + + + +ComputeNode + + +Compute Node + + +VMs + + +VMs + + +VM1-1 + + +VM1-2 + + +VM3-1 + + +VM3-2 + + +VM4 + + +private1 + + +private1 + + +private2 + + +private2 + + +public + + +private2 + + +private1 + + +10.0.2.3 + + +10.0.2.4 + + +10.0.3.3 + + +10.0.3.4 + + +172.24.4.5 + + +10.0.3.6 + + +10.0.2.6 + + + +LinuxBridge + + +Linux Bridge + + +tape0d697f2-cb + + +tapbd4f1f72-5f + + +tapbd96ca7d-5e + + +tap4441e3a6-f2 + + +tapce3d7b20-1d + + +tapf0841d56-02 + + +tapfbb76083-60 + + +qbre0d697f2-cb + + +qbrbd4f1f72-5f + + +qbrbd96ca7d-5e + + +qbr4441e3a6-f2 + + +qbrce3d7b20-1d + + +qbrf0841d56-02 + + +qbrfbb76083-60 + + +qvbe0d697f2-cb + + +qvbbd4f1f72-5f + + +qvbbd96ca7d-5e + + +qvb4441e3a6-f2 + + +qvbce3d7b20-1d + + +qvbf0841d56-02 + + +qvbfbb76083-60 + + +VMs:10023:s->LinuxBridge:tape0d697f2_cb:n + + + +VMs:10024:s->LinuxBridge:tapbd4f1f72_5f:n + + + +VMs:10033:s->LinuxBridge:tapbd96ca7d_5e:n + + + +VMs:10034:s->LinuxBridge:tap4441e3a6_f2:n + + + +VMs:1722445:s->LinuxBridge:tapce3d7b20_1d:n + + + +VMs:10036:s->LinuxBridge:tapf0841d56_02:n + + + +VMs:10026:s->LinuxBridge:tapfbb76083_60:n + + + +compute_br_int + + +OVS br_int + + +[9] qvoe0d697f2-cb + + +[10] qvobd4f1f72-5f + + +[11] qvobd96ca7d-5e + + +[12] qvo4441e3a6-f2 + + +[17] qvoce3d7b20-1d + + +[16] qvof0841d56-02 + + +[15] qvofbb76083-60 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:4 + + +VLAN tag:3 + + +VLAN tag:2 + + +[1] patch-tun + + +LinuxBridge:qvbe0d697f2_cb:s->compute_br_int:qvoe0d697f2_cb:n + + + +LinuxBridge:qvbbd4f1f72_5f:s->compute_br_int:qvobd4f1f72_5f:n + + + +LinuxBridge:qvbbd96ca7d_5e:s->compute_br_int:qvobd96ca7d_5e:n + + + +LinuxBridge:qvb4441e3a6_f2:s->compute_br_int:qvo4441e3a6_f2:n + + + +LinuxBridge:qvbce3d7b20_1d:s->compute_br_int:qvoce3d7b20_1d:n + + + +LinuxBridge:qvbf0841d56_02:s->compute_br_int:qvof0841d56_02:n + + + +LinuxBridge:qvbfbb76083_60:s->compute_br_int:qvofbb76083_60:n + + + +compute_br_tun + + +OVS br_tun + + +[1] patch-int + + +compute_br_int:patch_tun:s->compute_br_tun:patch_int:n + + + +NetworkNode + + +Network Node + + +br_ex + + +OVS br_ex + + +router1 + + +router2 + + +172.24.4.3/24 + + +172.24.4.4/24 + + +[2] qg-eb8796fb-83 + + +[3] qg-e2b1b0d3-a8 + + + +network_br_int + + +OVS br_int + + +[8] qr-09a15e37-ca + + +[6] tapd0828ef0-eb + + +[7] qr-622abba5-e2 + + +[5] tapd6f091a2-c0 + + +[13] qr-361be2af-e5 + + +[5] tapd6f091a2-c0 + + +[14] qr-b66b902a-36 + + +[6] tapd0828ef0-eb + + +VLAN tag:3 + + +VLAN tag:3 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:2 + + +VLAN tag:3 + + +VLAN tag:3 + + +10.0.3.1/24 + + +10.0.3.2/24 + + +10.0.2.1/24 + + +10.0.2.2/24 + + +10.0.2.5/24 + + +10.0.2.2/24 + + +10.0.3.5/24 + + +10.0.3.2/24 + + +private2 + + +private1 + + +private1 + + +private2 + + +router1 + + +router2 + + +[1] patch-tun + + +br_ex:qg_e2b1b0d3_a8:s->network_br_int:qr_361be2af_e5:n + + + +br_ex:qg_e2b1b0d3_a8:s->network_br_int:qr_b66b902a_36:n + + + +br_ex:qg_eb8796fb_83:s->network_br_int:qr_09a15e37_ca:n + + + +br_ex:qg_eb8796fb_83:s->network_br_int:qr_622abba5_e2:n + + + +network_br_tun + + +OVS br_tun + + +[1] patch-int + + +network_br_int:patch_tun:s->network_br_tun:patch_int:n + + + + diff --git a/test-requirements.txt b/test-requirements.txt index d354162..5423631 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -2,7 +2,8 @@ # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. -hacking<0.11,>=0.10.0 +#hacking<0.11,>=0.10.0 +flake8<2.6.0,>=2.5.4 coverage>=3.6 python-subunit>=0.0.18 diff --git a/tox.ini b/tox.ini index 1f9b76f..325433b 100644 --- a/tox.ini +++ b/tox.ini @@ -59,6 +59,6 @@ commands = oslo_debug_helper {posargs} # E123, E125 skipped as they are invalid PEP-8. show-source = True -ignore = E123,E125 +ignore = E123,E125,H101,H302,H803,W601,E501 builtins = _ -exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build +exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,local_settings.py