summaryrefslogtreecommitdiff
path: root/xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py
diff options
context:
space:
mode:
Diffstat (limited to 'xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py')
-rw-r--r--xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py296
1 files changed, 296 insertions, 0 deletions
diff --git a/xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py b/xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py
new file mode 100644
index 000000000..8f4be3139
--- /dev/null
+++ b/xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py
@@ -0,0 +1,296 @@
+# Copyright (c) Citrix Systems 2008. All rights reserved.
+# xsconsole is proprietary software.
+#
+# Xen, the Xen logo, XenCenter, XenMotion are trademarks or registered
+# trademarks of Citrix Systems, Inc., in the United States and other
+# countries.
+
+# Copyright (c) 2009 Nicira Networks.
+
+import logging
+log = logging.getLogger("vswitch-cfg-update")
+logging.basicConfig(filename="/var/log/vswitch-xsplugin.log", level=logging.DEBUG)
+
+import os
+import subprocess
+
+cfg_mod="/root/vswitch/bin/ovs-cfg-mod"
+vswitchd_cfg_filename="/etc/ovs-vswitchd.conf"
+
+if __name__ == "__main__":
+ raise Exception("This script is a plugin for xsconsole and cannot run independently")
+
+from XSConsoleStandard import *
+
+class VSwitchService:
+ service = {}
+
+ def __init__(self, name, processname=None):
+ self.name = name
+ self.processname = processname
+ if self.processname == None:
+ self.processname = name
+
+ def status(self):
+ try:
+ output = ShellPipe(["service", self.name, "status"]).Stdout()
+ except StandardError, e:
+ log.error("status retrieval error: " + str(e))
+ return "<unknown>"
+ if len(output) == 0:
+ return "<unknown>"
+ for l in output:
+ if self.processname not in l:
+ continue
+ elif "running" in l:
+ return "Running"
+ elif "stop" in l:
+ return "Stopped"
+ else:
+ return "<unknown>"
+ return "<unknown>"
+
+ def restart(self):
+ try:
+ ShellPipe(["service", self.name, "restart"]).Call()
+ except StandardError, e:
+ log.error("restart error: " + str(e))
+
+ @classmethod
+ def Inst(cls, name, processname=None):
+ key = name
+ if processname != None:
+ key = key + "-" + processname
+ if name not in cls.service:
+ cls.service[key] = VSwitchService(name, processname)
+ return cls.service[key]
+
+class VSwitchConfig:
+
+ @staticmethod
+ def Get(key):
+ try:
+ output = ShellPipe([cfg_mod, "-vANY:console:emer", "-F",
+ vswitchd_cfg_filename, "-q", key]).Stdout()
+ except StandardError, e:
+ log.error("config retrieval error: " + str(e))
+ return "<unknown>"
+
+ if len(output) == 0:
+ output = ""
+ else:
+ output = output[0].strip()
+ return output
+
+
+class VSwitchControllerDialogue(Dialogue):
+ def __init__(self):
+ Dialogue.__init__(self)
+ data=Data.Inst()
+
+ self.hostsInPool = 0
+ self.hostsUpdated = 0
+ self.controller = data.GetPoolForThisHost().get("other_config", {}).get("vSwitchController", "")
+
+ choiceDefs = [
+ ChoiceDef(Lang("Set pool-wide controller"),
+ lambda: self.getController()),
+ ChoiceDef(Lang("Delete pool-wide controller"),
+ lambda: self.deleteController()),
+ ChoiceDef(Lang("Resync server controller config"),
+ lambda: self.syncController()),
+# ChoiceDef(Lang("Restart ovs-vswitchd"),
+# lambda: self.restartService("vswitch")),
+# ChoiceDef(Lang("Restart ovs-brcompatd"),
+# lambda: self.restartService("vswitch-brcompatd"))
+ ]
+ self.menu = Menu(self, None, Lang("Configure vSwitch"), choiceDefs)
+
+ self.ChangeState("INITIAL")
+
+ def BuildPane(self):
+ pane = self.NewPane(DialoguePane(self.parent))
+ pane.TitleSet(Lang("Configure vSwitch"))
+ pane.AddBox()
+
+ def ChangeState(self, inState):
+ self.state = inState
+ self.BuildPane()
+ self.UpdateFields()
+
+ def UpdateFields(self):
+ self.Pane().ResetPosition()
+ getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
+
+ def UpdateFieldsINITIAL(self):
+ pane = self.Pane()
+ pane.AddTitleField(Lang("Select an action"))
+ pane.AddMenuField(self.menu)
+ pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
+
+ def UpdateFieldsGETCONTROLLER(self):
+ pane = self.Pane()
+ pane.ResetFields()
+
+ pane.AddTitleField(Lang("Enter IP address of controller"))
+ pane.AddInputField(Lang("Address", 16), self.controller, "address")
+ pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
+ if pane.CurrentInput() is None:
+ pane.InputIndexSet(0)
+
+ def HandleKey(self, inKey):
+ handled = False
+ if hasattr(self, "HandleKey" + self.state):
+ handled = getattr(self, "HandleKey" + self.state)(inKey)
+ if not handled and inKey == 'KEY_ESCAPE':
+ Layout.Inst().PopDialogue()
+ handled = True
+ return handled
+
+ def HandleKeyINITIAL(self, inKey):
+ return self.menu.HandleKey(inKey)
+
+ def HandleKeyGETCONTROLLER(self, inKey):
+ pane = self.Pane()
+ if pane.CurrentInput() is None:
+ pane.InputIndexSet(0)
+ if inKey == 'KEY_ENTER':
+ inputValues = pane.GetFieldValues()
+ self.controller = inputValues['address']
+ Layout.Inst().PopDialogue()
+ Layout.Inst().TransientBanner(Lang("Setting controller..."))
+ try:
+ self.SetController(self.controller)
+ Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
+ except Exception, e:
+ Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
+
+ self.ChangeState("INITIAL")
+ return True
+ else:
+ return pane.CurrentInput().HandleKey(inKey)
+
+ def restartService(self, name):
+ s = VSwitchService.Inst(name)
+ s.restart()
+ Layout.Inst().PopDialogue()
+
+ def getController(self):
+ self.ChangeState("GETCONTROLLER")
+ self.Pane().InputIndexSet(0)
+
+ def deleteController(self):
+ self.controller = ""
+ Layout.Inst().PopDialogue()
+ Layout.Inst().TransientBanner(Lang("Deleting controller..."))
+ try:
+ self.SetController(None)
+ Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
+ except Exception, e:
+ Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
+
+ def syncController(self):
+ Layout.Inst().PopDialogue()
+ Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
+ try:
+ Task.Sync(lambda s: self._updateThisServer(s))
+ Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
+ except Exception, e:
+ Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
+
+ def SetController(self, ip):
+ self.hostsInPool = 0
+ self.hostsUpdated = 0
+ Task.Sync(lambda s: self._modifyPoolConfig(s, "vSwitchController", ip))
+ # Should be done asynchronously, maybe with an external script?
+ Task.Sync(lambda s: self._updateActiveServers(s))
+
+ def _modifyPoolConfig(self, session, key, value):
+ """Modify pool configuration.
+
+ If value == None then delete key, otherwise set key to value."""
+ pools = session.xenapi.pool.get_all()
+ # We assume there is only ever one pool...
+ if len(pools) == 0:
+ log.error("No pool for host.")
+ raise XenAPIPlugin.Failure("NO_POOL_FOR_HOST", [])
+ if len(pools) > 1:
+ log.error("More than one pool for host.")
+ raise XenAPIPlugin.Failure("MORE_THAN_ONE_POOL_FOR_HOST", [])
+ session.xenapi.pool.remove_from_other_config(pools[0], key)
+ if value != None:
+ session.xenapi.pool.add_to_other_config(pools[0], key, value)
+ Data.Inst().Update()
+
+ def _updateActiveServers(self, session):
+ hosts = session.xenapi.host.get_all()
+ self.hostsUpdated = 0
+ self.hostsInPool = len(hosts)
+ self.UpdateFields()
+ for host in hosts:
+ Layout.Inst().TransientBanner("Updating host %d out of %d"
+ % (self.hostsUpdated + 1, self.hostsInPool))
+ session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
+ self.hostsUpdated = self.hostsUpdated + 1
+
+ def _updateThisServer(self, session):
+ data = Data.Inst()
+ host = data.host.opaqueref()
+ session.xenapi.host.call_plugin(host, "vswitch-cfg-update", "update", {})
+
+
+class XSFeatureVSwitch:
+
+ @classmethod
+ def StatusUpdateHandler(cls, inPane):
+ data = Data.Inst()
+
+ inPane.AddTitleField(Lang("vSwitch"))
+
+ inPane.NewLine()
+
+ versionStr = data.host.other_config({}).get("vSwitchVersion", "<Unknown>")
+ inPane.AddStatusField(Lang("Version", 20), versionStr)
+
+ inPane.NewLine()
+ dbController = data.GetPoolForThisHost().get("other_config", {}).get("vSwitchController", "")
+ if dbController == "":
+ dbController = Lang("<None>")
+ inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
+ controller = VSwitchConfig.Get("mgmt.controller")
+ if controller == "":
+ controller = Lang("<None>")
+ elif controller[0:4] == "ssl:":
+ controller = controller[4:]
+ inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
+
+ inPane.NewLine()
+ inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
+ VSwitchService.Inst("vswitch", "ovs-vswitchd").status())
+ inPane.AddStatusField(Lang("ovs-brcompatd status", 20),
+ VSwitchService.Inst("vswitch", "ovs-brcompatd").status())
+
+ inPane.AddKeyHelpField( {
+ Lang("<Enter>") : Lang("Reconfigure"),
+ Lang("<F5>") : Lang("Refresh")
+ })
+
+ @classmethod
+ def ActivateHandler(cls):
+ DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
+
+ def Register(self):
+ Importer.RegisterNamedPlugIn(
+ self,
+ 'VSwitch', # Key of this plugin for replacement, etc.
+ {
+ 'menuname' : 'MENU_NETWORK',
+ 'menupriority' : 800,
+ 'menutext' : Lang('vSwitch') ,
+ 'statusupdatehandler' : self.StatusUpdateHandler,
+ 'activatehandler' : self.ActivateHandler
+ }
+ )
+
+# Register this plugin when module is imported
+XSFeatureVSwitch().Register()