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)
17 cfg_mod="/root/vswitch/bin/ovs-cfg-mod"
18 vswitchd_cfg_filename="/etc/ovs-vswitchd.conf"
20 if __name__ == "__main__":
21 raise Exception("This script is a plugin for xsconsole and cannot run independently")
23 from XSConsoleStandard import *
28 def __init__(self, name, processname=None):
30 self.processname = processname
31 if self.processname == None:
32 self.processname = name
36 output = ShellPipe(["service", self.name, "status"]).Stdout()
37 except StandardError, e:
38 log.error("status retrieval error: " + str(e))
43 if self.processname not in l:
55 ShellPipe(["service", self.name, "restart"]).Call()
56 except StandardError, e:
57 log.error("restart error: " + str(e))
60 def Inst(cls, name, processname=None):
62 if processname != None:
63 key = key + "-" + processname
64 if name not in cls.service:
65 cls.service[key] = VSwitchService(name, processname)
66 return cls.service[key]
73 output = ShellPipe([cfg_mod, "-vANY:console:emer", "-F",
74 vswitchd_cfg_filename, "-q", key]).Stdout()
75 except StandardError, e:
76 log.error("config retrieval error: " + str(e))
82 output = output[0].strip()
86 class VSwitchControllerDialogue(Dialogue):
88 Dialogue.__init__(self)
93 pool = data.GetPoolForThisHost()
95 self.controller = pool.get("other_config", {}).get("vSwitchController", "")
100 ChoiceDef(Lang("Set pool-wide controller"),
101 lambda: self.getController()),
102 ChoiceDef(Lang("Delete pool-wide controller"),
103 lambda: self.deleteController()),
104 ChoiceDef(Lang("Resync server controller config"),
105 lambda: self.syncController()),
106 # ChoiceDef(Lang("Restart ovs-vswitchd"),
107 # lambda: self.restartService("vswitch")),
108 # ChoiceDef(Lang("Restart ovs-brcompatd"),
109 # lambda: self.restartService("vswitch-brcompatd"))
111 self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
113 self.ChangeState("INITIAL")
116 pane = self.NewPane(DialoguePane(self.parent))
117 pane.TitleSet(Lang("Configure vSwitch"))
120 def ChangeState(self, inState):
125 def UpdateFields(self):
126 self.Pane().ResetPosition()
127 getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
129 def UpdateFieldsINITIAL(self):
131 pane.AddTitleField(Lang("Select an action"))
132 pane.AddMenuField(self.menu)
133 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
135 def UpdateFieldsGETCONTROLLER(self):
139 pane.AddTitleField(Lang("Enter IP address of controller"))
140 pane.AddInputField(Lang("Address", 16), self.controller, "address")
141 pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
142 if pane.CurrentInput() is None:
143 pane.InputIndexSet(0)
145 def HandleKey(self, inKey):
147 if hasattr(self, "HandleKey" + self.state):
148 handled = getattr(self, "HandleKey" + self.state)(inKey)
149 if not handled and inKey == 'KEY_ESCAPE':
150 Layout.Inst().PopDialogue()
154 def HandleKeyINITIAL(self, inKey):
155 return self.menu.HandleKey(inKey)
157 def HandleKeyGETCONTROLLER(self, inKey):
159 if pane.CurrentInput() is None:
160 pane.InputIndexSet(0)
161 if inKey == 'KEY_ENTER':
162 inputValues = pane.GetFieldValues()
163 self.controller = inputValues['address']
164 Layout.Inst().PopDialogue()
165 Layout.Inst().TransientBanner(Lang("Setting controller..."))
167 self.SetController(self.controller)
168 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
170 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
172 self.ChangeState("INITIAL")
175 return pane.CurrentInput().HandleKey(inKey)
177 def restartService(self, name):
178 s = VSwitchService.Inst(name)
180 Layout.Inst().PopDialogue()
182 def getController(self):
183 self.ChangeState("GETCONTROLLER")
184 self.Pane().InputIndexSet(0)
186 def deleteController(self):
188 Layout.Inst().PopDialogue()
189 Layout.Inst().TransientBanner(Lang("Deleting controller..."))
191 self.SetController(None)
192 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
194 Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
196 def syncController(self):
197 Layout.Inst().PopDialogue()
198 Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
200 Task.Sync(lambda s: self._updateThisServer(s))
201 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
203 Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
205 def SetController(self, ip):
207 self.hostsUpdated = 0
208 Task.Sync(lambda s: self._modifyPoolConfig(s, "vSwitchController", ip))
209 # Should be done asynchronously, maybe with an external script?
210 Task.Sync(lambda s: self._updateActiveServers(s))
212 def _modifyPoolConfig(self, session, key, value):
213 """Modify pool configuration.
215 If value == None then delete key, otherwise set key to value."""
216 pools = session.xenapi.pool.get_all()
217 # We assume there is only ever one pool...
219 log.error("No pool for host.")
220 raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", [])
222 log.error("More than one pool for host.")
223 raise XenAPIPlugin.Failure("MORE_THAN_ONE_POOL_FOR_HOST", [])
224 session.xenapi.pool.remove_from_other_config(pools[0], key)
226 session.xenapi.pool.add_to_other_config(pools[0], key, value)
229 def _updateActiveServers(self, session):
230 hosts = session.xenapi.host.get_all()
231 self.hostsUpdated = 0
232 self.hostsInPool = len(hosts)
235 Layout.Inst().TransientBanner("Updating host %d out of %d"
236 % (self.hostsUpdated + 1, self.hostsInPool))
237 session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
238 self.hostsUpdated = self.hostsUpdated + 1
240 def _updateThisServer(self, session):
242 host = data.host.opaqueref()
243 session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
246 class XSFeatureVSwitch:
249 def StatusUpdateHandler(cls, inPane):
252 inPane.AddTitleField(Lang("vSwitch"))
256 versionStr = data.host.other_config({}).get("vSwitchVersion", "<Unknown>")
257 inPane.AddStatusField(Lang("Version", 20), versionStr)
261 pool = data.GetPoolForThisHost()
263 dbController = pool.get("other_config", {}).get("vSwitchController", "")
267 if dbController == "":
268 dbController = Lang("<None>")
269 inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
270 controller = VSwitchConfig.Get("mgmt.controller")
272 controller = Lang("<None>")
273 elif controller[0:4] == "ssl:":
274 controller = controller[4:]
275 inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
278 inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
279 VSwitchService.Inst("vswitch", "ovs-vswitchd").status())
280 inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
281 VSwitchService.Inst("vswitch", "ovs-brcompatd").status())
283 inPane.AddKeyHelpField( {
284 Lang("<Enter>") : Lang("Reconfigure"),
285 Lang("<F5>") : Lang("Refresh")
289 def ActivateHandler(cls):
290 DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
293 Importer.RegisterNamedPlugIn(
295 'VSwitch', # Key of this plugin for replacement, etc.
297 'menuname' : 'MENU_NETWORK',
298 'menupriority' : 800,
299 'menutext' : Lang('vSwitch') ,
300 'statusupdatehandler' : self.StatusUpdateHandler,
301 'activatehandler' : self.ActivateHandler
305 # Register this plugin when module is imported
306 XSFeatureVSwitch().Register()