Merge master branch into xs5.7.
[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 from XSConsoleLog import *
11
12 import os
13 import socket
14 import subprocess
15
16 cfg_mod="/usr/bin/ovs-cfg-mod"
17 vswitchd_cfg_filename="/etc/ovs-vswitchd.conf"
18
19 if __name__ == "__main__":
20     raise Exception("This script is a plugin for xsconsole and cannot run independently")
21
22 from XSConsoleStandard import *
23
24 class VSwitchService:
25     service = {}
26
27     def __init__(self, name, processname=None):
28         self.name = name
29         self.processname = processname
30         if self.processname == None:
31             self.processname = name
32
33     def version(self):
34         try:
35             output = ShellPipe(["service", self.name, "version"]).Stdout()
36         except StandardError, e:
37             XSLogError("vswitch version retrieval error: " + str(e))
38             return "<unknown>"
39         for line in output:
40             if self.processname in line:
41                 return line.split()[-1]
42         return "<unknown>"
43
44     def status(self):
45         try:
46             output = ShellPipe(["service", self.name, "status"]).Stdout()
47         except StandardError, e:
48             XSLogError("vswitch status retrieval error: " + str(e))
49             return "<unknown>"
50         if len(output) == 0:
51             return "<unknown>"
52         for line in output:
53             if self.processname not in line:
54                 continue
55             elif "running" in line:
56                 return "Running"
57             elif "stop" in line:
58                 return "Stopped"
59             else:
60                 return "<unknown>"
61         return "<unknown>"
62
63     def restart(self):
64         try:
65             ShellPipe(["service", self.name, "restart"]).Call()
66         except StandardError, e:
67             XSLogError("vswitch restart error: " + str(e))
68
69     @classmethod
70     def Inst(cls, name, processname=None):
71         key = name
72         if processname != None:
73             key = key + "-" + processname
74         if name not in cls.service:
75             cls.service[key] = VSwitchService(name, processname)
76         return cls.service[key]
77
78 class VSwitchConfig:
79
80     @staticmethod
81     def Get(key):
82         try:
83             output = ShellPipe([cfg_mod, "-vANY:console:emer", "-F", 
84                     vswitchd_cfg_filename, "-q", key]).Stdout()
85         except StandardError, e:
86             XSLogError("config retrieval error: " + str(e))
87             return "<unknown>"
88
89         if len(output) == 0:
90             output = ""
91         else:
92             output = output[0].strip()
93         return output
94
95
96 class VSwitchControllerDialogue(Dialogue):
97     def __init__(self):
98         Dialogue.__init__(self)
99         data=Data.Inst()
100
101         self.hostsInPool = 0
102         self.hostsUpdated = 0
103         pool = data.GetPoolForThisHost()
104         if pool is not None:
105             self.controller = pool.get("vswitch_controller", "")
106         else:
107             self.controller = ""
108
109         choiceDefs = [
110             ChoiceDef(Lang("Set pool-wide controller"),
111                       lambda: self.getController()),
112             ChoiceDef(Lang("Delete pool-wide controller"),
113                       lambda: self.deleteController()),
114 #             ChoiceDef(Lang("Restart ovs-vswitchd"),
115 #                       lambda: self.restartService("vswitch")),
116 #             ChoiceDef(Lang("Restart ovs-brcompatd"),
117 #                       lambda: self.restartService("vswitch-brcompatd"))
118             ]
119         self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
120
121         self.ChangeState("INITIAL")
122
123     def BuildPane(self):
124         pane = self.NewPane(DialoguePane(self.parent))
125         pane.TitleSet(Lang("Configure vSwitch"))
126         pane.AddBox()
127
128     def ChangeState(self, inState):
129         self.state = inState
130         self.BuildPane()
131         self.UpdateFields()
132
133     def UpdateFields(self):
134         self.Pane().ResetPosition()
135         getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
136
137     def UpdateFieldsINITIAL(self):
138         pane = self.Pane()
139         pane.AddTitleField(Lang("Select an action"))
140         pane.AddMenuField(self.menu)
141         pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
142
143     def UpdateFieldsGETCONTROLLER(self):
144         pane = self.Pane()
145         pane.ResetFields()
146
147         pane.AddTitleField(Lang("Enter IP address of controller"))
148         pane.AddInputField(Lang("Address", 16), self.controller, "address")
149         pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
150         if pane.CurrentInput() is None:
151             pane.InputIndexSet(0)
152
153     def HandleKey(self, inKey):
154         handled = False
155         if hasattr(self, "HandleKey" + self.state):
156             handled = getattr(self, "HandleKey" + self.state)(inKey)
157         if not handled and inKey == 'KEY_ESCAPE':
158             Layout.Inst().PopDialogue()
159             handled = True
160         return handled
161
162     def HandleKeyINITIAL(self, inKey):
163         return self.menu.HandleKey(inKey)
164
165     def HandleKeyGETCONTROLLER(self, inKey):
166         pane = self.Pane()
167         if pane.CurrentInput() is None:
168             pane.InputIndexSet(0)
169         if inKey == 'KEY_ENTER':
170             inputValues = pane.GetFieldValues()
171             self.controller = inputValues['address']
172             Layout.Inst().PopDialogue()
173
174             # Make sure the controller is specified as a valid dotted quad
175             try:
176                 socket.inet_aton(self.controller)
177             except socket.error:
178                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format")))
179                 return True
180
181             Layout.Inst().TransientBanner(Lang("Setting controller..."))
182             try:
183                 self.SetController(self.controller)
184                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
185             except Exception, e:
186                 Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
187
188             self.ChangeState("INITIAL")
189             return True
190         else:
191             return pane.CurrentInput().HandleKey(inKey)
192
193     def restartService(self, name):
194         s = VSwitchService.Inst(name)
195         s.restart()
196         Layout.Inst().PopDialogue()
197
198     def getController(self):
199         self.ChangeState("GETCONTROLLER")
200         self.Pane().InputIndexSet(0)
201
202     def deleteController(self):
203         self.controller = ""
204         Layout.Inst().PopDialogue()
205         Layout.Inst().TransientBanner(Lang("Deleting controller..."))
206         try:
207             self.SetController(None)
208             Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
209         except Exception, e:
210             Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
211
212     def SetController(self, ip):
213         self.hostsInPool = 0
214         self.hostsUpdated = 0
215         Task.Sync(lambda s: self._modifyPoolConfig(s, ip or ""))
216
217     def _modifyPoolConfig(self, session, value):
218         """Modify pool configuration.
219
220         If value == None then delete configuration, otherwise set to value."""
221         pools = session.xenapi.pool.get_all()
222         # We assume there is only ever one pool...
223         if len(pools) == 0:
224             XSLogFatal(Lang("No pool found for host."))
225             return
226         if len(pools) > 1:
227             XSLogFatal(Lang("More than one pool for host."))
228             return
229         session.xenapi.pool.set_vswitch_controller(value)
230         Data.Inst().Update()
231
232 class XSFeatureVSwitch:
233
234     @classmethod
235     def StatusUpdateHandler(cls, inPane):
236         data = Data.Inst()
237
238         inPane.AddTitleField(Lang("vSwitch"))
239
240         inPane.NewLine()
241
242         inPane.AddStatusField(Lang("Version", 20),
243                               VSwitchService.Inst("vswitch", "ovs-vswitchd").version())
244
245         inPane.NewLine()
246
247         pool = data.GetPoolForThisHost()
248         if pool is not None:
249             dbController = pool.get("vswitch_controller", "")
250         else:
251             dbController = ""
252
253         if dbController == "":
254             dbController = Lang("<None>")
255         inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
256         controller = VSwitchConfig.Get("mgmt.controller")
257         if controller == "":
258             controller = Lang("<None>")
259         elif controller[0:4] == "ssl:":
260             controller = controller[4:]
261         inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
262
263         inPane.NewLine()
264         inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
265                               VSwitchService.Inst("vswitch", "ovs-vswitchd").status())
266         #inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
267         #                      VSwitchService.Inst("vswitch", "ovs-brcompatd").status())
268
269         inPane.AddKeyHelpField( {
270             Lang("<Enter>") : Lang("Reconfigure"),
271             Lang("<F5>") : Lang("Refresh")
272         })
273
274     @classmethod
275     def ActivateHandler(cls):
276         DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
277
278     def Register(self):
279         Importer.RegisterNamedPlugIn(
280             self,
281             'VSwitch', # Key of this plugin for replacement, etc.
282             {
283                 'menuname' : 'MENU_NETWORK',
284                 'menupriority' : 800,
285                 'menutext' : Lang('vSwitch') ,
286                 'statusupdatehandler' : self.StatusUpdateHandler,
287                 'activatehandler' : self.ActivateHandler
288             }
289         )
290
291 # Register this plugin when module is imported, IFF vswitchd is running
292 if os.path.exists('/var/run/ovs-vswitchd.pid'):
293     XSFeatureVSwitch().Register()