xenserver: Retrieve vSwitch version from binary in xsconsole
[sliver-openvswitch.git] / xenserver / usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py
1 # Copyright (c) Citrix Systems 2008. All rights reserved.
2 # xsconsole is proprietary software.
3 #
4 # Xen, the Xen logo, XenCenter, XenMotion are trademarks or registered
5 # trademarks of Citrix Systems, Inc., in the United States and other
6 # countries.
7
8 # Copyright (c) 2009 Nicira Networks.
9
10 import logging
11 log = logging.getLogger("vswitch-cfg-update")
12 logging.basicConfig(filename="/var/log/vswitch-xsplugin.log", level=logging.DEBUG)
13
14 import os
15 import socket
16 import subprocess
17
18 cfg_mod="/root/vswitch/bin/ovs-cfg-mod"
19 vswitchd_cfg_filename="/etc/ovs-vswitchd.conf"
20
21 if __name__ == "__main__":
22     raise Exception("This script is a plugin for xsconsole and cannot run independently")
23
24 from XSConsoleStandard import *
25
26 class VSwitchService:
27     service = {}
28
29     def __init__(self, name, processname=None):
30         self.name = name
31         self.processname = processname
32         if self.processname == None:
33             self.processname = name
34
35     def version(self):
36         try:
37             output = ShellPipe(["service", self.name, "version"]).Stdout()
38         except StandardError, e:
39             log.error("version retrieval error: " + str(e))
40             return "<unknown>"
41         for line in output:
42             if self.processname in line:
43                 return line.split()[-1]
44         return "<unknown>"
45
46     def status(self):
47         try:
48             output = ShellPipe(["service", self.name, "status"]).Stdout()
49         except StandardError, e:
50             log.error("status retrieval error: " + str(e))
51             return "<unknown>"
52         if len(output) == 0:
53             return "<unknown>"
54         for line in output:
55             if self.processname not in line:
56                 continue
57             elif "running" in line:
58                 return "Running"
59             elif "stop" in line:
60                 return "Stopped"
61             else:
62                 return "<unknown>"
63         return "<unknown>"
64
65     def restart(self):
66         try:
67             ShellPipe(["service", self.name, "restart"]).Call()
68         except StandardError, e:
69             log.error("restart error: " + str(e))
70
71     @classmethod
72     def Inst(cls, name, processname=None):
73         key = name
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]
79
80 class VSwitchConfig:
81
82     @staticmethod
83     def Get(key):
84         try:
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))
89             return "<unknown>"
90
91         if len(output) == 0:
92             output = ""
93         else:
94             output = output[0].strip()
95         return output
96
97
98 class VSwitchControllerDialogue(Dialogue):
99     def __init__(self):
100         Dialogue.__init__(self)
101         data=Data.Inst()
102
103         self.hostsInPool = 0
104         self.hostsUpdated = 0
105         pool = data.GetPoolForThisHost()
106         if pool is not None:
107             self.controller = pool.get("other_config", {}).get("vSwitchController", "")
108         else:
109             self.controller = ""
110
111         choiceDefs = [
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"))
122             ]
123         self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
124
125         self.ChangeState("INITIAL")
126
127     def BuildPane(self):
128         pane = self.NewPane(DialoguePane(self.parent))
129         pane.TitleSet(Lang("Configure vSwitch"))
130         pane.AddBox()
131
132     def ChangeState(self, inState):
133         self.state = inState
134         self.BuildPane()
135         self.UpdateFields()
136
137     def UpdateFields(self):
138         self.Pane().ResetPosition()
139         getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
140
141     def UpdateFieldsINITIAL(self):
142         pane = self.Pane()
143         pane.AddTitleField(Lang("Select an action"))
144         pane.AddMenuField(self.menu)
145         pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
146
147     def UpdateFieldsGETCONTROLLER(self):
148         pane = self.Pane()
149         pane.ResetFields()
150
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)
156
157     def HandleKey(self, inKey):
158         handled = False
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()
163             handled = True
164         return handled
165
166     def HandleKeyINITIAL(self, inKey):
167         return self.menu.HandleKey(inKey)
168
169     def HandleKeyGETCONTROLLER(self, inKey):
170         pane = self.Pane()
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()
177
178             # Make sure the controller is specified as a valid dotted quad
179             try:
180                 socket.inet_aton(self.controller)
181             except socket.error:
182                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format")))
183                 return True
184
185             Layout.Inst().TransientBanner(Lang("Setting controller..."))
186             try:
187                 self.SetController(self.controller)
188                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
189             except Exception, e:
190                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
191
192             self.ChangeState("INITIAL")
193             return True
194         else:
195             return pane.CurrentInput().HandleKey(inKey)
196
197     def restartService(self, name):
198         s = VSwitchService.Inst(name)
199         s.restart()
200         Layout.Inst().PopDialogue()
201
202     def getController(self):
203         self.ChangeState("GETCONTROLLER")
204         self.Pane().InputIndexSet(0)
205
206     def deleteController(self):
207         self.controller = ""
208         Layout.Inst().PopDialogue()
209         Layout.Inst().TransientBanner(Lang("Deleting controller..."))
210         try:
211             self.SetController(None)
212             Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
213         except Exception, e:
214             Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
215
216     def syncController(self):
217         Layout.Inst().PopDialogue()
218         Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
219         try:
220             Task.Sync(lambda s: self._updateThisServer(s))
221             Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
222         except Exception, e:
223             Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
224
225     def SetController(self, ip):
226         self.hostsInPool = 0
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))
231
232     def _modifyPoolConfig(self, session, key, value):
233         """Modify pool configuration.
234
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...
238         if len(pools) == 0:
239             log.error("No pool for host.")
240             raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", [])
241         if len(pools) > 1:
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)
245         if value != None:
246             session.xenapi.pool.add_to_other_config(pools[0], key, value)
247         Data.Inst().Update()
248
249     def _updateActiveServers(self, session):
250         hosts = session.xenapi.host.get_all()
251         self.hostsUpdated = 0
252         self.hostsInPool = len(hosts)
253         self.UpdateFields()
254         for host in 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
259
260     def _updateThisServer(self, session):
261         data = Data.Inst()
262         host = data.host.opaqueref()
263         session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
264
265
266 class XSFeatureVSwitch:
267
268     @classmethod
269     def StatusUpdateHandler(cls, inPane):
270         data = Data.Inst()
271
272         inPane.AddTitleField(Lang("vSwitch"))
273
274         inPane.NewLine()
275
276         inPane.AddStatusField(Lang("Version", 20),
277                               VSwitchService.Inst("vswitch", "ovs-vswitchd").version())
278
279         inPane.NewLine()
280
281         pool = data.GetPoolForThisHost()
282         if pool is not None:
283             dbController = pool.get("other_config", {}).get("vSwitchController", "")
284         else:
285             dbController = ""
286
287         if dbController == "":
288             dbController = Lang("<None>")
289         inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
290         controller = VSwitchConfig.Get("mgmt.controller")
291         if controller == "":
292             controller = Lang("<None>")
293         elif controller[0:4] == "ssl:":
294             controller = controller[4:]
295         inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
296
297         inPane.NewLine()
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())
302
303         inPane.AddKeyHelpField( {
304             Lang("<Enter>") : Lang("Reconfigure"),
305             Lang("<F5>") : Lang("Refresh")
306         })
307
308     @classmethod
309     def ActivateHandler(cls):
310         DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
311
312     def Register(self):
313         Importer.RegisterNamedPlugIn(
314             self,
315             'VSwitch', # Key of this plugin for replacement, etc.
316             {
317                 'menuname' : 'MENU_NETWORK',
318                 'menupriority' : 800,
319                 'menutext' : Lang('vSwitch') ,
320                 'statusupdatehandler' : self.StatusUpdateHandler,
321                 'activatehandler' : self.ActivateHandler
322             }
323         )
324
325 # Register this plugin when module is imported
326 XSFeatureVSwitch().Register()