Import from old repository commit 61ef2b42a9c4ba8e1600f15bb0236765edc2ad45.
[cascardo/ovs.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         self.controller = data.GetPoolForThisHost().get("other_config", {}).get("vSwitchController", "")
94
95         choiceDefs = [
96             ChoiceDef(Lang("Set pool-wide controller"),
97                       lambda: self.getController()),
98             ChoiceDef(Lang("Delete pool-wide controller"),
99                       lambda: self.deleteController()),
100             ChoiceDef(Lang("Resync server controller config"),
101                       lambda: self.syncController()),
102 #             ChoiceDef(Lang("Restart ovs-vswitchd"),
103 #                       lambda: self.restartService("vswitch")),
104 #             ChoiceDef(Lang("Restart ovs-brcompatd"),
105 #                       lambda: self.restartService("vswitch-brcompatd"))
106             ]
107         self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
108
109         self.ChangeState("INITIAL")
110
111     def BuildPane(self):
112         pane = self.NewPane(DialoguePane(self.parent))
113         pane.TitleSet(Lang("Configure vSwitch"))
114         pane.AddBox()
115
116     def ChangeState(self, inState):
117         self.state = inState
118         self.BuildPane()
119         self.UpdateFields()
120
121     def UpdateFields(self):
122         self.Pane().ResetPosition()
123         getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
124
125     def UpdateFieldsINITIAL(self):
126         pane = self.Pane()
127         pane.AddTitleField(Lang("Select an action"))
128         pane.AddMenuField(self.menu)
129         pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
130
131     def UpdateFieldsGETCONTROLLER(self):
132         pane = self.Pane()
133         pane.ResetFields()
134
135         pane.AddTitleField(Lang("Enter IP address of controller"))
136         pane.AddInputField(Lang("Address", 16), self.controller, "address")
137         pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
138         if pane.CurrentInput() is None:
139             pane.InputIndexSet(0)
140
141     def HandleKey(self, inKey):
142         handled = False
143         if hasattr(self, "HandleKey" + self.state):
144             handled = getattr(self, "HandleKey" + self.state)(inKey)
145         if not handled and inKey == 'KEY_ESCAPE':
146             Layout.Inst().PopDialogue()
147             handled = True
148         return handled
149
150     def HandleKeyINITIAL(self, inKey):
151         return self.menu.HandleKey(inKey)
152
153     def HandleKeyGETCONTROLLER(self, inKey):
154         pane = self.Pane()
155         if pane.CurrentInput() is None:
156             pane.InputIndexSet(0)
157         if inKey == 'KEY_ENTER':
158             inputValues = pane.GetFieldValues()
159             self.controller = inputValues['address']
160             Layout.Inst().PopDialogue()
161             Layout.Inst().TransientBanner(Lang("Setting controller..."))
162             try:
163                 self.SetController(self.controller)
164                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
165             except Exception, e:
166                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
167
168             self.ChangeState("INITIAL")
169             return True
170         else:
171             return pane.CurrentInput().HandleKey(inKey)
172
173     def restartService(self, name):
174         s = VSwitchService.Inst(name)
175         s.restart()
176         Layout.Inst().PopDialogue()
177
178     def getController(self):
179         self.ChangeState("GETCONTROLLER")
180         self.Pane().InputIndexSet(0)
181
182     def deleteController(self):
183         self.controller = ""
184         Layout.Inst().PopDialogue()
185         Layout.Inst().TransientBanner(Lang("Deleting controller..."))
186         try:
187             self.SetController(None)
188             Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
189         except Exception, e:
190             Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
191
192     def syncController(self):
193         Layout.Inst().PopDialogue()
194         Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
195         try:
196             Task.Sync(lambda s: self._updateThisServer(s))
197             Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
198         except Exception, e:
199             Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
200
201     def SetController(self, ip):
202         self.hostsInPool = 0
203         self.hostsUpdated = 0
204         Task.Sync(lambda s: self._modifyPoolConfig(s, "vSwitchController", ip))
205         # Should be done asynchronously, maybe with an external script?
206         Task.Sync(lambda s: self._updateActiveServers(s))
207
208     def _modifyPoolConfig(self, session, key, value):
209         """Modify pool configuration.
210
211         If value == None then delete key, otherwise set key to value."""
212         pools = session.xenapi.pool.get_all()
213         # We assume there is only ever one pool...
214         if len(pools) == 0:
215             log.error("No pool for host.")
216             raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", [])
217         if len(pools) > 1:
218             log.error("More than one pool for host.")
219             raise XenAPIPlugin.Failure("MORE_THAN_ONE_POOL_FOR_HOST", [])
220         session.xenapi.pool.remove_from_other_config(pools[0], key)
221         if value != None:
222             session.xenapi.pool.add_to_other_config(pools[0], key, value)
223         Data.Inst().Update()
224
225     def _updateActiveServers(self, session):
226         hosts = session.xenapi.host.get_all()
227         self.hostsUpdated = 0
228         self.hostsInPool = len(hosts)
229         self.UpdateFields()
230         for host in hosts:
231             Layout.Inst().TransientBanner("Updating host %d out of %d" 
232                     % (self.hostsUpdated + 1, self.hostsInPool))
233             session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
234             self.hostsUpdated = self.hostsUpdated + 1
235
236     def _updateThisServer(self, session):
237         data = Data.Inst()
238         host = data.host.opaqueref()
239         session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
240
241
242 class XSFeatureVSwitch:
243
244     @classmethod
245     def StatusUpdateHandler(cls, inPane):
246         data = Data.Inst()
247
248         inPane.AddTitleField(Lang("vSwitch"))
249
250         inPane.NewLine()
251
252         versionStr = data.host.other_config({}).get("vSwitchVersion", "<Unknown>")
253         inPane.AddStatusField(Lang("Version", 20), versionStr)
254
255         inPane.NewLine()
256         dbController = data.GetPoolForThisHost().get("other_config", {}).get("vSwitchController", "")
257         if dbController == "":
258             dbController = Lang("<None>")
259         inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
260         controller = VSwitchConfig.Get("mgmt.controller")
261         if controller == "":
262             controller = Lang("<None>")
263         elif controller[0:4] == "ssl:":
264             controller = controller[4:]
265         inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
266
267         inPane.NewLine()
268         inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
269                               VSwitchService.Inst("vswitch", "ovs-vswitchd").status())
270         inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
271                               VSwitchService.Inst("vswitch", "ovs-brcompatd").status())
272
273         inPane.AddKeyHelpField( {
274             Lang("<Enter>") : Lang("Reconfigure"),
275             Lang("<F5>") : Lang("Refresh")
276         })
277
278     @classmethod
279     def ActivateHandler(cls):
280         DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
281
282     def Register(self):
283         Importer.RegisterNamedPlugIn(
284             self,
285             'VSwitch', # Key of this plugin for replacement, etc.
286             {
287                 'menuname' : 'MENU_NETWORK',
288                 'menupriority' : 800,
289                 'menutext' : Lang('vSwitch') ,
290                 'statusupdatehandler' : self.StatusUpdateHandler,
291                 'activatehandler' : self.ActivateHandler
292             }
293         )
294
295 # Register this plugin when module is imported
296 XSFeatureVSwitch().Register()