From 508920a12199a40f2a22fcf917e5c1d3d9dfbc4e Mon Sep 17 00:00:00 2001 From: Ramaraja Date: Mon, 1 Aug 2016 10:03:58 +0000 Subject: [PATCH] Adding DON changes to the repo Change-Id: I694a3483cfddd5cff8d0a95466087b0e0e9897ba --- etc/don/don.conf | 6 + openstack_dashboard/don/__init__.py | 0 openstack_dashboard/don/api.py | 22 + openstack_dashboard/don/archive/__init__.py | 0 openstack_dashboard/don/archive/panel.py | 10 + .../don/archive/templates/archive/index.html | 11 + .../don/archive/templates/archive/view.html | 11 + openstack_dashboard/don/archive/urls.py | 13 + openstack_dashboard/don/archive/views.py | 49 + openstack_dashboard/don/dashboard.py | 13 + openstack_dashboard/don/models.py | 11 + openstack_dashboard/don/ovs/__init__.py | 0 openstack_dashboard/don/ovs/admin-openrc.sh | 37 + openstack_dashboard/don/ovs/analyzer.py | 412 ++++++++ openstack_dashboard/don/ovs/collector.py | 965 ++++++++++++++++++ openstack_dashboard/don/ovs/common.py | 295 ++++++ openstack_dashboard/don/ovs/credentials.yaml | 24 + openstack_dashboard/don/ovs/don.json | 1 + openstack_dashboard/don/ovs/forms.py | 10 + openstack_dashboard/don/ovs/load_json.py | 11 + openstack_dashboard/don/ovs/ovs.py | 176 ++++ openstack_dashboard/don/ovs/panel.py | 12 + openstack_dashboard/don/ovs/path.py | 496 +++++++++ openstack_dashboard/don/ovs/ping.py | 122 +++ openstack_dashboard/don/ovs/plot.py | 898 ++++++++++++++++ openstack_dashboard/don/ovs/run_nms_cmd.py | 92 ++ .../don/ovs/static/CollapsibleLists.js | 152 +++ .../don/ovs/static/button-closed.png | Bin 0 -> 256 bytes .../don/ovs/static/button-open.png | Bin 0 -> 240 bytes openstack_dashboard/don/ovs/static/button.png | Bin 0 -> 230 bytes .../don/ovs/static/compute.dot | 21 + .../don/ovs/static/compute.svg | 111 ++ .../don/ovs/static/dashboard/ovs/view.html | 31 + openstack_dashboard/don/ovs/static/don.css | 212 ++++ openstack_dashboard/don/ovs/static/don.dot | 198 ++++ openstack_dashboard/don/ovs/static/don.html | 31 + .../don/ovs/static/don.report.html | 144 +++ openstack_dashboard/don/ovs/static/don.svg | 204 ++++ .../don/ovs/static/don_analysis.png | Bin 0 -> 54812 bytes .../don/ovs/static/don_internal.png | Bin 0 -> 102281 bytes .../don/ovs/static/don_ping_notworking.png | Bin 0 -> 96654 bytes .../don/ovs/static/don_test.dot | 278 +++++ .../don/ovs/static/don_test.svg | 473 +++++++++ .../don/ovs/static/net_topology.png | Bin 0 -> 79590 bytes .../don/ovs/static/network.dot | 92 ++ .../don/ovs/static/network.svg | 100 ++ .../openstack-cloud-software-vertical-web.png | Bin 0 -> 9804 bytes openstack_dashboard/don/ovs/static/path.html | 27 + openstack_dashboard/don/ovs/static/ping.dot | 278 +++++ openstack_dashboard/don/ovs/static/ping.html | 1 + openstack_dashboard/don/ovs/static/ping.svg | 473 +++++++++ openstack_dashboard/don/ovs/static/view.html | 31 + .../don/ovs/templates/ovs/analyze.html | 15 + .../don/ovs/templates/ovs/index.html | 15 + .../don/ovs/templates/ovs/path.html | 15 + .../don/ovs/templates/ovs/ping.html | 15 + .../don/ovs/templates/ovs/views.html | 15 + openstack_dashboard/don/ovs/urls.py | 18 + openstack_dashboard/don/ovs/views.py | 233 +++++ .../don/static/dashboard/don/ovs/view.html | 31 + .../don/static/don/ovs/compute.dot | 21 + .../don/static/don/ovs/compute.svg | 111 ++ .../don/static/don/ovs/don.dot | 198 ++++ .../don/static/don/ovs/don.svg | 204 ++++ .../don/static/don/ovs/network.dot | 92 ++ .../don/static/don/ovs/network.svg | 100 ++ .../don/static/don/ovs/ping.dot | 278 +++++ .../don/static/don/ovs/ping.svg | 473 +++++++++ .../don/static/don/ovs/view.html | 31 + openstack_dashboard/don/tables.py | 36 + .../don/templates/don/base.html | 11 + .../don/templates/don/don.html | 93 ++ .../don/templates/don/don.report.html | 144 +++ openstack_dashboard/don/templates/don/don.svg | 204 ++++ .../don/templates/don/path.html | 27 + .../don/templates/don/ping.html | 14 + .../don/templates/don/test.png | Bin 0 -> 103116 bytes .../don/templates/don/view.html | 47 + .../local/enabled/_7000_don.py | 29 + requirements.txt | 1 + static/don/CollapsibleLists.js | 152 +++ static/don/button-closed.png | Bin 0 -> 256 bytes static/don/button-open.png | Bin 0 -> 240 bytes static/don/button.png | Bin 0 -> 230 bytes static/don/compute.dot | 112 ++ static/don/compute.svg | 132 +++ static/don/don.css | 211 ++++ static/don/don.dot | 200 ++++ static/don/don.report.html | 144 +++ static/don/don.svg | 225 ++++ static/don/network.dot | 92 ++ static/don/network.svg | 100 ++ static/don/ping.dot | 278 +++++ static/don/ping.svg | 473 +++++++++ test-requirements.txt | 3 +- tox.ini | 4 +- 96 files changed, 11148 insertions(+), 3 deletions(-) create mode 100644 etc/don/don.conf create mode 100644 openstack_dashboard/don/__init__.py create mode 100644 openstack_dashboard/don/api.py create mode 100644 openstack_dashboard/don/archive/__init__.py create mode 100644 openstack_dashboard/don/archive/panel.py create mode 100644 openstack_dashboard/don/archive/templates/archive/index.html create mode 100644 openstack_dashboard/don/archive/templates/archive/view.html create mode 100644 openstack_dashboard/don/archive/urls.py create mode 100644 openstack_dashboard/don/archive/views.py create mode 100644 openstack_dashboard/don/dashboard.py create mode 100644 openstack_dashboard/don/models.py create mode 100644 openstack_dashboard/don/ovs/__init__.py create mode 100644 openstack_dashboard/don/ovs/admin-openrc.sh create mode 100644 openstack_dashboard/don/ovs/analyzer.py create mode 100644 openstack_dashboard/don/ovs/collector.py create mode 100644 openstack_dashboard/don/ovs/common.py create mode 100644 openstack_dashboard/don/ovs/credentials.yaml create mode 100644 openstack_dashboard/don/ovs/don.json create mode 100644 openstack_dashboard/don/ovs/forms.py create mode 100644 openstack_dashboard/don/ovs/load_json.py create mode 100644 openstack_dashboard/don/ovs/ovs.py create mode 100644 openstack_dashboard/don/ovs/panel.py create mode 100644 openstack_dashboard/don/ovs/path.py create mode 100644 openstack_dashboard/don/ovs/ping.py create mode 100644 openstack_dashboard/don/ovs/plot.py create mode 100644 openstack_dashboard/don/ovs/run_nms_cmd.py create mode 100644 openstack_dashboard/don/ovs/static/CollapsibleLists.js create mode 100644 openstack_dashboard/don/ovs/static/button-closed.png create mode 100644 openstack_dashboard/don/ovs/static/button-open.png create mode 100644 openstack_dashboard/don/ovs/static/button.png create mode 100644 openstack_dashboard/don/ovs/static/compute.dot create mode 100644 openstack_dashboard/don/ovs/static/compute.svg create mode 100644 openstack_dashboard/don/ovs/static/dashboard/ovs/view.html create mode 100644 openstack_dashboard/don/ovs/static/don.css create mode 100644 openstack_dashboard/don/ovs/static/don.dot create mode 100644 openstack_dashboard/don/ovs/static/don.html create mode 100644 openstack_dashboard/don/ovs/static/don.report.html create mode 100644 openstack_dashboard/don/ovs/static/don.svg create mode 100644 openstack_dashboard/don/ovs/static/don_analysis.png create mode 100644 openstack_dashboard/don/ovs/static/don_internal.png create mode 100644 openstack_dashboard/don/ovs/static/don_ping_notworking.png create mode 100644 openstack_dashboard/don/ovs/static/don_test.dot create mode 100644 openstack_dashboard/don/ovs/static/don_test.svg create mode 100644 openstack_dashboard/don/ovs/static/net_topology.png create mode 100644 openstack_dashboard/don/ovs/static/network.dot create mode 100644 openstack_dashboard/don/ovs/static/network.svg create mode 100644 openstack_dashboard/don/ovs/static/openstack-cloud-software-vertical-web.png create mode 100644 openstack_dashboard/don/ovs/static/path.html create mode 100644 openstack_dashboard/don/ovs/static/ping.dot create mode 100644 openstack_dashboard/don/ovs/static/ping.html create mode 100644 openstack_dashboard/don/ovs/static/ping.svg create mode 100644 openstack_dashboard/don/ovs/static/view.html create mode 100644 openstack_dashboard/don/ovs/templates/ovs/analyze.html create mode 100644 openstack_dashboard/don/ovs/templates/ovs/index.html create mode 100644 openstack_dashboard/don/ovs/templates/ovs/path.html create mode 100644 openstack_dashboard/don/ovs/templates/ovs/ping.html create mode 100644 openstack_dashboard/don/ovs/templates/ovs/views.html create mode 100644 openstack_dashboard/don/ovs/urls.py create mode 100644 openstack_dashboard/don/ovs/views.py create mode 100644 openstack_dashboard/don/static/dashboard/don/ovs/view.html create mode 100644 openstack_dashboard/don/static/don/ovs/compute.dot create mode 100644 openstack_dashboard/don/static/don/ovs/compute.svg create mode 100644 openstack_dashboard/don/static/don/ovs/don.dot create mode 100644 openstack_dashboard/don/static/don/ovs/don.svg create mode 100644 openstack_dashboard/don/static/don/ovs/network.dot create mode 100644 openstack_dashboard/don/static/don/ovs/network.svg create mode 100644 openstack_dashboard/don/static/don/ovs/ping.dot create mode 100644 openstack_dashboard/don/static/don/ovs/ping.svg create mode 100644 openstack_dashboard/don/static/don/ovs/view.html create mode 100644 openstack_dashboard/don/tables.py create mode 100644 openstack_dashboard/don/templates/don/base.html create mode 100644 openstack_dashboard/don/templates/don/don.html create mode 100644 openstack_dashboard/don/templates/don/don.report.html create mode 100644 openstack_dashboard/don/templates/don/don.svg create mode 100644 openstack_dashboard/don/templates/don/path.html create mode 100644 openstack_dashboard/don/templates/don/ping.html create mode 100644 openstack_dashboard/don/templates/don/test.png create mode 100644 openstack_dashboard/don/templates/don/view.html create mode 100644 openstack_dashboard/local/enabled/_7000_don.py create mode 100644 static/don/CollapsibleLists.js create mode 100644 static/don/button-closed.png create mode 100644 static/don/button-open.png create mode 100644 static/don/button.png create mode 100644 static/don/compute.dot create mode 100644 static/don/compute.svg create mode 100644 static/don/don.css create mode 100644 static/don/don.dot create mode 100644 static/don/don.report.html create mode 100644 static/don/don.svg create mode 100644 static/don/network.dot create mode 100644 static/don/network.svg create mode 100644 static/don/ping.dot create mode 100644 static/don/ping.svg 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 0000000000000000000000000000000000000000..417eb2fc40d844946cb18f0966073d9549b2682d GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X7J0fjhE&{YnRu4-h=M??`^DysoChD5DQRk`<~UBuW2qKc zziXdDcc-59hY!zd`yQXz^Ic!5%6zAqU-6V>n_mhrtlhfXuuURaI5PXG;W68mg4800 z-G-bOYuRTgKjzR_n)kX+IsWPo<0ApmnQQkS+m?HMM)ACd7Y{5Hj7qC3N#qpYl5P_0 zWxnG)w_L?GyGa5~OQuZTz%MUjB%e`n`M&%g4g-eh-O1-=TXk}Qu4V9a^>bP0l+XkK Dnpk0b literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ac4a6ef32a50516609a06733d3365c78237e2143 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XCV9FzhE&{|GBJ>gS&_#j`GUT_;*XoNPsnBTUtm6Y&+UWI zzhh}p_Fm4rZ#T9-;BoF*cdbf!(e5A-HSIvY)bAY6=4_uLWcl2u=-kP@-`8&amZQa? zcbGwU|ALtpHI%;ip7~?-g8%3Cn;OS5n38#<|C#vBUf-h_aen=WPb~a4CH+tKE*EHG o{QhC0^XI-+?dA4A1QHnTT=Tf{X8(i9K-V#Ny85}Sb4q9e0Cgu}D*ylh literal 0 HcmV?d00001 diff --git a/openstack_dashboard/don/ovs/static/button.png b/openstack_dashboard/don/ovs/static/button.png new file mode 100644 index 0000000000000000000000000000000000000000..631d734d136a3c0e7698e249b09ac06462589ca8 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XIy_w*Ln>}_1wcZrxMOpdTT1SY*0BaajE)#RCaoBVu2L* zl`q`0TYJ;m{DmgIKFEJ*``x_5|FhF>RLskN_)_w^#h<=_&?6G!7R%4e%r2L^v-9u4 c=?u&a+cKO!dpZ{#2fB&D)78&qol`;+0OL4aGynhq literal 0 HcmV?d00001 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

