mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			244 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			244 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/python
 | |
| # Creates a tunnel mesh across xenserver hosts
 | |
| # Enforces broadcast drop rules on ingress GRE tunnels
 | |
| 
 | |
| import cloudstack_pluginlib as lib
 | |
| import logging
 | |
| import os
 | |
| import sys
 | |
| import subprocess
 | |
| import time
 | |
| 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 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}
 | |
| 
 | |
| 
 | |
| 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()
 | |
|     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"])
 | |
|         #FIXME: WOW, this an error
 | |
|         #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)
 | |
|         # This will not cancel the original exception
 | |
|         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})
 |