95020ad49983718668b64d74dfdd5915fcf843e1
[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 subprocess
16
17 cfg_mod="/root/vswitch/bin/ovs-cfg-mod"
18 vswitchd_cfg_filename="/etc/ovs-vswitchd.conf"
19
20 if __name__ == "__main__":
21     raise Exception("This script is a plugin for xsconsole and cannot run independently")
22
23 from XSConsoleStandard import *
24
25 class VSwitchService:
26     service = {}
27
28     def __init__(self, name, processname=None):
29         self.name = name
30         self.processname = processname
31         if self.processname == None:
32             self.processname = name
33
34     def status(self):
35         try:
36             output = ShellPipe(["service", self.name, "status"]).Stdout()
37         except StandardError, e:
38             log.error("status retrieval error: " + str(e))
39             return "<unknown>"
40         if len(output) == 0:
41             return "<unknown>"
42         for l in output:
43             if self.processname not in l:
44                 continue
45             elif "running" in l:
46                 return "Running"
47             elif "stop" in l:
48                 return "Stopped"
49             else:
50                 return "<unknown>"
51         return "<unknown>"
52
53     def restart(self):
54         try:
55             ShellPipe(["service", self.name, "restart"]).Call()
56         except StandardError, e:
57             log.error("restart error: " + str(e))
58
59     @classmethod
60     def Inst(cls, name, processname=None):
61         key = name
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]
67
68 class VSwitchConfig:
69
70     @staticmethod
71     def Get(key):
72         try:
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))
77             return "<unknown>"
78
79         if len(output) == 0:
80             output = ""
81         else:
82             output = output[0].strip()
83         return output
84
85
86 class VSwitchControllerDialogue(Dialogue):
87     def __init__(self):
88         Dialogue.__init__(self)
89         data=Data.Inst()
90
91         self.hostsInPool = 0
92         self.hostsUpdated = 0
93         pool = data.GetPoolForThisHost()
94         if pool is not None:
95             self.controller = pool.get("other_config", {}).get("vSwitchController", "")
96         else:
97             self.controller = ""
98
99         choiceDefs = [
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"))
110             ]
111         self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
112
113         self.ChangeState("INITIAL")
114
115     def BuildPane(self):
116         pane = self.NewPane(DialoguePane(self.parent))
117         pane.TitleSet(Lang("Configure vSwitch"))
118         pane.AddBox()
119
120     def ChangeState(self, inState):
121         self.state = inState
122         self.BuildPane()
123         self.UpdateFields()
124
125     def UpdateFields(self):
126         self.Pane().ResetPosition()
127         getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
128
129     def UpdateFieldsINITIAL(self):
130         pane = self.Pane()
131         pane.AddTitleField(Lang("Select an action"))
132         pane.AddMenuField(self.menu)
133         pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
134
135     def UpdateFieldsGETCONTROLLER(self):
136         pane = self.Pane()
137         pane.ResetFields()
138
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)
144
145     def HandleKey(self, inKey):
146         handled = False
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()
151             handled = True
152         return handled
153
154     def HandleKeyINITIAL(self, inKey):
155         return self.menu.HandleKey(inKey)
156
157     def HandleKeyGETCONTROLLER(self, inKey):
158         pane = self.Pane()
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..."))
166             try:
167                 self.SetController(self.controller)
168                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
169             except Exception, e:
170                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
171
172             self.ChangeState("INITIAL")
173             return True
174         else:
175             return pane.CurrentInput().HandleKey(inKey)
176
177     def restartService(self, name):
178         s = VSwitchService.Inst(name)
179         s.restart()
180         Layout.Inst().PopDialogue()
181
182     def getController(self):
183         self.ChangeState("GETCONTROLLER")
184         self.Pane().InputIndexSet(0)
185
186     def deleteController(self):
187         self.controller = ""
188         Layout.Inst().PopDialogue()
189         Layout.Inst().TransientBanner(Lang("Deleting controller..."))
190         try:
191             self.SetController(None)
192             Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
193         except Exception, e:
194             Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
195
196     def syncController(self):
197         Layout.Inst().PopDialogue()
198         Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
199         try:
200             Task.Sync(lambda s: self._updateThisServer(s))
201             Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
202         except Exception, e:
203             Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
204
205     def SetController(self, ip):
206         self.hostsInPool = 0
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))
211
212     def _modifyPoolConfig(self, session, key, value):
213         """Modify pool configuration.
214
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...
218         if len(pools) == 0:
219             log.error("No pool for host.")
220             raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", [])
221         if len(pools) > 1:
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)
225         if value != None:
226             session.xenapi.pool.add_to_other_config(pools[0], key, value)
227         Data.Inst().Update()
228
229     def _updateActiveServers(self, session):
230         hosts = session.xenapi.host.get_all()
231         self.hostsUpdated = 0
232         self.hostsInPool = len(hosts)
233         self.UpdateFields()
234         for host in 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
239
240     def _updateThisServer(self, session):
241         data = Data.Inst()
242         host = data.host.opaqueref()
243         session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
244
245
246 class XSFeatureVSwitch:
247
248     @classmethod
249     def StatusUpdateHandler(cls, inPane):
250         data = Data.Inst()
251
252         inPane.AddTitleField(Lang("vSwitch"))
253
254         inPane.NewLine()
255
256         versionStr = data.host.other_config({}).get("vSwitchVersion", "<Unknown>")
257         inPane.AddStatusField(Lang("Version", 20), versionStr)
258
259         inPane.NewLine()
260
261         pool = data.GetPoolForThisHost()
262         if pool is not None:
263             dbController = pool.get("other_config", {}).get("vSwitchController", "")
264         else:
265             dbController = ""
266
267         if dbController == "":
268             dbController = Lang("<None>")
269         inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
270         controller = VSwitchConfig.Get("mgmt.controller")
271         if controller == "":
272             controller = Lang("<None>")
273         elif controller[0:4] == "ssl:":
274             controller = controller[4:]
275         inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
276
277         inPane.NewLine()
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())
282
283         inPane.AddKeyHelpField( {
284             Lang("<Enter>") : Lang("Reconfigure"),
285             Lang("<F5>") : Lang("Refresh")
286         })
287
288     @classmethod
289     def ActivateHandler(cls):
290         DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
291
292     def Register(self):
293         Importer.RegisterNamedPlugIn(
294             self,
295             'VSwitch', # Key of this plugin for replacement, etc.
296             {
297                 'menuname' : 'MENU_NETWORK',
298                 'menupriority' : 800,
299                 'menutext' : Lang('vSwitch') ,
300                 'statusupdatehandler' : self.StatusUpdateHandler,
301                 'activatehandler' : self.ActivateHandler
302             }
303         )
304
305 # Register this plugin when module is imported
306 XSFeatureVSwitch().Register()