#!/usr/bin/env python # # xapi plugin script to update the cache of configuration items in the # ovs-vswitchd configuration file that are managed in the xapi database # when integrated with Citrix management tools. # Copyright (C) 2009 Nicira Networks, Inc. # # Licensed 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. # TBD: - error handling needs to be improved. Currently this can leave # TBD: the system in a bad state if anything goes wrong. import XenAPIPlugin import XenAPI import os import subprocess vsctl="/usr/bin/ovs-vsctl" cacert_filename="/etc/ovs-vswitchd.cacert" # Delete the CA certificate, so that we go back to boot-strapping mode def delete_cacert(): try: os.remove(cacert_filename) except OSError: # Ignore error if file doesn't exist pass def update(session, args): pools = session.xenapi.pool.get_all() # We assume there is only ever one pool... if len(pools) == 0: raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", []) if len(pools) > 1: raise XenAPIPlugin.Failure("MORE_THAN_ONE_POOL_FOR_HOST", []) pool = session.xenapi.pool.get_record(pools[0]) try: controller = pool["other_config"]["vSwitchController"] except KeyError, e: controller = "" if controller == "do-not-update": return "XAPI key set to do-not-update" if controller.startswith("for-bridges|"): l = controller.split("|")[1:] for netctrl in l: xapiNet = session.xenapi.network n, t = netctrl.split("=") blist = xapiNet.get_by_name_label(n) if len(blist) == 0: # If there is no bridge for the network, just keep # going so we bring up as much as possible. continue elif len(blist) > 1: raise XenAPIPlugin.Failure("TOO_MANY_MATCHING_NETWORKS", [n,blist]) b = xapiNet.get_bridge(blist[0]) setBrControllerCfg(b, t) return "Completed setting controllers on specific bridges" currentController = vswitchCurrentController() if controller == "" and currentController != "": delete_cacert() removeControllerCfg() return "Successfully removed controller config" elif controller != currentController: delete_cacert() setControllerCfg(controller) return "Successfully set controller to " + controller else: return "No change to configuration" def vswitchCurrentController(): controller = vswitchCfgQuery("get-controller") if controller == "": return controller if len(controller) < 4 or controller[0:4] != "ssl:": return controller else: return controller[4:] def removeControllerCfg(): vswitchCfgMod(["--", "del-controller", "--", "del-ssl"]) def setBrControllerCfg(br, target): # Terrible hack... When this is run at boot the required bridges # may not be present. So, we fork a process for each bridge that # needs to be set which sits around in the background and updates # it when it becomes available, finally timing out after a long # interval if it never becomes available. # # The right way to do this is to hook the bridge creation somehow # but I don't believe this is possible in XenServer 5.5 without # either listening to XAPI events or writing it in C code in # brcompatd. import time import syslog import resource p = os.fork() if p != 0: return os.setsid() p = os.fork() if p != 0: sys.exit(0) os.chdir("/") os.umask(0) maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1] if maxfd == resource.RLIM_INFINITY: maxfd = 1024 for fd in range(0, maxfd): try: os.close(fd) except OSError: pass os.open("/dev/null", os.O_RDWR) os.dup2(0, 1) os.dup2(0, 2) syslog.openlog("vswitch-cfg-update", syslog.LOG_PID) syslog.syslog(syslog.LOG_INFO, "Started background process waiting on bridge %s" % (br,)) count = 0 error = None sleep_time = 10 while count < 60: count += 1 try: vswitchCfgMod(["--", "del-controller", br, "--", "set-controller", br, target, "--", "set-fail-mode", br, "secure"]) except XenAPIPlugin.Failure, e: error = e syslog.syslog(syslog.LOG_INFO, "Attempt to set br %s controller failed" % (br,)) time.sleep(sleep_time) continue syslog.syslog(syslog.LOG_INFO, "Successfully set br %s controller to %s" % (br, repr(target))) return syslog.syslog(syslog.LOG_ERR, "Giving up on setting br %s controller" % (br,)) def setControllerCfg(controller): vswitchCfgMod(["--", "del-controller", "--", "del-ssl", "--", "--bootstrap", "set-ssl", "/etc/xensource/xapi-ssl.pem", "/etc/xensource/xapi-ssl.pem", "/etc/ovs-vswitchd.cacert", "--", "set-controller", "ssl:" + controller]) def vswitchCfgQuery(action): cmd = [vsctl, "-vANY:console:emer", action] output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate() if len(output) == 0 or output[0] == None: output = "" else: output = output[0].strip() return output def vswitchCfgMod(action_args): cmd = [vsctl, "-vANY:console:emer"] + action_args exitcode = subprocess.call(cmd) if exitcode != 0: raise XenAPIPlugin.Failure("VSWITCH_CONFIG_MOD_FAILURE", [ str(exitcode) , str(action_args) ]) if __name__ == "__main__": XenAPIPlugin.dispatch({"update": update})