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.
11 log = logging.getLogger("vswitch-cfg-update")
12 logging.basicConfig(filename="/var/log/vswitch-xsplugin.log", level=logging.DEBUG)
18 cfg_mod="/root/vswitch/bin/ovs-cfg-mod"
19 vswitchd_cfg_filename="/etc/ovs-vswitchd.conf"
21 if __name__ == "__main__":
22 raise Exception("This script is a plugin for xsconsole and cannot run independently")
24 from XSConsoleStandard import *
29 def __init__(self, name, processname=None):
31 self.processname = processname
32 if self.processname == None:
33 self.processname = name
37 output = ShellPipe(["service", self.name, "version"]).Stdout()
38 except StandardError, e:
39 log.error("version retrieval error: " + str(e))
42 if self.processname in line:
43 return line.split()[-1]
48 output = ShellPipe(["service", self.name, "status"]).Stdout()
49 except StandardError, e:
50 log.error("status retrieval error: " + str(e))
55 if self.processname not in line:
57 elif "running" in line:
67 ShellPipe(["service", self.name, "restart"]).Call()
68 except StandardError, e:
69 log.error("restart error: " + str(e))
72 def Inst(cls, name, processname=None):
74 if processname != None:
75 key = key + "-" + processname
76 if name not in cls.service:
77 cls.service[key] = VSwitchService(name, processname)
78 return cls.service[key]
85 output = ShellPipe([cfg_mod, "-vANY:console:emer", "-F",
86 vswitchd_cfg_filename, "-q", key]).Stdout()
87 except StandardError, e:
88 log.error("config retrieval error: " + str(e))
94 output = output[0].strip()
98 class VSwitchControllerDialogue(Dialogue):
100 Dialogue.__init__(self)
104 self.hostsUpdated = 0
105 pool = data.GetPoolForThisHost()
107 self.controller = pool.get("other_config", {}).get("vSwitchController", "")
112 ChoiceDef(Lang("Set pool-wide controller"),
113 lambda: self.getController()),
114 ChoiceDef(Lang("Delete pool-wide controller"),
115 lambda: self.deleteController()),
116 ChoiceDef(Lang("Resync server controller config"),
117 lambda: self.syncController()),
118 # ChoiceDef(Lang("Restart ovs-vswitchd"),
119 # lambda: self.restartService("vswitch")),
120 # ChoiceDef(Lang("Restart ovs-brcompatd"),
121 # lambda: self.restartService("vswitch-brcompatd"))
123 self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
125 self.ChangeState("INITIAL")
128 pane = self.NewPane(DialoguePane(self.parent))
129 pane.TitleSet(Lang("Configure vSwitch"))
132 def ChangeState(self, inState):
137 def UpdateFields(self):
138 self.Pane().ResetPosition()
139 getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
141 def UpdateFieldsINITIAL(self):
143 pane.AddTitleField(Lang("Select an action"))
144 pane.AddMenuField(self.menu)
145 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
147 def UpdateFieldsGETCONTROLLER(self):
151 pane.AddTitleField(Lang("Enter IP address of controller"))
152 pane.AddInputField(Lang("Address", 16), self.controller, "address")
153 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
154 if pane.CurrentInput() is None:
155 pane.InputIndexSet(0)
157 def HandleKey(self, inKey):
159 if hasattr(self, "HandleKey" + self.state):
160 handled = getattr(self, "HandleKey" + self.state)(inKey)
161 if not handled and inKey == 'KEY_ESCAPE':
162 Layout.Inst().PopDialogue()
166 def HandleKeyINITIAL(self, inKey):
167 return self.menu.HandleKey(inKey)
169 def HandleKeyGETCONTROLLER(self, inKey):
171 if pane.CurrentInput() is None:
172 pane.InputIndexSet(0)
173 if inKey == 'KEY_ENTER':
174 inputValues = pane.GetFieldValues()
175 self.controller = inputValues['address']
176 Layout.Inst().PopDialogue()
178 # Make sure the controller is specified as a valid dotted quad
180 socket.inet_aton(self.controller)
182 Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format")))
185 Layout.Inst().TransientBanner(Lang("Setting controller..."))
187 self.SetController(self.controller)
188 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
190 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
192 self.ChangeState("INITIAL")
195 return pane.CurrentInput().HandleKey(inKey)
197 def restartService(self, name):
198 s = VSwitchService.Inst(name)
200 Layout.Inst().PopDialogue()
202 def getController(self):
203 self.ChangeState("GETCONTROLLER")
204 self.Pane().InputIndexSet(0)
206 def deleteController(self):
208 Layout.Inst().PopDialogue()
209 Layout.Inst().TransientBanner(Lang("Deleting controller..."))
211 self.SetController(None)
212 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
214 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
216 def syncController(self):
217 Layout.Inst().PopDialogue()
218 Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
220 Task.Sync(lambda s: self._updateThisServer(s))
221 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
223 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
225 def SetController(self, ip):
227 self.hostsUpdated = 0
228 Task.Sync(lambda s: self._modifyPoolConfig(s, "vSwitchController", ip))
229 # Should be done asynchronously, maybe with an external script?
230 Task.Sync(lambda s: self._updateActiveServers(s))
232 def _modifyPoolConfig(self, session, key, value):
233 """Modify pool configuration.
235 If value == None then delete key, otherwise set key to value."""
236 pools = session.xenapi.pool.get_all()
237 # We assume there is only ever one pool...
239 log.error("No pool for host.")
240 raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", [])
242 log.error("More than one pool for host.")
243 raise XenAPIPlugin.Failure("MORE_THAN_ONE_POOL_FOR_HOST", [])
244 session.xenapi.pool.remove_from_other_config(pools[0], key)
246 session.xenapi.pool.add_to_other_config(pools[0], key, value)
249 def _updateActiveServers(self, session):
250 hosts = session.xenapi.host.get_all()
251 self.hostsUpdated = 0
252 self.hostsInPool = len(hosts)
255 Layout.Inst().TransientBanner("Updating host %d out of %d"
256 % (self.hostsUpdated + 1, self.hostsInPool))
257 session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
258 self.hostsUpdated = self.hostsUpdated + 1
260 def _updateThisServer(self, session):
262 host = data.host.opaqueref()
263 session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
266 class XSFeatureVSwitch:
269 def StatusUpdateHandler(cls, inPane):
272 inPane.AddTitleField(Lang("vSwitch"))
276 inPane.AddStatusField(Lang("Version", 20),
277 VSwitchService.Inst("vswitch", "ovs-vswitchd").version())
281 pool = data.GetPoolForThisHost()
283 dbController = pool.get("other_config", {}).get("vSwitchController", "")
287 if dbController == "":
288 dbController = Lang("<None>")
289 inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
290 controller = VSwitchConfig.Get("mgmt.controller")
292 controller = Lang("<None>")
293 elif controller[0:4] == "ssl:":
294 controller = controller[4:]
295 inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
298 inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
299 VSwitchService.Inst("vswitch", "ovs-vswitchd").status())
300 inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
301 VSwitchService.Inst("vswitch", "ovs-brcompatd").status())
303 inPane.AddKeyHelpField( {
304 Lang("<Enter>") : Lang("Reconfigure"),
305 Lang("<F5>") : Lang("Refresh")
309 def ActivateHandler(cls):
310 DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
313 Importer.RegisterNamedPlugIn(
315 'VSwitch', # Key of this plugin for replacement, etc.
317 'menuname' : 'MENU_NETWORK',
318 'menupriority' : 800,
319 'menutext' : Lang('vSwitch') ,
320 'statusupdatehandler' : self.StatusUpdateHandler,
321 'activatehandler' : self.ActivateHandler
325 # Register this plugin when module is imported
326 XSFeatureVSwitch().Register()