xenserver: Must pass --no-wait to ovs-vsctl before starting ovs-vswitchd.
[sliver-openvswitch.git] / xenserver / etc_xapi.d_plugins_vswitch-cfg-update
1 #!/usr/bin/env python
2 #
3 # xapi plugin script to update the cache of configuration items in the
4 # ovs-vswitchd configuration file that are managed in the xapi database
5 # when integrated with Citrix management tools.
6
7 # Copyright (C) 2009 Nicira Networks, Inc.
8 #
9 # Licensed under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at:
12 #
13 #     http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
20
21 # TBD: - error handling needs to be improved.  Currently this can leave
22 # TBD:   the system in a bad state if anything goes wrong.
23
24 import XenAPIPlugin
25 import XenAPI
26 import os
27 import subprocess
28
29 vsctl="/usr/bin/ovs-vsctl"
30 cacert_filename="/etc/ovs-vswitchd.cacert"
31
32 # Delete the CA certificate, so that we go back to boot-strapping mode
33 def delete_cacert():
34     try:
35         os.remove(cacert_filename)
36     except OSError:
37         # Ignore error if file doesn't exist
38         pass
39
40 def update(session, args):
41     pools = session.xenapi.pool.get_all()
42     # We assume there is only ever one pool...
43     if len(pools) == 0:
44         raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", [])
45     if len(pools) > 1:
46         raise XenAPIPlugin.Failure("MORE_THAN_ONE_POOL_FOR_HOST", [])
47     pool = session.xenapi.pool.get_record(pools[0])
48     try:
49         controller = pool["other_config"]["vSwitchController"]
50     except KeyError, e:
51         controller = ""
52     if controller == "do-not-update":
53         return "XAPI key set to do-not-update"
54     if controller.startswith("for-bridges|"):
55         l = controller.split("|")[1:]
56         for netctrl in l:
57             xapiNet = session.xenapi.network
58             n, t = netctrl.split("=")
59             blist = xapiNet.get_by_name_label(n)
60             if len(blist) == 0:
61                 # If there is no bridge for the network, just keep
62                 # going so we bring up as much as possible.
63                 continue
64             elif len(blist) > 1:
65                 raise XenAPIPlugin.Failure("TOO_MANY_MATCHING_NETWORKS", [n,blist])
66             b = xapiNet.get_bridge(blist[0])
67             setBrControllerCfg(b, t)
68         return "Completed setting controllers on specific bridges"
69     currentController = vswitchCurrentController()
70     if controller == "" and currentController != "":
71         delete_cacert()
72         removeControllerCfg()
73         return "Successfully removed controller config"
74     elif controller != currentController:
75         delete_cacert()
76         setControllerCfg(controller)
77         return "Successfully set controller to " + controller
78     else:
79         return "No change to configuration"
80
81 def vswitchCurrentController():
82     controller = vswitchCfgQuery("get-controller")
83     if controller == "":
84         return controller
85     if len(controller) < 4 or controller[0:4] != "ssl:":
86         return controller
87     else:
88         return controller[4:]
89
90 def removeControllerCfg():
91     vswitchCfgMod(["--", "del-controller",
92                    "--", "del-ssl"])
93
94 def setBrControllerCfg(br, target):
95     # Terrible hack... When this is run at boot the required bridges
96     # may not be present.  So, we fork a process for each bridge that
97     # needs to be set which sits around in the background and updates
98     # it when it becomes available, finally timing out after a long
99     # interval if it never becomes available.
100     #
101     # The right way to do this is to hook the bridge creation somehow
102     # but I don't believe this is possible in XenServer 5.5 without
103     # either listening to XAPI events or writing it in C code in
104     # brcompatd.
105     import time
106     import syslog
107     import resource
108
109     p = os.fork()
110     if p != 0:
111         return
112
113     os.setsid()
114     p = os.fork()
115     if p != 0:
116         sys.exit(0)
117
118     os.chdir("/")
119     os.umask(0)
120     maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
121     if maxfd == resource.RLIM_INFINITY:
122         maxfd = 1024
123     for fd in range(0, maxfd):
124         try:
125             os.close(fd)
126         except OSError:
127             pass
128     os.open("/dev/null", os.O_RDWR)
129     os.dup2(0, 1)
130     os.dup2(0, 2)
131
132     syslog.openlog("vswitch-cfg-update", syslog.LOG_PID)
133     syslog.syslog(syslog.LOG_INFO,
134                   "Started background process waiting on bridge %s" % (br,))
135
136     count = 0
137     error = None
138     sleep_time = 10
139     while count < 60:
140         count += 1
141         try:
142             vswitchCfgMod(["--", "del-controller", br,
143                            "--", "set-controller", br, target,
144                            "--", "set-fail-mode", br, "secure"])
145         except XenAPIPlugin.Failure, e:
146             error = e
147             syslog.syslog(syslog.LOG_INFO,
148                           "Attempt to set br %s controller failed" % (br,))
149             time.sleep(sleep_time)
150             continue
151         syslog.syslog(syslog.LOG_INFO,
152                       "Successfully set br %s controller to %s" % (br, repr(target)))
153         return
154     syslog.syslog(syslog.LOG_ERR,
155                   "Giving up on setting br %s controller" % (br,))
156
157 def setControllerCfg(controller):
158     vswitchCfgMod(["--", "del-controller",
159                    "--", "del-ssl",
160                    "--", "--bootstrap", "set-ssl",
161                    "/etc/xensource/xapi-ssl.pem",
162                    "/etc/xensource/xapi-ssl.pem",
163                    "/etc/ovs-vswitchd.cacert",
164                    "--", "set-controller", "ssl:" + controller])
165
166 def vswitchCfgQuery(action):
167     cmd = [vsctl, "-vANY:console:emer", action]
168     output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
169     if len(output) == 0 or output[0] == None:
170         output = ""
171     else:
172         output = output[0].strip()
173     return output
174
175 def vswitchCfgMod(action_args):
176     cmd = [vsctl, "-vANY:console:emer"] + action_args
177     exitcode = subprocess.call(cmd)
178     if exitcode != 0:
179         raise XenAPIPlugin.Failure("VSWITCH_CONFIG_MOD_FAILURE",
180                                    [ str(exitcode) , str(action_args) ])
181     
182 if __name__ == "__main__":
183     XenAPIPlugin.dispatch({"update": update})