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 Nicira Networks.
10 from XSConsoleLog import *
16 cfg_mod="/usr/bin/ovs-cfg-mod"
17 vswitchd_cfg_filename="/etc/ovs-vswitchd.conf"
19 if __name__ == "__main__":
20 raise Exception("This script is a plugin for xsconsole and cannot run independently")
22 from XSConsoleStandard import *
27 def __init__(self, name, processname=None):
29 self.processname = processname
30 if self.processname == None:
31 self.processname = name
35 output = ShellPipe(["service", self.name, "version"]).Stdout()
36 except StandardError, e:
37 XSLogError("vswitch version retrieval error: " + str(e))
40 if self.processname in line:
41 return line.split()[-1]
46 output = ShellPipe(["service", self.name, "status"]).Stdout()
47 except StandardError, e:
48 XSLogError("vswitch status retrieval error: " + str(e))
53 if self.processname not in line:
55 elif "running" in line:
65 ShellPipe(["service", self.name, "restart"]).Call()
66 except StandardError, e:
67 XSLogError("vswitch restart error: " + str(e))
70 def Inst(cls, name, processname=None):
72 if processname != None:
73 key = key + "-" + processname
74 if name not in cls.service:
75 cls.service[key] = VSwitchService(name, processname)
76 return cls.service[key]
83 output = ShellPipe([cfg_mod, "-vANY:console:emer", "-F",
84 vswitchd_cfg_filename, "-q", key]).Stdout()
85 except StandardError, e:
86 XSLogError("config retrieval error: " + str(e))
92 output = output[0].strip()
96 class VSwitchControllerDialogue(Dialogue):
98 Dialogue.__init__(self)
102 self.hostsUpdated = 0
103 pool = data.GetPoolForThisHost()
105 self.controller = pool.get("other_config", {}).get("vSwitchController", "")
110 ChoiceDef(Lang("Set pool-wide controller"),
111 lambda: self.getController()),
112 ChoiceDef(Lang("Delete pool-wide controller"),
113 lambda: self.deleteController()),
114 ChoiceDef(Lang("Resync server controller config"),
115 lambda: self.syncController()),
116 # ChoiceDef(Lang("Restart ovs-vswitchd"),
117 # lambda: self.restartService("vswitch")),
118 # ChoiceDef(Lang("Restart ovs-brcompatd"),
119 # lambda: self.restartService("vswitch-brcompatd"))
121 self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
123 self.ChangeState("INITIAL")
126 pane = self.NewPane(DialoguePane(self.parent))
127 pane.TitleSet(Lang("Configure vSwitch"))
130 def ChangeState(self, inState):
135 def UpdateFields(self):
136 self.Pane().ResetPosition()
137 getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
139 def UpdateFieldsINITIAL(self):
141 pane.AddTitleField(Lang("Select an action"))
142 pane.AddMenuField(self.menu)
143 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
145 def UpdateFieldsGETCONTROLLER(self):
149 pane.AddTitleField(Lang("Enter IP address of controller"))
150 pane.AddInputField(Lang("Address", 16), self.controller, "address")
151 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
152 if pane.CurrentInput() is None:
153 pane.InputIndexSet(0)
155 def HandleKey(self, inKey):
157 if hasattr(self, "HandleKey" + self.state):
158 handled = getattr(self, "HandleKey" + self.state)(inKey)
159 if not handled and inKey == 'KEY_ESCAPE':
160 Layout.Inst().PopDialogue()
164 def HandleKeyINITIAL(self, inKey):
165 return self.menu.HandleKey(inKey)
167 def HandleKeyGETCONTROLLER(self, inKey):
169 if pane.CurrentInput() is None:
170 pane.InputIndexSet(0)
171 if inKey == 'KEY_ENTER':
172 inputValues = pane.GetFieldValues()
173 self.controller = inputValues['address']
174 Layout.Inst().PopDialogue()
176 # Make sure the controller is specified as a valid dotted quad
178 socket.inet_aton(self.controller)
180 Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format")))
183 Layout.Inst().TransientBanner(Lang("Setting controller..."))
185 self.SetController(self.controller)
186 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
188 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
190 self.ChangeState("INITIAL")
193 return pane.CurrentInput().HandleKey(inKey)
195 def restartService(self, name):
196 s = VSwitchService.Inst(name)
198 Layout.Inst().PopDialogue()
200 def getController(self):
201 self.ChangeState("GETCONTROLLER")
202 self.Pane().InputIndexSet(0)
204 def deleteController(self):
206 Layout.Inst().PopDialogue()
207 Layout.Inst().TransientBanner(Lang("Deleting controller..."))
209 self.SetController(None)
210 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
212 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
214 def syncController(self):
215 Layout.Inst().PopDialogue()
216 Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
218 Task.Sync(lambda s: self._updateThisServer(s))
219 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
221 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
223 def SetController(self, ip):
225 self.hostsUpdated = 0
226 Task.Sync(lambda s: self._modifyPoolConfig(s, "vSwitchController", ip))
227 # Should be done asynchronously, maybe with an external script?
228 Task.Sync(lambda s: self._updateActiveServers(s))
230 def _modifyPoolConfig(self, session, key, value):
231 """Modify pool configuration.
233 If value == None then delete key, otherwise set key to value."""
234 pools = session.xenapi.pool.get_all()
235 # We assume there is only ever one pool...
237 log.error("No pool for host.")
238 raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", [])
240 log.error("More than one pool for host.")
241 raise XenAPIPlugin.Failure("MORE_THAN_ONE_POOL_FOR_HOST", [])
242 session.xenapi.pool.remove_from_other_config(pools[0], key)
244 session.xenapi.pool.add_to_other_config(pools[0], key, value)
247 def _updateActiveServers(self, session):
248 hosts = session.xenapi.host.get_all()
249 self.hostsUpdated = 0
250 self.hostsInPool = len(hosts)
253 Layout.Inst().TransientBanner("Updating host %d out of %d"
254 % (self.hostsUpdated + 1, self.hostsInPool))
255 session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
256 self.hostsUpdated = self.hostsUpdated + 1
258 def _updateThisServer(self, session):
260 host = data.host.opaqueref()
261 session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
264 class XSFeatureVSwitch:
267 def StatusUpdateHandler(cls, inPane):
270 inPane.AddTitleField(Lang("vSwitch"))
274 inPane.AddStatusField(Lang("Version", 20),
275 VSwitchService.Inst("vswitch", "ovs-vswitchd").version())
279 pool = data.GetPoolForThisHost()
281 dbController = pool.get("other_config", {}).get("vSwitchController", "")
285 if dbController == "":
286 dbController = Lang("<None>")
287 inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
288 controller = VSwitchConfig.Get("mgmt.controller")
290 controller = Lang("<None>")
291 elif controller[0:4] == "ssl:":
292 controller = controller[4:]
293 inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
296 inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
297 VSwitchService.Inst("vswitch", "ovs-vswitchd").status())
298 inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
299 VSwitchService.Inst("vswitch", "ovs-brcompatd").status())
301 inPane.AddKeyHelpField( {
302 Lang("<Enter>") : Lang("Reconfigure"),
303 Lang("<F5>") : Lang("Refresh")
307 def ActivateHandler(cls):
308 DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
311 Importer.RegisterNamedPlugIn(
313 'VSwitch', # Key of this plugin for replacement, etc.
315 'menuname' : 'MENU_NETWORK',
316 'menupriority' : 800,
317 'menutext' : Lang('vSwitch') ,
318 'statusupdatehandler' : self.StatusUpdateHandler,
319 'activatehandler' : self.ActivateHandler
323 # Register this plugin when module is imported
324 XSFeatureVSwitch().Register()