+
    +
  • OVS bridge br-int +
      +
    • tag 2 +
        +
      • 6 → 7 +
          +
          +'sudo ovs-appctl fdb/flush br-int'
          +'sudo ovs-appctl ofproto/trace br-int in_port=6,dl_src=AA:BB:CC:DD:EE:11,dl_dst=AA:BB:CC:DD:EE:22 -generate'
          +'sudo ovs-appctl fdb/show br-int'
          +'sudo ovs-appctl ofproto/trace br-int in_port=7,dl_src=AA:BB:CC:DD:EE:22,dl_dst=AA:BB:CC:DD:EE:11 -generate'
          +{
          +    "command_list": [
          +        {
          +            "cmd": "sudo ovs-appctl fdb/flush br-int", 
          +            "output": [
          +                "table successfully flushed", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl ofproto/trace br-int in_port=6,dl_src=AA:BB:CC:DD:EE:11,dl_dst=AA:BB:CC:DD:EE:22 -generate", 
          +            "output": [
          +                "Flow: metadata=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "Rule: table=0 cookie=0x9ecb964f4eaf403e priority=0", 
          +                "OpenFlow actions=NORMAL", 
          +                "no learned MAC for destination, flooding", 
          +                "", 
          +                "    Resubmitted flow: metadata=0,in_port=1,dl_vlan=2,dl_vlan_pcp=0,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "    Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "    Resubmitted  odp: push_vlan(vid=2,pcp=0),5,6,pop_vlan,7,9", 
          +                "    Rule: table=0 cookie=0x9ecb964f4eaf403e priority=1,in_port=1", 
          +                "    OpenFlow actions=resubmit(,2)", 
          +                "", 
          +                "        Resubmitted flow: unchanged", 
          +                "        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "        Resubmitted  odp: push_vlan(vid=2,pcp=0),5,6,pop_vlan,7,9", 
          +                "        Rule: table=2 cookie=0x9ecb964f4eaf403e priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00", 
          +                "        OpenFlow actions=resubmit(,20)", 
          +                "", 
          +                "            Resubmitted flow: unchanged", 
          +                "            Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "            Resubmitted  odp: push_vlan(vid=2,pcp=0),5,6,pop_vlan,7,9", 
          +                "            Rule: table=20 cookie=0x9ecb964f4eaf403e priority=0", 
          +                "            OpenFlow actions=resubmit(,22)", 
          +                "", 
          +                "                Resubmitted flow: unchanged", 
          +                "                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "                Resubmitted  odp: push_vlan(vid=2,pcp=0),5,6,pop_vlan,7,9", 
          +                "                Rule: table=22 cookie=0x9ecb964f4eaf403e priority=0", 
          +                "                OpenFlow actions=drop", 
          +                "", 
          +                "Final flow: metadata=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "Relevant fields: skb_priority=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000,nw_proto=0,nw_frag=no,tp_src=0", 
          +                "Datapath actions: push_vlan(vid=2,pcp=0),5,6,pop_vlan,7,9,12", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl fdb/show br-int", 
          +            "output": [
          +                " port  VLAN  MAC                Age", 
          +                "    6     2  aa:bb:cc:dd:ee:11    0", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl ofproto/trace br-int in_port=7,dl_src=AA:BB:CC:DD:EE:22,dl_dst=AA:BB:CC:DD:EE:11 -generate", 
          +            "output": [
          +                "Flow: metadata=0,in_port=7,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:22,dl_dst=aa:bb:cc:dd:ee:11,dl_type=0x0000", 
          +                "Rule: table=0 cookie=0x9ecb964f4eaf403e priority=0", 
          +                "OpenFlow actions=NORMAL", 
          +                "forwarding to learned port", 
          +                "", 
          +                "Final flow: unchanged", 
          +                "Relevant fields: skb_priority=0,in_port=7,vlan_tci=0x0000/0x1fff,dl_src=aa:bb:cc:dd:ee:22,dl_dst=aa:bb:cc:dd:ee:11,dl_type=0x0000,nw_proto=0,nw_frag=no,tp_src=0", 
          +                "Datapath actions: 11", 
          +                ""
          +            ]
          +        }
          +    ], 
          +    "comment": "ovs br-int port 6 -->  7", 
          +    "debugs": [
          +        "AA:BB:CC:DD:EE:11 learnt on expected vlan 2 on port 6", 
          +        "Packet for learnt mac forwarded properly"
          +    ], 
          +    "errors": [
          +        "Packet forwarded to incorrect port 11, expected 6"
          +    ], 
          +    "pass": false
          +}
          +
          +      
        +
      • +
      +
    • +
    +
  • +
+OVERALL RESULT: +

Ping Test Results

+

Ping test between all pairs of VMs

+
    +
  • router1 (Namespace [qrouter-90111551-6cfc-4be0-b1c2-ce8bffb7edf6]) +
      +
    • vm1 → vm2 +
        +
      • 10.10.0.3 → 10.10.0.4 +
          +
          +{
          +    "command_list": [
          +        {
          +            "cmd": "ssh 10.10.0.3 with provided username and passwd", 
          +            "output": "Could not ssh to 10.10.0.3", 
          +            "pass": false
          +        }
          +    ], 
          +    "comment": "PING 10.10.0.3 to 10.10.0.4", 
          +    "errors": [], 
          +    "pass": false
          +}
          +
          +        
          +
        +
      • +
      +
    • +
    +
  • +
+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 0000000000000000000000000000000000000000..8d48b617475698c17557472b70714d91f998a989 GIT binary patch literal 54812 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 z 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..be3c0e0f05692acd72feafece83c9bae3992e7e4 GIT binary patch literal 102281 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�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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b7dfcfd9e5d01d3932304c1e658c4b9b67f3944f GIT binary patch literal 96654 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c3c225a5efee025c0be8b541865c39b4203893c7 GIT binary patch literal 79590 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

3Dchc^paChP-!mHjzx}SCuV%m=SnJ- z)e~pjtVlbLgXjFc!ejTuElX{1=x3CR3H?>SZ^)nrkd{(3P?LAW`8x?+=M4p5|E6qd z)bPN6b0oJ?Q7D=f)FX=F^;hd&Ff`=>@dmI^m2j)3AOq%r;_U zASohU3d+zD8|GL(qinyhPM`Jx{AiX04F8cF`S;^Jdv)pBWKm@kUi}k2{ro{G5P;98 zlCw36=7KE^g<~6X#RF{y7xSwd9v(74z7vdqAQj8NLFx93KG)BsRKRgTXfww67{6eL zC;Z%DL_h;k|6_?>`)Bsk8SJqp--7n_SCwh}_8XhT#?DzmM9cJNyG|N!WQ0pK+oiaV zt^FM?TPL#)t227{Mv)FpE`-7R{N%Sy@!q}EsH=|nVoT%ld+X0hkq+97upRz@?y#Ql zZ;gL`#Q**qV?A9LWUk0R|GO^#yAuDq3;y>!{688CGK?_WzxJOx{&++>t5XyCkxFu^ KvIWw&AN(I&?$p%) literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9ee6d77c3c30fc96b26d1001b325451323fe342e GIT binary patch literal 9804 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 literal 0 HcmV?d00001 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 + + + +

+ + + 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

+
    +
  • OVS bridge br-int +
      +
    • tag 2 +
        +
      • 6 → 7 +
          +
          +'sudo ovs-appctl fdb/flush br-int'
          +'sudo ovs-appctl ofproto/trace br-int in_port=6,dl_src=AA:BB:CC:DD:EE:11,dl_dst=AA:BB:CC:DD:EE:22 -generate'
          +'sudo ovs-appctl fdb/show br-int'
          +'sudo ovs-appctl ofproto/trace br-int in_port=7,dl_src=AA:BB:CC:DD:EE:22,dl_dst=AA:BB:CC:DD:EE:11 -generate'
          +{
          +    "command_list": [
          +        {
          +            "cmd": "sudo ovs-appctl fdb/flush br-int", 
          +            "output": [
          +                "table successfully flushed", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl ofproto/trace br-int in_port=6,dl_src=AA:BB:CC:DD:EE:11,dl_dst=AA:BB:CC:DD:EE:22 -generate", 
          +            "output": [
          +                "Flow: metadata=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "Rule: table=0 cookie=0x9ddbfd9c4f252cdc priority=0", 
          +                "OpenFlow actions=NORMAL", 
          +                "no learned MAC for destination, flooding", 
          +                "", 
          +                "    Resubmitted flow: metadata=0,in_port=1,dl_vlan=2,dl_vlan_pcp=0,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "    Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "    Resubmitted  odp: push_vlan(vid=2,pcp=0),6,5,pop_vlan,8,9,push_vlan(vid=2,pcp=0),10", 
          +                "    Rule: table=0 cookie=0x9ddbfd9c4f252cdc priority=1,in_port=1", 
          +                "    OpenFlow actions=resubmit(,2)", 
          +                "", 
          +                "        Resubmitted flow: unchanged", 
          +                "        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "        Resubmitted  odp: push_vlan(vid=2,pcp=0),6,5,pop_vlan,8,9,push_vlan(vid=2,pcp=0),10", 
          +                "        Rule: table=2 cookie=0x9ddbfd9c4f252cdc priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00", 
          +                "        OpenFlow actions=resubmit(,20)", 
          +                "", 
          +                "            Resubmitted flow: unchanged", 
          +                "            Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "            Resubmitted  odp: push_vlan(vid=2,pcp=0),6,5,pop_vlan,8,9,push_vlan(vid=2,pcp=0),10", 
          +                "            Rule: table=20 cookie=0x9ddbfd9c4f252cdc priority=0", 
          +                "            OpenFlow actions=resubmit(,22)", 
          +                "", 
          +                "                Resubmitted flow: unchanged", 
          +                "                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "                Resubmitted  odp: push_vlan(vid=2,pcp=0),6,5,pop_vlan,8,9,push_vlan(vid=2,pcp=0),10", 
          +                "                Rule: table=22 cookie=0x9ddbfd9c4f252cdc priority=0", 
          +                "                OpenFlow actions=drop", 
          +                "", 
          +                "Final flow: metadata=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "Relevant fields: skb_priority=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000,nw_proto=0,nw_frag=no,tp_src=0", 
          +                "Datapath actions: push_vlan(vid=2,pcp=0),6,5,pop_vlan,8,9,push_vlan(vid=2,pcp=0),10,pop_vlan,14", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl fdb/show br-int", 
          +            "output": [
          +                " port  VLAN  MAC                Age", 
          +                "    6     2  aa:bb:cc:dd:ee:11    0", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl ofproto/trace br-int in_port=7,dl_src=AA:BB:CC:DD:EE:22,dl_dst=AA:BB:CC:DD:EE:11 -generate", 
          +            "output": [
          +                "Flow: metadata=0,in_port=7,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:22,dl_dst=aa:bb:cc:dd:ee:11,dl_type=0x0000", 
          +                "Rule: table=0 cookie=0x9ddbfd9c4f252cdc priority=0", 
          +                "OpenFlow actions=NORMAL", 
          +                "forwarding to learned port", 
          +                "", 
          +                "Final flow: unchanged", 
          +                "Relevant fields: skb_priority=0,in_port=7,vlan_tci=0x0000/0x1fff,dl_src=aa:bb:cc:dd:ee:22,dl_dst=aa:bb:cc:dd:ee:11,dl_type=0x0000,nw_proto=0,nw_frag=no,tp_src=0", 
          +                "Datapath actions: 13", 
          +                ""
          +            ]
          +        }
          +    ], 
          +    "comment": "ovs br-int port 6 -->  7", 
          +    "debugs": [
          +        "AA:BB:CC:DD:EE:11 learnt on expected vlan 2 on port 6", 
          +        "Packet for learnt mac forwarded properly"
          +    ], 
          +    "errors": [
          +        "Packet forwarded to incorrect port 13, expected 6"
          +    ], 
          +    "pass": false
          +}
          +
          +      
        +
      • +
      +
    • +
    +
  • +
+OVERALL RESULT: +

Ping Test Results

+

Ping test between all pairs of VMs

+
    +
  • router1 (Namespace [qrouter-90111551-6cfc-4be0-b1c2-ce8bffb7edf6]) +
      +
    • vm1 → vm2 +
        +
      • 10.10.0.3 → 10.10.0.4 +
          +
          +{
          +    "command_list": [
          +        {
          +            "cmd": "ssh 10.10.0.3 with provided username and passwd", 
          +            "output": "Could not ssh to 10.10.0.3", 
          +            "pass": false
          +        }
          +    ], 
          +    "comment": "PING 10.10.0.3 to 10.10.0.4", 
          +    "errors": [], 
          +    "pass": false
          +}
          +
          +        
          +
        +
      • +
      +
    • +
    +
  • +
+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 0000000000000000000000000000000000000000..9a0d49bd3df88aff553b70b89e545e763ca5ab68 GIT binary patch literal 103116 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
3MQQYC?#9R^i^J76C5XD`iKM(HD@1*O_FqOjA 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..417eb2fc40d844946cb18f0966073d9549b2682d GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5X7J0fjhE&{YnRu4-h=M??`^DysoChD5DQRk`<~UBuW2qKc zziXdDcc-59hY!zd`yQXz^Ic!5%6zAqU-6V>n_mhrtlhfXuuURaI5PXG;W68mg4800 z-G-bOYuRTgKjzR_n)kX+IsWPo<0ApmnQQkS+m?HMM)ACd7Y{5Hj7qC3N#qpYl5P_0 zWxnG)w_L?GyGa5~OQuZTz%MUjB%e`n`M&%g4g-eh-O1-=TXk}Qu4V9a^>bP0l+XkK Dnpk0b literal 0 HcmV?d00001 diff --git a/static/don/button-open.png b/static/don/button-open.png new file mode 100644 index 0000000000000000000000000000000000000000..ac4a6ef32a50516609a06733d3365c78237e2143 GIT binary patch literal 240 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XCV9FzhE&{|GBJ>gS&_#j`GUT_;*XoNPsnBTUtm6Y&+UWI zzhh}p_Fm4rZ#T9-;BoF*cdbf!(e5A-HSIvY)bAY6=4_uLWcl2u=-kP@-`8&amZQa? zcbGwU|ALtpHI%;ip7~?-g8%3Cn;OS5n38#<|C#vBUf-h_aen=WPb~a4CH+tKE*EHG o{QhC0^XI-+?dA4A1QHnTT=Tf{X8(i9K-V#Ny85}Sb4q9e0Cgu}D*ylh literal 0 HcmV?d00001 diff --git a/static/don/button.png b/static/don/button.png new file mode 100644 index 0000000000000000000000000000000000000000..631d734d136a3c0e7698e249b09ac06462589ca8 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^+#t-s1|(OmDOUqhk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XIy_w*Ln>}_1wcZrxMOpdTT1SY*0BaajE)#RCaoBVu2L* zl`q`0TYJ;m{DmgIKFEJ*``x_5|FhF>RLskN_)_w^#h<=_&?6G!7R%4e%r2L^v-9u4 c=?u&a+cKO!dpZ{#2fB&D)78&qol`;+0OL4aGynhq literal 0 HcmV?d00001 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

+
    +
  • OVS bridge br-int +
      +
    • tag 2 +
        +
      • 6 → 7 +
          +
          +'sudo ovs-appctl fdb/flush br-int'
          +'sudo ovs-appctl ofproto/trace br-int in_port=6,dl_src=AA:BB:CC:DD:EE:11,dl_dst=AA:BB:CC:DD:EE:22 -generate'
          +'sudo ovs-appctl fdb/show br-int'
          +'sudo ovs-appctl ofproto/trace br-int in_port=7,dl_src=AA:BB:CC:DD:EE:22,dl_dst=AA:BB:CC:DD:EE:11 -generate'
          +{
          +    "command_list": [
          +        {
          +            "cmd": "sudo ovs-appctl fdb/flush br-int", 
          +            "output": [
          +                "table successfully flushed", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl ofproto/trace br-int in_port=6,dl_src=AA:BB:CC:DD:EE:11,dl_dst=AA:BB:CC:DD:EE:22 -generate", 
          +            "output": [
          +                "Flow: metadata=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "Rule: table=0 cookie=0xbafdefa428c693b6 priority=0", 
          +                "OpenFlow actions=NORMAL", 
          +                "no learned MAC for destination, flooding", 
          +                "", 
          +                "    Resubmitted flow: metadata=0,in_port=1,dl_vlan=2,dl_vlan_pcp=0,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "    Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "    Resubmitted  odp: push_vlan(vid=2,pcp=0),6,pop_vlan,8,push_vlan(vid=2,pcp=0),5,9", 
          +                "    Rule: table=0 cookie=0xbafdefa428c693b6 priority=1,in_port=1", 
          +                "    OpenFlow actions=resubmit(,2)", 
          +                "", 
          +                "        Resubmitted flow: unchanged", 
          +                "        Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "        Resubmitted  odp: push_vlan(vid=2,pcp=0),6,pop_vlan,8,push_vlan(vid=2,pcp=0),5,9", 
          +                "        Rule: table=2 cookie=0xbafdefa428c693b6 priority=0,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00", 
          +                "        OpenFlow actions=resubmit(,20)", 
          +                "", 
          +                "            Resubmitted flow: unchanged", 
          +                "            Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "            Resubmitted  odp: push_vlan(vid=2,pcp=0),6,pop_vlan,8,push_vlan(vid=2,pcp=0),5,9", 
          +                "            Rule: table=20 cookie=0xbafdefa428c693b6 priority=0", 
          +                "            OpenFlow actions=resubmit(,22)", 
          +                "", 
          +                "                Resubmitted flow: unchanged", 
          +                "                Resubmitted regs: reg0=0x0 reg1=0x0 reg2=0x0 reg3=0x0 reg4=0x0 reg5=0x0 reg6=0x0 reg7=0x0", 
          +                "                Resubmitted  odp: push_vlan(vid=2,pcp=0),6,pop_vlan,8,push_vlan(vid=2,pcp=0),5,9", 
          +                "                Rule: table=22 cookie=0xbafdefa428c693b6 priority=0", 
          +                "                OpenFlow actions=drop", 
          +                "", 
          +                "Final flow: metadata=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000", 
          +                "Relevant fields: skb_priority=0,in_port=6,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:11,dl_dst=aa:bb:cc:dd:ee:22,dl_type=0x0000,nw_proto=0,nw_frag=no,tp_src=0", 
          +                "Datapath actions: push_vlan(vid=2,pcp=0),6,pop_vlan,8,push_vlan(vid=2,pcp=0),5,9,pop_vlan,13,14", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl fdb/show br-int", 
          +            "output": [
          +                " port  VLAN  MAC                Age", 
          +                "    6     2  aa:bb:cc:dd:ee:11    0", 
          +                ""
          +            ]
          +        }, 
          +        {
          +            "cmd": "sudo ovs-appctl ofproto/trace br-int in_port=7,dl_src=AA:BB:CC:DD:EE:22,dl_dst=AA:BB:CC:DD:EE:11 -generate", 
          +            "output": [
          +                "Flow: metadata=0,in_port=7,vlan_tci=0x0000,dl_src=aa:bb:cc:dd:ee:22,dl_dst=aa:bb:cc:dd:ee:11,dl_type=0x0000", 
          +                "Rule: table=0 cookie=0xbafdefa428c693b6 priority=0", 
          +                "OpenFlow actions=NORMAL", 
          +                "forwarding to learned port", 
          +                "", 
          +                "Final flow: unchanged", 
          +                "Relevant fields: skb_priority=0,in_port=7,vlan_tci=0x0000/0x1fff,dl_src=aa:bb:cc:dd:ee:22,dl_dst=aa:bb:cc:dd:ee:11,dl_type=0x0000,nw_proto=0,nw_frag=no,tp_src=0", 
          +                "Datapath actions: 12", 
          +                ""
          +            ]
          +        }
          +    ], 
          +    "comment": "ovs br-int port 6 -->  7", 
          +    "debugs": [
          +        "AA:BB:CC:DD:EE:11 learnt on expected vlan 2 on port 6", 
          +        "Packet for learnt mac forwarded properly"
          +    ], 
          +    "errors": [
          +        "Packet forwarded to incorrect port 12, expected 6"
          +    ], 
          +    "pass": false
          +}
          +
          +      
        +
      • +
      +
    • +
    +
  • +
+OVERALL RESULT: +

Ping Test Results

+

Ping test between all pairs of VMs

+
    +
  • router1 (Namespace [qrouter-90111551-6cfc-4be0-b1c2-ce8bffb7edf6]) +
      +
    • vm1 → vm2 +
        +
      • 10.10.0.3 → 10.10.0.4 +
          +
          +{
          +    "command_list": [
          +        {
          +            "cmd": "ssh 10.10.0.3 with provided username and passwd", 
          +            "output": "Could not ssh to 10.10.0.3", 
          +            "pass": false
          +        }
          +    ], 
          +    "comment": "PING 10.10.0.3 to 10.10.0.4", 
          +    "errors": [], 
          +    "pass": false
          +}
          +
          +        
          +
        +
      • +
      +
    • +
    +
  • +
+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

K+1i2OuyTGcw5QIDN?+g5fZ3kK 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