1 # Copyright (c) Citrix Systems 2008. All rights reserved.
2 # xsconsole is proprietary software.
4 # Xen, the Xen logo, XenCenter, XenMotion are trademarks or registered
5 # trademarks of Citrix Systems, Inc., in the United States and other
8 # Copyright (c) 2009, 2010 Nicira Networks.
10 from XSConsoleLog import *
16 vsctl="/usr/bin/ovs-vsctl"
18 if __name__ == "__main__":
19 raise Exception("This script is a plugin for xsconsole and cannot run independently")
21 from XSConsoleStandard import *
26 def __init__(self, name, processname=None):
28 self.processname = processname
29 if self.processname == None:
30 self.processname = name
34 output = ShellPipe(["service", self.name, "version"]).Stdout()
35 except StandardError, e:
36 XSLogError("vswitch version retrieval error: " + str(e))
39 if self.processname in line:
40 return line.split()[-1]
45 output = ShellPipe(["service", self.name, "status"]).Stdout()
46 except StandardError, e:
47 XSLogError("vswitch status retrieval error: " + str(e))
52 if self.processname not in line:
54 elif "running" in line:
64 ShellPipe(["service", self.name, "restart"]).Call()
65 except StandardError, e:
66 XSLogError("vswitch restart error: " + str(e))
69 def Inst(cls, name, processname=None):
71 if processname != None:
72 key = key + "-" + processname
73 if name not in cls.service:
74 cls.service[key] = VSwitchService(name, processname)
75 return cls.service[key]
82 arg = [vsctl, "-vANY:console:emer"] + action.split()
83 output = ShellPipe(arg).Stdout()
84 except StandardError, e:
85 XSLogError("config retrieval error: " + str(e))
91 output = output[0].strip()
95 class VSwitchControllerDialogue(Dialogue):
97 Dialogue.__init__(self)
101 self.hostsUpdated = 0
102 self.xs_version = data.host.software_version.product_version('')
103 pool = data.GetPoolForThisHost()
105 if self.xs_version == "5.5.0":
106 self.controller = pool.get("other_config", {}).get("vSwitchController", "")
108 self.controller = pool.get("vswitch_controller", "")
113 ChoiceDef(Lang("Set pool-wide controller"),
114 lambda: self.getController()),
115 ChoiceDef(Lang("Delete pool-wide controller"),
116 lambda: self.deleteController()),
117 ChoiceDef(Lang("Resync server controller config"),
118 lambda: self.syncController()),
119 # ChoiceDef(Lang("Restart ovs-vswitchd"),
120 # lambda: self.restartService("vswitch")),
121 # ChoiceDef(Lang("Restart ovs-brcompatd"),
122 # lambda: self.restartService("vswitch-brcompatd"))
124 self.menu = Menu(self, None, Lang("Configure Open vSwitch"), choiceDefs)
126 self.ChangeState("INITIAL")
129 pane = self.NewPane(DialoguePane(self.parent))
130 pane.TitleSet(Lang("Configure Open vSwitch"))
133 def ChangeState(self, inState):
138 def UpdateFields(self):
139 self.Pane().ResetPosition()
140 getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
142 def UpdateFieldsINITIAL(self):
144 pane.AddTitleField(Lang("Select an action"))
145 pane.AddMenuField(self.menu)
146 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
148 def UpdateFieldsGETCONTROLLER(self):
152 pane.AddTitleField(Lang("Enter IP address of controller"))
153 pane.AddInputField(Lang("Address", 16), self.controller, "address")
154 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
155 if pane.CurrentInput() is None:
156 pane.InputIndexSet(0)
158 def HandleKey(self, inKey):
160 if hasattr(self, "HandleKey" + self.state):
161 handled = getattr(self, "HandleKey" + self.state)(inKey)
162 if not handled and inKey == 'KEY_ESCAPE':
163 Layout.Inst().PopDialogue()
167 def HandleKeyINITIAL(self, inKey):
168 return self.menu.HandleKey(inKey)
170 def HandleKeyGETCONTROLLER(self, inKey):
172 if pane.CurrentInput() is None:
173 pane.InputIndexSet(0)
174 if inKey == 'KEY_ENTER':
175 inputValues = pane.GetFieldValues()
176 self.controller = inputValues['address']
177 Layout.Inst().PopDialogue()
179 # Make sure the controller is specified as a valid dotted quad
181 socket.inet_aton(self.controller)
183 Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format")))
186 Layout.Inst().TransientBanner(Lang("Setting controller..."))
188 self.SetController(self.controller)
189 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
191 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
193 self.ChangeState("INITIAL")
196 return pane.CurrentInput().HandleKey(inKey)
198 def restartService(self, name):
199 s = VSwitchService.Inst(name)
201 Layout.Inst().PopDialogue()
203 def getController(self):
204 self.ChangeState("GETCONTROLLER")
205 self.Pane().InputIndexSet(0)
207 def deleteController(self):
209 Layout.Inst().PopDialogue()
210 Layout.Inst().TransientBanner(Lang("Deleting controller..."))
212 self.SetController(None)
213 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
215 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
217 def syncController(self):
218 Layout.Inst().PopDialogue()
219 Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
221 Task.Sync(lambda s: self._updateThisServer(s))
222 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
224 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
226 def SetController(self, ip):
228 self.hostsUpdated = 0
229 Task.Sync(lambda s: self._modifyPoolConfig(s, ip or ""))
230 # Should be done asynchronously, maybe with an external script?
231 Task.Sync(lambda s: self._updateActiveServers(s))
233 def _modifyPoolConfig(self, session, value):
234 """Modify pool configuration.
236 If value == "" then delete configuration, otherwise set to value.
238 pools = session.xenapi.pool.get_all()
239 # We assume there is only ever one pool...
241 XSLogFatal(Lang("No pool found for host."))
244 XSLogFatal(Lang("More than one pool for host."))
246 if self.xs_version == "5.5.0":
247 key = "vSwitchController"
248 session.xenapi.pool.remove_from_other_config(pools[0], key)
250 session.xenapi.pool.add_to_other_config(pools[0], key, value)
252 session.xenapi.pool.set_vswitch_controller(value)
255 def _updateActiveServers(self, session):
256 hosts = session.xenapi.host.get_all()
257 self.hostsUpdated = 0
258 self.hostsInPool = len(hosts)
261 Layout.Inst().TransientBanner("Updating host %d out of %d"
262 % (self.hostsUpdated + 1, self.hostsInPool))
263 session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {})
264 self.hostsUpdated = self.hostsUpdated + 1
266 def _updateThisServer(self, session):
268 host = data.host.opaqueref()
269 session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {})
272 class XSFeatureVSwitch:
275 def StatusUpdateHandler(cls, inPane):
277 xs_version = data.host.software_version.product_version('')
279 inPane.AddTitleField(Lang("Open vSwitch"))
283 inPane.AddStatusField(Lang("Version", 20),
284 VSwitchService.Inst("openvswitch", "ovs-vswitchd").version())
288 pool = data.GetPoolForThisHost()
290 if (xs_version == "5.5.0"):
291 dbController = pool.get("other_config", {}).get("vSwitchController", "")
293 dbController = pool.get("vswitch_controller", "")
297 if dbController == "":
298 dbController = Lang("<None>")
299 inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
300 controller = VSwitchConfig.Get("get Open_vSwitch . managers")
301 controller = controller.strip('[]"')
304 controller = Lang("<None>")
305 elif controller[0:4] == "ssl:":
306 controller = controller.split(':')[1]
307 inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
310 inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
311 VSwitchService.Inst("openvswitch", "ovs-vswitchd").status())
312 inPane.AddStatusField(Lang("ovsdb-server status", 20),
313 VSwitchService.Inst("openvswitch", "ovsdb-server").status())
315 # Only XenServer 5.5.0 runs ovs-brcompatd
316 if (xs_version == "5.5.0"):
317 inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
318 VSwitchService.Inst("openvswitch", "ovs-brcompatd").status())
320 inPane.AddKeyHelpField( {
321 Lang("<Enter>") : Lang("Reconfigure"),
322 Lang("<F5>") : Lang("Refresh")
326 def ActivateHandler(cls):
327 DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
330 Importer.RegisterNamedPlugIn(
332 'VSwitch', # Key of this plugin for replacement, etc.
334 'menuname' : 'MENU_NETWORK',
335 'menupriority' : 800,
336 'menutext' : Lang('Open vSwitch') ,
337 'statusupdatehandler' : self.StatusUpdateHandler,
338 'activatehandler' : self.ActivateHandler
342 # Register this plugin when module is imported, IFF vswitchd is running
343 if os.path.exists('/var/run/openvswitch/ovs-vswitchd.pid'):
344 XSFeatureVSwitch().Register()