mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
299 lines
12 KiB
Python
Executable File
299 lines
12 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/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
|
|
# 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 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
|
|
|
|
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
|
|
|
|
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})
|