1 # Copyright (c) 2007-2010 Citrix Systems Inc.
2 # Copyright (c) 2009,2010 Nicira Networks.
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; version 2 only.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 from XSConsoleLog import *
23 vsctl="/usr/bin/ovs-vsctl"
25 if __name__ == "__main__":
26 raise Exception("This script is a plugin for xsconsole and cannot run independently")
28 from XSConsoleStandard import *
33 def __init__(self, name, processname=None):
35 self.processname = processname
36 if self.processname == None:
37 self.processname = name
41 output = ShellPipe(["service", self.name, "version"]).Stdout()
42 except StandardError, e:
43 XSLogError("vswitch version retrieval error: " + str(e))
46 if self.processname in line:
47 return line.split()[-1]
52 output = ShellPipe(["service", self.name, "status"]).Stdout()
53 except StandardError, e:
54 XSLogError("vswitch status retrieval error: " + str(e))
59 if self.processname not in line:
61 elif "running" in line:
71 ShellPipe(["service", self.name, "restart"]).Call()
72 except StandardError, e:
73 XSLogError("vswitch restart error: " + str(e))
76 def Inst(cls, name, processname=None):
78 if processname != None:
79 key = key + "-" + processname
80 if name not in cls.service:
81 cls.service[key] = VSwitchService(name, processname)
82 return cls.service[key]
89 arg = [vsctl, "--timeout=30", "-vANY:console:emer"] + action.split()
90 output = ShellPipe(arg).Stdout()
91 except StandardError, e:
92 XSLogError("config retrieval error: " + str(e))
98 output = output[0].strip()
102 class VSwitchControllerDialogue(Dialogue):
104 Dialogue.__init__(self)
108 self.hostsUpdated = 0
109 self.xs_version = data.host.software_version.product_version('')
110 pool = data.GetPoolForThisHost()
112 if self.xs_version == "5.5.0":
113 self.controller = pool.get("other_config", {}).get("vSwitchController", "")
115 self.controller = pool.get("vswitch_controller", "")
120 ChoiceDef(Lang("Set pool-wide controller"),
121 lambda: self.getController()),
122 ChoiceDef(Lang("Delete pool-wide controller"),
123 lambda: self.deleteController()),
124 ChoiceDef(Lang("Resync server controller config"),
125 lambda: self.syncController()),
126 # ChoiceDef(Lang("Restart ovs-vswitchd"),
127 # lambda: self.restartService("vswitch")),
128 # ChoiceDef(Lang("Restart ovs-brcompatd"),
129 # lambda: self.restartService("vswitch-brcompatd"))
131 self.menu = Menu(self, None, Lang("Configure Open vSwitch"), choiceDefs)
133 self.ChangeState("INITIAL")
136 pane = self.NewPane(DialoguePane(self.parent))
137 pane.TitleSet(Lang("Configure Open vSwitch"))
140 def ChangeState(self, inState):
145 def UpdateFields(self):
146 self.Pane().ResetPosition()
147 getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
149 def UpdateFieldsINITIAL(self):
151 pane.AddTitleField(Lang("Select an action"))
152 pane.AddMenuField(self.menu)
153 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
155 def UpdateFieldsGETCONTROLLER(self):
159 pane.AddTitleField(Lang("Enter IP address of controller"))
160 pane.AddInputField(Lang("Address", 16), self.controller, "address")
161 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
162 if pane.CurrentInput() is None:
163 pane.InputIndexSet(0)
165 def HandleKey(self, inKey):
167 if hasattr(self, "HandleKey" + self.state):
168 handled = getattr(self, "HandleKey" + self.state)(inKey)
169 if not handled and inKey == 'KEY_ESCAPE':
170 Layout.Inst().PopDialogue()
174 def HandleKeyINITIAL(self, inKey):
175 return self.menu.HandleKey(inKey)
177 def HandleKeyGETCONTROLLER(self, inKey):
179 if pane.CurrentInput() is None:
180 pane.InputIndexSet(0)
181 if inKey == 'KEY_ENTER':
182 inputValues = pane.GetFieldValues()
183 self.controller = inputValues['address']
184 Layout.Inst().PopDialogue()
186 # Make sure the controller is specified as a valid dotted quad
188 socket.inet_aton(self.controller)
190 Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format")))
193 Layout.Inst().TransientBanner(Lang("Setting controller..."))
195 self.SetController(self.controller)
196 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
198 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
200 self.ChangeState("INITIAL")
203 return pane.CurrentInput().HandleKey(inKey)
205 def restartService(self, name):
206 s = VSwitchService.Inst(name)
208 Layout.Inst().PopDialogue()
210 def getController(self):
211 self.ChangeState("GETCONTROLLER")
212 self.Pane().InputIndexSet(0)
214 def deleteController(self):
216 Layout.Inst().PopDialogue()
217 Layout.Inst().TransientBanner(Lang("Deleting controller..."))
219 self.SetController(None)
220 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
222 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
224 def syncController(self):
225 Layout.Inst().PopDialogue()
226 Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
228 Task.Sync(lambda s: self._updateThisServer(s))
229 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
231 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
233 def SetController(self, ip):
235 self.hostsUpdated = 0
236 Task.Sync(lambda s: self._modifyPoolConfig(s, ip or ""))
237 # Should be done asynchronously, maybe with an external script?
238 Task.Sync(lambda s: self._updateActiveServers(s))
240 def _modifyPoolConfig(self, session, value):
241 """Modify pool configuration.
243 If value == "" then delete configuration, otherwise set to value.
245 pools = session.xenapi.pool.get_all()
246 # We assume there is only ever one pool...
248 XSLogFatal(Lang("No pool found for host."))
251 XSLogFatal(Lang("More than one pool for host."))
253 if self.xs_version == "5.5.0":
254 key = "vSwitchController"
255 session.xenapi.pool.remove_from_other_config(pools[0], key)
257 session.xenapi.pool.add_to_other_config(pools[0], key, value)
259 session.xenapi.pool.set_vswitch_controller(value)
262 def _updateActiveServers(self, session):
263 hosts = session.xenapi.host.get_all()
264 self.hostsUpdated = 0
265 self.hostsInPool = len(hosts)
268 Layout.Inst().TransientBanner("Updating host %d out of %d"
269 % (self.hostsUpdated + 1, self.hostsInPool))
270 session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {})
271 self.hostsUpdated = self.hostsUpdated + 1
273 def _updateThisServer(self, session):
275 host = data.host.opaqueref()
276 session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {})
279 class XSFeatureVSwitch:
282 def StatusUpdateHandler(cls, inPane):
284 xs_version = data.host.software_version.product_version('')
286 inPane.AddTitleField(Lang("Open vSwitch"))
290 inPane.AddStatusField(Lang("Version", 20),
291 VSwitchService.Inst("openvswitch", "ovs-vswitchd").version())
295 pool = data.GetPoolForThisHost()
297 if (xs_version == "5.5.0"):
298 dbController = pool.get("other_config", {}).get("vSwitchController", "")
300 dbController = pool.get("vswitch_controller", "")
304 if dbController == "":
305 dbController = Lang("<None>")
306 inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
307 controller = VSwitchConfig.Get("get Open_vSwitch . managers")
308 controller = controller.strip('[]"')
311 controller = Lang("<None>")
312 elif controller[0:4] == "ssl:":
313 controller = controller.split(':')[1]
314 inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
317 inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
318 VSwitchService.Inst("openvswitch", "ovs-vswitchd").status())
319 inPane.AddStatusField(Lang("ovsdb-server status", 20),
320 VSwitchService.Inst("openvswitch", "ovsdb-server").status())
322 # Only XenServer 5.5.0 runs ovs-brcompatd
323 if (xs_version == "5.5.0"):
324 inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
325 VSwitchService.Inst("openvswitch", "ovs-brcompatd").status())
327 inPane.AddKeyHelpField( {
328 Lang("<Enter>") : Lang("Reconfigure"),
329 Lang("<F5>") : Lang("Refresh")
333 def ActivateHandler(cls):
334 DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
337 Importer.RegisterNamedPlugIn(
339 'VSwitch', # Key of this plugin for replacement, etc.
341 'menuname' : 'MENU_NETWORK',
342 'menupriority' : 800,
343 'menutext' : Lang('Open vSwitch') ,
344 'statusupdatehandler' : self.StatusUpdateHandler,
345 'activatehandler' : self.ActivateHandler
349 # Register this plugin when module is imported, IFF vswitchd is running
350 if os.path.exists('/var/run/openvswitch/ovs-vswitchd.pid'):
351 XSFeatureVSwitch().Register()