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 pool = data.GetPoolForThisHost()
104 self.controller = pool.get("other_config", {}).get("vSwitchController", "")
109 ChoiceDef(Lang("Set pool-wide controller"),
110 lambda: self.getController()),
111 ChoiceDef(Lang("Delete pool-wide controller"),
112 lambda: self.deleteController()),
113 ChoiceDef(Lang("Resync server controller config"),
114 lambda: self.syncController()),
115 # ChoiceDef(Lang("Restart ovs-vswitchd"),
116 # lambda: self.restartService("vswitch")),
117 # ChoiceDef(Lang("Restart ovs-brcompatd"),
118 # lambda: self.restartService("vswitch-brcompatd"))
120 self.menu = Menu(self, None, Lang("Configure Open vSwitch"), choiceDefs)
122 self.ChangeState("INITIAL")
125 pane = self.NewPane(DialoguePane(self.parent))
126 pane.TitleSet(Lang("Configure Open vSwitch"))
129 def ChangeState(self, inState):
134 def UpdateFields(self):
135 self.Pane().ResetPosition()
136 getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
138 def UpdateFieldsINITIAL(self):
140 pane.AddTitleField(Lang("Select an action"))
141 pane.AddMenuField(self.menu)
142 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
144 def UpdateFieldsGETCONTROLLER(self):
148 pane.AddTitleField(Lang("Enter IP address of controller"))
149 pane.AddInputField(Lang("Address", 16), self.controller, "address")
150 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
151 if pane.CurrentInput() is None:
152 pane.InputIndexSet(0)
154 def HandleKey(self, inKey):
156 if hasattr(self, "HandleKey" + self.state):
157 handled = getattr(self, "HandleKey" + self.state)(inKey)
158 if not handled and inKey == 'KEY_ESCAPE':
159 Layout.Inst().PopDialogue()
163 def HandleKeyINITIAL(self, inKey):
164 return self.menu.HandleKey(inKey)
166 def HandleKeyGETCONTROLLER(self, inKey):
168 if pane.CurrentInput() is None:
169 pane.InputIndexSet(0)
170 if inKey == 'KEY_ENTER':
171 inputValues = pane.GetFieldValues()
172 self.controller = inputValues['address']
173 Layout.Inst().PopDialogue()
175 # Make sure the controller is specified as a valid dotted quad
177 socket.inet_aton(self.controller)
179 Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format")))
182 Layout.Inst().TransientBanner(Lang("Setting controller..."))
184 self.SetController(self.controller)
185 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
187 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
189 self.ChangeState("INITIAL")
192 return pane.CurrentInput().HandleKey(inKey)
194 def restartService(self, name):
195 s = VSwitchService.Inst(name)
197 Layout.Inst().PopDialogue()
199 def getController(self):
200 self.ChangeState("GETCONTROLLER")
201 self.Pane().InputIndexSet(0)
203 def deleteController(self):
205 Layout.Inst().PopDialogue()
206 Layout.Inst().TransientBanner(Lang("Deleting controller..."))
208 self.SetController(None)
209 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
211 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
213 def syncController(self):
214 Layout.Inst().PopDialogue()
215 Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
217 Task.Sync(lambda s: self._updateThisServer(s))
218 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
220 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
222 def SetController(self, ip):
224 self.hostsUpdated = 0
225 Task.Sync(lambda s: self._modifyPoolConfig(s, "vSwitchController", ip))
226 # Should be done asynchronously, maybe with an external script?
227 Task.Sync(lambda s: self._updateActiveServers(s))
229 def _modifyPoolConfig(self, session, key, value):
230 """Modify pool configuration.
232 If value == None then delete key, otherwise set key to value."""
233 pools = session.xenapi.pool.get_all()
234 # We assume there is only ever one pool...
236 XSLogFatal(Lang("No pool found for host."))
239 XSLogFatal(Lang("More than one pool for host."))
241 session.xenapi.pool.remove_from_other_config(pools[0], key)
243 session.xenapi.pool.add_to_other_config(pools[0], key, value)
246 def _updateActiveServers(self, session):
247 hosts = session.xenapi.host.get_all()
248 self.hostsUpdated = 0
249 self.hostsInPool = len(hosts)
252 Layout.Inst().TransientBanner("Updating host %d out of %d"
253 % (self.hostsUpdated + 1, self.hostsInPool))
254 session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {})
255 self.hostsUpdated = self.hostsUpdated + 1
257 def _updateThisServer(self, session):
259 host = data.host.opaqueref()
260 session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {})
263 class XSFeatureVSwitch:
266 def StatusUpdateHandler(cls, inPane):
269 inPane.AddTitleField(Lang("Open vSwitch"))
273 inPane.AddStatusField(Lang("Version", 20),
274 VSwitchService.Inst("openvswitch", "ovs-vswitchd").version())
278 pool = data.GetPoolForThisHost()
280 dbController = pool.get("other_config", {}).get("vSwitchController", "")
284 if dbController == "":
285 dbController = Lang("<None>")
286 inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
287 controller = VSwitchConfig.Get("get Open_vSwitch . managers")
288 controller = controller.strip('[]"')
291 controller = Lang("<None>")
292 elif controller[0:4] == "ssl:":
293 controller = controller.split(':')[1]
294 inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
297 inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
298 VSwitchService.Inst("openvswitch", "ovs-vswitchd").status())
299 inPane.AddStatusField(Lang("ovsdb-server status", 20),
300 VSwitchService.Inst("openvswitch", "ovsdb-server").status())
302 # Only XenServer 5.5.0 runs ovs-brcompatd
303 xs_version = data.host.software_version.product_version('')
304 if (xs_version == "5.5.0"):
305 inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
306 VSwitchService.Inst("openvswitch", "ovs-brcompatd").status())
308 inPane.AddKeyHelpField( {
309 Lang("<Enter>") : Lang("Reconfigure"),
310 Lang("<F5>") : Lang("Refresh")
314 def ActivateHandler(cls):
315 DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
318 Importer.RegisterNamedPlugIn(
320 'VSwitch', # Key of this plugin for replacement, etc.
322 'menuname' : 'MENU_NETWORK',
323 'menupriority' : 800,
324 'menutext' : Lang('Open vSwitch') ,
325 'statusupdatehandler' : self.StatusUpdateHandler,
326 'activatehandler' : self.ActivateHandler
330 # Register this plugin when module is imported
331 XSFeatureVSwitch().Register()