Last active
December 28, 2015 02:59
-
-
Save jcpowermac/7431484 to your computer and use it in GitHub Desktop.
OpenStack: Interfaces for instance connectivity from tap to ovsbridge.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Sources: | |
# http://effbot.org/zone/element-xpath.htm | |
# http://eli.thegreenplace.net/2012/03/15/processing-xml-in-python-with-elementtree/ | |
# http://wiki.libvirt.org/page/SSHSetup | |
# http://docs.python.org/2/tutorial/datastructures.html#dictionaries | |
# http://libvirt.org/git/?p=libvirt.git;a=blob;f=tools/virsh-domain-monitor.c | |
# http://www.linuxproblem.org/art_9.html | |
# http://j2labs.tumblr.com/post/4477180133/ssh-with-pythons-paramiko | |
__author__ = 'jcallen' | |
import os | |
import libvirt | |
import sys | |
import prettytable | |
from novaclient.v1_1 import client | |
import xml.etree.ElementTree as ET | |
import paramiko | |
import re | |
import argparse | |
def ssh_connect(host, username, private_key, port=22): | |
"""Helper function to initiate an ssh connection to a host.""" | |
transport = paramiko.Transport((host, port)) | |
if os.path.exists(private_key): | |
rsa_key = paramiko.RSAKey.from_private_key_file(private_key) | |
transport.connect(username=username, pkey=rsa_key) | |
else: | |
raise TypeError("Incorrect private key path") | |
return transport | |
def exec_cmd(transport, command): | |
"""Executes a command on the same server as the provided | |
transport | |
""" | |
try: | |
channel = transport.open_session() | |
channel.exec_command(command) | |
if channel.recv_exit_status() == 0: | |
output = channel.makefile('rb', -1).readlines() | |
return output | |
else: | |
stderr_output = channel.makefile_stderr('rb', -1).readlines() | |
print stderr_output | |
print "error: " + channel.recv_exit_status() | |
return "" | |
except: | |
print sys.exc_info() | |
def show_brctl_veth(device, transport): | |
BRCTL = "/usr/sbin/brctl" | |
command = BRCTL + " show " + device | |
output = exec_cmd(transport, command) | |
if output != "": | |
ifline = output[1] | |
match = re.findall("(qvb[\w-]+$)", ifline) | |
veth = match[0] | |
return veth | |
def ethtool_adapter_stats(device, transport): | |
ETHTOOL = "/usr/sbin/ethtool" | |
command = ETHTOOL + " -S " + device | |
output = exec_cmd(transport, command) | |
if output != "": | |
ifline = output[1] | |
match = re.findall("peer_ifindex: ([\d]+)$", ifline) | |
peer_ifindex = match[0] | |
return peer_ifindex | |
def ip_link(peer_ifindex, transport): | |
ETHTOOL = "/usr/sbin/ip" | |
command = ETHTOOL + " link " | |
output = exec_cmd(transport, command) | |
if output != "": | |
for interfaces in output: | |
ifline = interfaces | |
matchstring = "^%s:\s([\w-]+)" % peer_ifindex | |
match = re.findall(matchstring, ifline) | |
if len(match) != 0: | |
veth_pair2 = match[0] | |
return veth_pair2 | |
def ovs_ofctl(action, device, transport): | |
""" | |
Executes ovs-ofctl via paramiko ssh client | |
Requires a sudoers entry: | |
Defaults !requiretty | |
user ALL = NOPASSWD: /usr/bin/ovs-ofctl | |
""" | |
OFCTL="sudo /usr/bin/ovs-ofctl" | |
command = OFCTL + " " + action + " " + device | |
output = exec_cmd(transport, command) | |
return output | |
def ovs_ofctl_dump_flows(device, tag, transport): | |
output = ovs_ofctl("dump-flows", device, transport) | |
if output != "": | |
for line in output: | |
matchstring = "mod_vlan_vid:%s" % tag | |
if re.search(matchstring, line) is not None: | |
matchstring = "dl_vlan=(\d+)" | |
match = re.findall(matchstring, line) | |
if len(match) != 0: | |
#print match | |
vlan = match[0] | |
return vlan | |
def ovs_vsctl(action, device, transport): | |
""" | |
Executes ovs-vsctl via paramiko ssh client | |
Requires a sudoers entry: | |
Defaults !requiretty | |
user ALL = NOPASSWD: /usr/bin/ovs-vsctl | |
""" | |
VSCTL="sudo /usr/bin/ovs-vsctl" | |
command = VSCTL + " " + action + " " + device | |
output = exec_cmd(transport, command) | |
return output | |
def ovs_vsctl_list_port(device, transport): | |
output = ovs_vsctl("list port", device, transport) | |
if output != "": | |
for line in output: | |
matchstring = "^tag[\s:]*(\d+)" | |
match = re.findall(matchstring, line) | |
if len(match) != 0: | |
tag = match[0] | |
return tag | |
def ovs_vsctl_port_to_br(device, transport): | |
output = ovs_vsctl("port-to-br", device, transport) | |
bridge = output[0].split("\n")[0] | |
return bridge | |
def domiflist(domxml): | |
tree = ET.ElementTree(ET.fromstring(domxml)) | |
elements = tree.iterfind("./devices/interface") | |
for ele in elements: | |
interface_type = ele.attrib['type'] | |
if interface_type == "bridge": | |
bridge = ele.find("source[@bridge]").attrib["bridge"] | |
tap = ele.find("target[@dev]").attrib["dev"] | |
mac = ele.find("mac[@address]").attrib["address"] | |
return bridge, tap, mac | |
else: | |
print "Error: Currently will only work with bridged interfaces." | |
def args(): | |
parser = argparse.ArgumentParser() | |
parser.add_argument('--hostname', help='Hostname of OpenStack instance', required=True) | |
parser.add_argument('--os-username', help='OpenStack Username', required=True) | |
parser.add_argument('--os-password', help='OpenStack Password', required=True) | |
parser.add_argument('--os-tenant-name', help='OpenStack Tenant', required=True) | |
parser.add_argument('--username', help='Username of OpenStack host', required=True) | |
parser.add_argument('--priv-key', help='Location of private SSH Key', required=True) | |
arguments = parser.parse_args() | |
return arguments | |
def main(): | |
args_namespace = args() | |
username = args_namespace.os_username | |
password = args_namespace.os_password | |
tenant = args_namespace.os_tenant_name | |
authurl = "http://%s:5000/v2.0" % args_namespace.hostname | |
osconn = client.Client(username, password, tenant, authurl, service_type="compute") | |
qemu = "qemu+ssh://%s@%s/system" % (args_namespace.username, args_namespace.hostname) | |
conn = libvirt.openReadOnly(qemu) | |
if osconn is None: | |
print 'Failed to open connection to OpenStack instance' | |
sys.exit(1) | |
if conn is None: | |
print 'Failed to open connection libvirtd' | |
sys.exit(1) | |
try: | |
pt = prettytable.PrettyTable(["OpenStack Name", "QEMU Name", "UUID", "linuxbridge", "TAP", "MAC", "veth_pair1", "veth_pair2", "ovsbridge", "tag", "vlan"]) | |
servers = osconn.servers.list(detailed=True) | |
for srv in servers: | |
dom = conn.lookupByUUIDString(srv.id) # get domain from UUID | |
xml = dom.XMLDesc() | |
bridge, tap, mac = domiflist(xml) | |
transport = ssh_connect(args_namespace.hostname, args_namespace.username, args_namespace.priv_key) | |
veth_pair1 = show_brctl_veth(bridge, transport) | |
peer_ifindex = ethtool_adapter_stats(veth_pair1, transport) | |
veth_pair2 = ip_link(peer_ifindex, transport) | |
ovsbridge = ovs_vsctl_port_to_br(veth_pair2, transport) | |
tag = ovs_vsctl_list_port(veth_pair2, transport) | |
vlan = ovs_ofctl_dump_flows(ovsbridge, tag, transport) | |
pt.add_row([srv.human_id, dom.name(), dom.UUIDString(), bridge, tap, mac, veth_pair1, veth_pair2, ovsbridge, tag, vlan]) | |
print(pt) | |
except: | |
print sys.exc_info() | |
sys.exit(1) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment