mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	last VM from the VPC is deleted on a host OVS distributed routing: ensure bridge is deleted when last VM from the VPC is deleted on a host. This fix ensures that bridge is destroyed.
		
			
				
	
	
		
			345 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			345 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/python
 | |
| # Licensed to the Apache Software Foundation (ASF) under one
 | |
| # or more contributor license agreements.  See the NOTICE file
 | |
| # distributed with this work for additional information
 | |
| # regarding copyright ownership.  The ASF licenses this file
 | |
| # to you 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.
 | |
| 
 | |
| 
 | |
| # Creates a tunnel mesh across xenserver hosts
 | |
| # Enforces broadcast drop rules on ingress GRE tunnels
 | |
| 
 | |
| import cloudstack_pluginlib as lib
 | |
| import logging
 | |
| import commands
 | |
| 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/cloud/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__
 | |
|         logging.debug("#### VMOPS enter  %s ####" % name)
 | |
|         res = fn(*v, **k)
 | |
|         logging.debug("#### 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])
 | |
| 
 | |
|     # enable stp
 | |
|     lib.do_cmd([lib.VSCTL_PATH, "set", "Bridge", bridge, "stp_enable=true"])
 | |
| 
 | |
|     # 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
 | |
|         lib.do_cmd([lib.XE_PATH, "network-param-set", "uuid=%s" % xs_nw_uuid,
 | |
|                    "other-config:is-ovs-tun-network=True"])
 | |
|         # 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"])
 | |
|         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"])
 | |
|         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
 | |
|         # Temporarily no need BLOCK IPv6
 | |
| #        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 setup_ovs_bridge_for_distributed_routing(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
 | |
|     res = lib.do_cmd([lib.VSCTL_PATH, "--", "--may-exist", "add-br", bridge])
 | |
| 
 | |
|     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, "list", "bridge", bridge])
 | |
| 
 | |
|         # 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-vpc-distributed-vr-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"])
 | |
|         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])
 | |
| 
 | |
|         # first clear the default rule (rule for 'NORMAL' processing which makes a bridge simple L2 learn & flood switch)
 | |
|         lib.del_flows(bridge, table=0)
 | |
| 
 | |
|         # add a default flow rule to send broadcast and multi-cast packets to L2 flooding table
 | |
|         lib.add_flow(bridge, priority=1200, dl_dst='ff:ff:ff:ff:ff:ff', table=lib.CLASSIFIER_TABLE,
 | |
|                      actions='resubmit(,%s)'%lib.L2_FLOOD_TABLE)
 | |
|         lib.add_flow(bridge, priority=1200, nw_dst='224.0.0.0/24', table=lib.CLASSIFIER_TABLE,
 | |
|                      actions='resubmit(,%s)'%lib.L2_FLOOD_TABLE)
 | |
| 
 | |
|         # add a default flow rule to send uni-cast traffic to L2 lookup table
 | |
|         lib.add_flow(bridge, priority=0, table=lib.CLASSIFIER_TABLE, actions='resubmit(,%s)'%lib.L2_LOOKUP_TABLE)
 | |
| 
 | |
|         # add a default rule to send unknown mac address to L2 flooding table
 | |
|         lib.add_flow(bridge, priority=0, table=lib.L2_LOOKUP_TABLE, actions='resubmit(,%s)'%lib.L2_FLOOD_TABLE)
 | |
| 
 | |
|         # add a default rule in L2 flood table to drop packet
 | |
|         lib.add_flow(bridge, priority=0, table=lib.L2_FLOOD_TABLE, actions='drop')
 | |
| 
 | |
|         # add a default rule in egress ACL table to forward packet to L3 lookup table
 | |
|         lib.add_flow(bridge, priority=0, table=lib.EGRESS_ACL_TABLE, actions='resubmit(,%s)'%lib.L3_LOOKUP_TABLE)
 | |
| 
 | |
|         # add a default rule in L3 lookup table to forward packet to L2 lookup table
 | |
|         lib.add_flow(bridge, priority=0, table=lib.L3_LOOKUP_TABLE, actions='resubmit(,%s)'%lib.L2_LOOKUP_TABLE)
 | |
| 
 | |
|         # add a default rule in ingress table to drop in bound packets
 | |
|         lib.add_flow(bridge, priority=0, table=lib.INGRESS_ACL_TABLE, actions='drop')
 | |
| 
 | |
|         # initialize the sequence number for the bridge
 | |
|         lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, "other-config:topology-update-sequence-number=0"])
 | |
|         lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge, "other-config:route-policy-update-sequence-number=0"])
 | |
| 
 | |
|         result = "SUCCESS: successfully setup bridge with flow rules"
 | |
| 
 | |
|     logging.debug("Setup_ovs_bridge completed with result:%s" % result)
 | |
|     return result
 | |
| 
 | |
| @echo
 | |
| def destroy_ovs_bridge(session, args):
 | |
|     bridge = args.pop("bridge")
 | |
|     this_host_id = args.pop("cs_host_id")
 | |
|     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([lib.XE_PATH, "network-list",
 | |
|                                 "bridge=%s" % bridge, "--minimal"])
 | |
|         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"])
 | |
|         new_conf_hosts = ""
 | |
|         hosts = conf_hosts.split(',')
 | |
|         for host in hosts:
 | |
|             if str(host) == str(this_host_id):
 | |
|                 continue
 | |
|             new_conf_hosts = host + "," + new_conf_hosts
 | |
|         new_conf_hosts = new_conf_hosts[:-1]
 | |
|         lib.do_cmd([lib.XE_PATH, "network-param-set", "uuid=%s" % xs_nw_uuid,
 | |
|                    "other-config:ovs-host-setup=%s" % new_conf_hosts])
 | |
|         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")
 | |
|     network_uuid = args.pop("cloudstack-network-id")
 | |
| 
 | |
|     return lib.create_tunnel(bridge, remote_ip, gre_key, src_host, dst_host, network_uuid)
 | |
| 
 | |
| @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
 | |
| 
 | |
| def is_xcp(session, args):
 | |
|     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()
 | |
| 
 | |
|     status, output = commands.getstatusoutput("xe host-param-list uuid="+host_uuid+" | grep platform_name")
 | |
|     if (status != 0):
 | |
|        return "FALSE"
 | |
| 
 | |
|     platform_cmd = [lib.XE_PATH, 'host-param-get', 'uuid=%s' % host_uuid,
 | |
|                                'param-name=software-version',
 | |
|                                'param-key=platform_name']
 | |
|     platform = lib.do_cmd(platform_cmd).split('.')[0]
 | |
|     return platform
 | |
| 
 | |
| def getLabel(session, args):
 | |
|     i = 0
 | |
|     pif_list_cmd = [lib.XE_PATH, 'pif-list', '--minimal']
 | |
|     pif_list_str = lib.do_cmd(pif_list_cmd)
 | |
|     while True:
 | |
| 	pif_uuid = pif_list_str.split(',')[i].strip()
 | |
| 	network_cmd = [lib.XE_PATH, 'pif-param-get', 'uuid=%s' % pif_uuid, 'param-name=network-uuid']
 | |
| 	network_uuid = lib.do_cmd(network_cmd).split('.')[0]
 | |
| 	iface_cmd = [lib.XE_PATH, 'network-param-get', 'uuid=%s' % network_uuid, 'param-name=bridge']
 | |
| 	iface = lib.do_cmd(iface_cmd)
 | |
| 	status,output = commands.getstatusoutput("ifconfig "+iface+" | grep inet")
 | |
| 	if (status != 0):
 | |
| 		i += 1
 | |
| 		continue
 | |
|     	label_cmd = [lib.XE_PATH, 'network-param-get', 'uuid=%s' % network_uuid, 'param-name=name-label']
 | |
|     	label = lib.do_cmd(label_cmd).split('.')[0]
 | |
|     	return label
 | |
|     return False
 | |
| 
 | |
| @echo
 | |
| def configure_ovs_bridge_for_network_topology(session, args):
 | |
|     bridge = args.pop("bridge")
 | |
|     json_config = args.pop("config")
 | |
|     this_host_id = args.pop("host-id")
 | |
|     sequence_no = args.pop("seq-no")
 | |
| 
 | |
|     # get the last update sequence number
 | |
|     last_seq_no = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", bridge, "other-config:topology-update-sequence-number"])
 | |
|     last_seq_no = last_seq_no[1:-1]
 | |
|     if long(sequence_no) > long(last_seq_no):
 | |
|         lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge,
 | |
|                     "other-config:topology-update-sequence-number=%s"%sequence_no])
 | |
|         return lib.configure_vpc_bridge_for_network_topology(bridge, this_host_id, json_config, sequence_no)
 | |
|     else:
 | |
|         return "SUCCESS: Ignoring the update with the sequence number %s" %sequence_no + " as there is already recent" \
 | |
|                 " update received and applied with sequence number %s" %last_seq_no
 | |
| 
 | |
| @echo
 | |
| def configure_ovs_bridge_for_routing_policies(session, args):
 | |
|     bridge = args.pop("bridge")
 | |
|     json_config = args.pop("config")
 | |
|     sequence_no = args.pop("seq-no")
 | |
| 
 | |
|     # get the last update sequence number
 | |
|     last_seq_no = lib.do_cmd([lib.VSCTL_PATH, "get", "bridge", bridge,
 | |
|                               "other-config:route-policy-update-sequence-number"])
 | |
|     last_seq_no = last_seq_no[1:-1]
 | |
|     if long(sequence_no) > long(last_seq_no):
 | |
|         lib.do_cmd([lib.VSCTL_PATH, "set", "bridge", bridge,
 | |
|                     "other-config:route-policy-update-sequence-number=%s"%sequence_no])
 | |
|         return lib.configure_vpc_bridge_for_routing_policies(bridge, json_config, sequence_no)
 | |
|     else:
 | |
|         return "SUCCESS: Ignoring the update with the sequence number %s" %sequence_no + " as there is already recent" \
 | |
|                 " update received and applied with sequence number %s" %last_seq_no
 | |
| 
 | |
| 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,
 | |
|                            "is_xcp": is_xcp,
 | |
|                            "getLabel": getLabel,
 | |
|                            "setup_ovs_bridge_for_distributed_routing": setup_ovs_bridge_for_distributed_routing,
 | |
|                            "configure_ovs_bridge_for_network_topology": configure_ovs_bridge_for_network_topology,
 | |
|                            "configure_ovs_bridge_for_routing_policies": configure_ovs_bridge_for_routing_policies}) |