Salvatore Orlando 3eef46f8a9 Open vSwitch tunnel manager
Applying patch with new ovs-tunnel-manager on top of cloudstack oss-master
2012-04-23 22:32:52 +01:00

301 lines
9.9 KiB
Python
Executable File

#!/usr/bin/python
# Version @VERSION@
#
# A plugin for executing script needed by vmops cloud
import cloudstack_pluginlib as lib
import logging
import os, sys, time
import subprocess
import XenAPIPlugin
sys.path.append("/opt/xensource/sm/")
import util
from time import localtime as _localtime, asctime as _asctime
xePath= "/opt/xensource/bin/xe"
lib.setup_logging("/var/log/ovstunnel.log")
def find_vifs_v5(xs_nw_uuid):
logging.debug("Fetching vifs on networks - for XS version 5.x")
vif_uuids_cmd = [xePath, 'network-list', 'uuid=%s' %xs_nw_uuid,
'params=VIF-uuids', '--minimal']
vif_uuids_str = lib.do_cmd(vif_uuids_cmd)
vif_uuids = vif_uuids_str.split(';')
vifs = []
for vif_uuid in vif_uuids:
vif_uuid = vif_uuid.strip()
is_attached_cmd = [xePath, 'vif-list', 'uuid=%s' %vif_uuid,
'params=currently-attached', '--minimal']
is_attached = lib.do_cmd(is_attached_cmd)
# Consider only attached VIFs
if is_attached == 'false':
continue
vm_uuid_cmd = [xePath, 'vif-list', 'uuid=%s' %vif_uuid,
'params=vm-uuid', '--minimal']
vm_uuid = lib.do_cmd(vm_uuid_cmd)
dom_id_cmd = [xePath, 'vm-list', 'uuid=%s' %vm_uuid,
'params=dom-id', '--minimal']
dom_id = lib.do_cmd(dom_id_cmd)
device_cmd = [xePath, 'vif-list', 'uuid=%s' %vif_uuid,
'params=device', '--minimal']
device = lib.do_cmd(device_cmd)
vifs.append("vif%s.%s" % (dom_id, device))
logging.debug("Vifs on network:%s" %vifs)
vif_ofports = []
for vif in vifs:
vif_ofport_cmd=[lib.VSCTL_PATH, 'get', 'interface', vif, 'ofport']
vif_ofport = lib.do_cmd(vif_ofport_cmd).strip()
if vif_ofport.endswith('\n'):
vif_ofport = vif_ofport[:-1]
vif_ofports.append(vif_ofport.strip())
return vif_ofports
def find_vifs_v6(xs_nw_uuid):
logging.debug("Fetching vifs on networks - for XS version 6.x")
cmd_vif_ofports = [lib.VSCTL_PATH, "--", "--columns=ofport",
"find", "interface",
"external_ids:xs-network-uuid=%s" % xs_nw_uuid,
"type!=gre"]
vif_ofports_str = lib.do_cmd(cmd_vif_ofports)
vif_ofports = []
for line in vif_ofports_str.split('\n'):
elements = line.split(':')
if len(elements)==2:
# ensure no trailing \n is returned
if elements[1].endswith('\n'):
elements[1] = elements[1][:-1]
vif_ofports.append(elements[1].strip())
return vif_ofports
vif_ofport_list_handlers = {
'5': find_vifs_v5,
'6': find_vifs_v6}
def block_ipv6_v5(bridge):
lib.add_flow(bridge, priority=65000, dl_type='0x86dd', actions='drop')
def block_ipv6_v6(bridge):
lib.add_flow(bridge, priority=65000, proto='ipv6', actions='drop')
block_ipv6_handlers = {
'5': block_ipv6_v5,
'6': block_ipv6_v6}
class PluginError(Exception):
"""Base Exception class for all plugin errors."""
def __init__(self, *args):
Exception.__init__(self, *args)
def echo(fn):
def wrapped(*v, **k):
name = fn.__name__
util.SMlog("#### VMOPS enter %s ####" % name )
res = fn(*v, **k)
util.SMlog("#### VMOPS exit %s ####" % name )
return res
return wrapped
@echo
def setup_ovs_bridge(session, args):
bridge = args.pop("bridge")
key = args.pop("key")
xs_nw_uuid = args.pop("xs_nw_uuid")
cs_host_id = args.pop("cs_host_id")
res = lib.check_switch()
if res != "SUCCESS":
return "FAILURE:%s" %res
logging.debug("About to manually create the bridge:%s" %bridge)
# create a bridge with the same name as the xapi network
# also associate gre key in other config attribute
res = lib.do_cmd([lib.VSCTL_PATH, "--", "--may-exist", "add-br", bridge,
"--", "set", "bridge", bridge,
"other_config:gre_key=%s" % key])
logging.debug("Bridge has been manually created:%s" %res)
# TODO: Make sure xs-network-uuid is set into external_ids
lib.do_cmd([lib.VSCTL_PATH, "set", "Bridge", bridge,
"external_ids:xs-network-uuid=%s" % xs_nw_uuid])
# Non empty result means something went wrong
if res:
result = "FAILURE:%s" %res
else:
# Verify the bridge actually exists, with the gre_key properly set
res = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge",
bridge, "other_config:gre_key"])
if key in res:
result = "SUCCESS:%s" %bridge
else:
result = "FAILURE:%s" %res
# Finally note in the xenapi network object that the network has
# been configured
xs_nw_uuid = lib.do_cmd([lib.XE_PATH, "network-list",
"bridge=%s" % bridge, "--minimal"])
lib.do_cmd([lib.XE_PATH,"network-param-set", "uuid=%s" % xs_nw_uuid,
"other-config:is-ovs-tun-network=True"])
conf_hosts = lib.do_cmd([lib.XE_PATH,"network-param-get",
"uuid=%s" % xs_nw_uuid,
"param-name=other-config",
"param-key=ovs-host-setup", "--minimal"])
conf_hosts = cs_host_id + (conf_hosts and ',%s' % conf_hosts or '')
lib.do_cmd([lib.XE_PATH,"network-param-set", "uuid=%s" % xs_nw_uuid,
"other-config:ovs-host-setup=%s" %conf_hosts])
# BLOCK IPv6 - Flow spec changes with ovs version
host_list_cmd = [lib.XE_PATH, 'host-list', '--minimal']
host_list_str = lib.do_cmd(host_list_cmd)
host_uuid = host_list_str.split(',')[0].strip()
version_cmd = [lib.XE_PATH, 'host-param-get', 'uuid=%s' % host_uuid,
'param-name=software-version',
'param-key=product_version']
version = lib.do_cmd(version_cmd).split('.')[0]
block_ipv6_handlers[version](bridge)
logging.debug("Setup_ovs_bridge completed with result:%s" %result)
return result
@echo
def destroy_ovs_bridge(session, args):
bridge = args.pop("bridge")
res = lib.check_switch()
# TODO: Must complete this routine
if res != "SUCCESS":
return res
res = lib.do_cmd([lib.VSCTL_PATH, "del-br", bridge])
logging.debug("Bridge has been manually removed:%s" %res)
if res:
result = "FAILURE:%s" %res
else:
# Note that the bridge has been removed on xapi network object
xs_nw_uuid = lib.do_cmd([xePath, "network-list",
"bridge=%s" % bridge, "--minimal"])
#lib.do_cmd([xePath,"network-param-set", "uuid=%s" % xs_nw_uuid,
# "other-config:ovs-setup=False"])
result = "SUCCESS:%s" %bridge
logging.debug("Destroy_ovs_bridge completed with result:%s" %result)
return result
@echo
def create_tunnel(session, args):
bridge = args.pop("bridge")
remote_ip = args.pop("remote_ip")
gre_key = args.pop("key")
src_host = args.pop("from")
dst_host = args.pop("to")
logging.debug("Entering create_tunnel")
res = lib.check_switch()
if res != "SUCCESS":
logging.debug("Openvswitch running: NO")
return "FAILURE:%s" %res
# We need to keep the name below 14 characters
# src and target are enough - consider a fixed length hash
name = "t%s-%s-%s" % (gre_key, src_host, dst_host)
# Verify the xapi bridge to be created
# NOTE: Timeout should not be necessary anymore
wait = [lib.VSCTL_PATH, "--timeout=30", "wait-until", "bridge", bridge, "--",
"get", "bridge", bridge, "name"]
res = lib.do_cmd(wait)
if bridge not in res:
logging.debug("WARNING:Can't find bridge %s for creating " +
"tunnel!" % bridge)
return "FAILURE:NO_BRIDGE"
logging.debug("bridge %s for creating tunnel - VERIFIED" % bridge)
tunnel_setup = False
drop_flow_setup = False
try:
# Create a port and configure the tunnel interface for it
add_tunnel = [lib.VSCTL_PATH, "add-port", bridge, name, "--", "set", "interface",
name, "type=gre", "options:key=%s" % gre_key,
"options:remote_ip=%s" % remote_ip]
lib.do_cmd(add_tunnel)
tunnel_setup = True
# verify port
verify_port = [lib.VSCTL_PATH, "get", "port", name, "interfaces"]
res = lib.do_cmd(verify_port)
# Expecting python-style list as output
iface_list = []
if len(res) > 2:
iface_list = res.strip()[1:-1].split(',')
if len(iface_list) != 1:
logging.debug("WARNING: Unexpected output while verifying " +
"port %s on bridge %s" %(name, bridge))
return "FAILURE:VERIFY_PORT_FAILED"
# verify interface
iface_uuid = iface_list[0]
verify_interface_key = [lib.VSCTL_PATH, "get", "interface",
iface_uuid, "options:key"]
verify_interface_ip = [lib.VSCTL_PATH, "get", "interface",
iface_uuid, "options:remote_ip"]
key_validation = lib.do_cmd(verify_interface_key)
ip_validation = lib.do_cmd(verify_interface_ip)
if not gre_key in key_validation or not remote_ip in ip_validation:
logging.debug("WARNING: Unexpected output while verifying " +
"interface %s on bridge %s" %(name, bridge))
return "FAILURE:VERIFY_INTERFACE_FAILED"
logging.debug("Tunnel interface validated:%s" %verify_interface_ip)
cmd_tun_ofport = [lib.VSCTL_PATH, "get", "interface",
iface_uuid, "ofport"]
tun_ofport = lib.do_cmd(cmd_tun_ofport)
# Ensure no trailing LF
if tun_ofport.endswith('\n'):
tun_ofport = tun_ofport[:-1]
# add flow entryies for dropping broadcast coming in from gre tunnel
lib.add_flow(bridge, priority=1000, in_port=tun_ofport,
dl_dst='ff:ff:ff:ff:ff:ff', actions='drop')
lib.add_flow(bridge, priority=1000, in_port=tun_ofport,
nw_dst='224.0.0.0/24', actions='drop')
drop_flow_setup = True
logging.debug("Broadcast drop rules added")
return "SUCCESS:%s" % name
except:
logging.debug("An unexpected error occured. Rolling back")
if tunnel_setup:
logging.debug("Deleting GRE interface")
# Destroy GRE port and interface
lib.del_port(bridge, name)
if drop_flow_setup:
# Delete flows
logging.debug("Deleting flow entries from GRE interface")
lib.del_flows(bridge, in_port=tun_ofport)
raise
@echo
def destroy_tunnel(session, args):
bridge = args.pop("bridge")
iface_name = args.pop("in_port")
logging.debug("Destroying tunnel at port %s for bridge %s"
% (iface_name, bridge))
ofport = get_field_of_interface(iface_name, "ofport")
lib.del_flows(bridge, in_port=ofport)
lib.del_port(bridge, iface_name)
return "SUCCESS"
def get_field_of_interface(iface_name, field):
get_iface_cmd = [lib.VSCTL_PATH, "get","interface", iface_name, field]
res = lib.do_cmd(get_iface_cmd)
return res
if __name__ == "__main__":
XenAPIPlugin.dispatch({"create_tunnel":create_tunnel,
"destroy_tunnel":destroy_tunnel,
"setup_ovs_bridge": setup_ovs_bridge,
"destroy_ovs_bridge": destroy_ovs_bridge})