Allow xend configuration via Solaris SMF as well as the config file.
authorEwan Mellor <ewan@xensource.com>
Thu, 18 Jan 2007 15:07:51 +0000 (15:07 +0000)
committerEwan Mellor <ewan@xensource.com>
Thu, 18 Jan 2007 15:07:51 +0000 (15:07 +0000)
Signed-off-by: John Levon <john.levon@sun.com>
tools/python/setup.py
tools/python/xen/lowlevel/scf/scf.c [new file with mode: 0644]
tools/python/xen/xend/XendOptions.py

index 56dd3e4a0b61d9aac18ed101b1f4ce1f99bbf5f1..7b25025d9d303e1579b5eb907b2c8ca5d3b16854 100644 (file)
@@ -30,6 +30,13 @@ xs = Extension("xs",
                libraries          = libraries,
                sources            = [ "xen/lowlevel/xs/xs.c" ])
 
+scf = Extension("scf",
+               extra_compile_args = extra_compile_args,
+               include_dirs       = include_dirs + [ "xen/lowlevel/scf" ],
+               library_dirs       = library_dirs,
+               libraries          = libraries,
+               sources            = [ "xen/lowlevel/scf/scf.c" ])
+             
 acm = Extension("acm",
                extra_compile_args = extra_compile_args,
                include_dirs       = include_dirs + [ "xen/lowlevel/acm" ],
@@ -37,6 +44,10 @@ acm = Extension("acm",
                libraries          = libraries,
                sources            = [ "xen/lowlevel/acm/acm.c" ])
 
+modules = [ xc, xs, acm ]
+if os.uname()[0] == 'SunOS':
+    modules.append(scf)
+
 setup(name            = 'xen',
       version         = '3.0',
       description     = 'Xen',
@@ -56,7 +67,7 @@ setup(name            = 'xen',
                          'xen.xm.tests'
                          ],
       ext_package = "xen.lowlevel",
-      ext_modules = [ xc, xs, acm ]
+      ext_modules = modules
       )
 
 os.chdir('logging')
diff --git a/tools/python/xen/lowlevel/scf/scf.c b/tools/python/xen/lowlevel/scf/scf.c
new file mode 100644 (file)
index 0000000..a73ac20
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <Python.h>
+
+#include <libscf.h>
+#include <stdio.h>
+
+#define        XEND_FMRI "svc:/system/xen/xend:default"
+#define        XEND_PG "config"
+
+static PyObject *scf_exc;
+
+static void *
+scf_exception(const char *err, const char *value)
+{
+       int scferr = scf_error();
+       const char *scfstrerr = scf_strerror(scferr);
+       PyObject *obj = Py_BuildValue("(isss)", scferr, err, scfstrerr, value);
+       PyErr_SetObject(scf_exc, obj);
+       return (NULL);
+}
+
+static PyObject *
+pyscf_get_bool(PyObject *o, PyObject *args, PyObject *kwargs)
+{
+       static char *kwlist[] = { "name", NULL };
+       scf_simple_prop_t *prop;
+       uint8_t *val;
+       char *name;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
+               return (NULL);
+
+       prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name);
+
+       if (prop == NULL)
+               return (scf_exception("scf_simple_prop_get() failed", name));
+
+       if ((val = scf_simple_prop_next_boolean(prop)) == NULL)
+               return (scf_exception("scf_simple_prop_next_boolean() failed",
+                   name));
+
+       if (*val) {
+               scf_simple_prop_free(prop);
+               Py_INCREF(Py_True);
+               return (Py_True);
+       }
+
+       scf_simple_prop_free(prop);
+       Py_INCREF(Py_False);
+       return (Py_False);
+}
+
+static PyObject *
+pyscf_get_int(PyObject *o, PyObject *args, PyObject *kwargs)
+{
+       static char *kwlist[] = { "name", NULL };
+       scf_simple_prop_t *prop;
+       PyObject *obj;
+       int64_t *val;
+       char *name;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
+               return (NULL);
+
+       prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name);
+
+       if (prop == NULL)
+               return (scf_exception("scf_simple_prop_get() failed", name));
+
+       if ((val = scf_simple_prop_next_integer(prop)) == NULL)
+               return (scf_exception("scf_simple_prop_next_integer() failed",
+                   name));
+
+       obj = PyInt_FromLong((long)*val);
+       scf_simple_prop_free(prop);
+       return (obj);
+}
+
+static PyObject *
+pyscf_get_string(PyObject *o, PyObject *args, PyObject *kwargs)
+{
+       static char *kwlist[] = { "name", NULL };
+       scf_simple_prop_t *prop;
+       PyObject *obj;
+       char *name;
+       char *str;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
+               return (NULL);
+
+       prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name);
+
+       if (prop == NULL)
+               return (scf_exception("scf_simple_prop_get() failed", name));
+
+       if ((str = scf_simple_prop_next_astring(prop)) == NULL) {
+               scf_simple_prop_free(prop);
+               return (scf_exception("scf_simple_prop_next_astring() failed",
+                   name));
+       }
+
+       obj = PyString_FromString(str);
+       scf_simple_prop_free(prop);
+       return (obj);
+}
+
+PyDoc_STRVAR(pyscf_get_bool__doc__,
+   "get_bool(name) - get the value of the named boolean property");
+PyDoc_STRVAR(pyscf_get_int__doc__,
+   "get_int(name) - get the value of the named integer property");
+PyDoc_STRVAR(pyscf_get_string__doc__,
+   "get_string(name) - get the value of the named string property");
+
+static struct PyMethodDef pyscf_module_methods[] = {
+       { "get_bool", (PyCFunction) pyscf_get_bool,
+         METH_VARARGS|METH_KEYWORDS, pyscf_get_bool__doc__ },
+       { "get_int", (PyCFunction) pyscf_get_int,
+         METH_VARARGS|METH_KEYWORDS, pyscf_get_int__doc__ },
+       { "get_string", (PyCFunction) pyscf_get_string,
+         METH_VARARGS|METH_KEYWORDS, pyscf_get_string__doc__ },
+       { NULL, NULL, 0, NULL } 
+};
+
+PyMODINIT_FUNC
+initscf(void)
+{
+       PyObject *m;
+       m = Py_InitModule("scf", pyscf_module_methods);
+
+       scf_exc = PyErr_NewException("scf.error", NULL, NULL);
+       Py_INCREF(scf_exc);
+       PyModule_AddObject(m, "error", scf_exc);
+       PyModule_AddIntConstant(m, "SCF_ERROR_NOT_FOUND", SCF_ERROR_NOT_FOUND);
+}
index 640c96f5f8a1ed3ad2c2a573aaabecf977435a83..3a97d09564a63bb0d4a2549e9348923c8528ee04 100644 (file)
@@ -20,7 +20,7 @@
 Creates the servers and handles configuration.
 
 Other classes get config variables by importing this module,
-using instance() to get a XendRoot instance, and then
+using instance() to get a XendOptions instance, and then
 the config functions (e.g. get_xend_port()) to get
 configured values.
 """
@@ -33,14 +33,11 @@ import sys
 from xen.xend import sxp, osdep, XendLogging
 from xen.xend.XendError import XendError
 
-class XendRoot:
-    """Root of the management classes."""
+if os.uname()[0] == 'SunOS':
+    from xen.lowlevel import scf
 
-    """Default path to the config file."""
-    config_default = "/etc/xen/xend-config.sxp"
-
-    """Environment variable used to override config_default."""
-    config_var     = "XEND_CONFIG"
+class XendOptions:
+    """Configuration options."""
 
     """Where network control scripts live."""
     network_script_dir = osdep.scripts_dir
@@ -75,10 +72,10 @@ class XendRoot:
     xend_relocation_address_default = ''
 
     """Default port xend serves HTTP at. """
-    xend_port_default         = '8000'
+    xend_port_default         = 8000
 
     """Default port xend serves relocation at. """
-    xend_relocation_port_default = '8002'
+    xend_relocation_port_default = 8002
 
     xend_relocation_hosts_allow_default = ''
 
@@ -92,9 +89,9 @@ class XendRoot:
     """Default path the unix-domain server listens at."""
     xend_unix_path_default = '/var/lib/xend/xend-socket'
 
-    dom0_min_mem_default = '0'
+    dom0_min_mem_default = 0
 
-    dom0_vcpus_default = '0'
+    dom0_vcpus_default = 0
 
     vncpasswd_default = None
 
@@ -107,16 +104,11 @@ class XendRoot:
     """Default xend management state storage."""
     xend_state_path_default = '/var/lib/xend/state'
 
-    components = {}
-
     def __init__(self):
-        self.config_path = None
-        self.config = None
         self.configure()
 
-
     def _logError(self, fmt, *args):
-        """Logging function to log to stderr. We use this for XendRoot log
+        """Logging function to log to stderr. We use this for XendOptions log
         messages because they may be logged before the logger has been
         configured.  Other components can safely use the logger.
         """
@@ -125,81 +117,25 @@ class XendRoot:
 
     def configure(self):
         self.set_config()
-        XendLogging.init(self.get_config_value("logfile",
+        XendLogging.init(self.get_config_string("logfile",
                                                self.logfile_default),
-                         self.get_config_value("loglevel",
+                         self.get_config_string("loglevel",
                                                self.loglevel_default))
 
-
     def set_config(self):
-        """If the config file exists, read it. If not, ignore it.
-
-        The config file is a sequence of sxp forms.
-        """
-        self.config_path = os.getenv(self.config_var, self.config_default)
-        if os.path.exists(self.config_path):
-            try:
-                fin = file(self.config_path, 'rb')
-                try:
-                    config = sxp.parse(fin)
-                finally:
-                    fin.close()
-                if config is None:
-                    config = ['xend-config']
-                else:
-                    config.insert(0, 'xend-config')
-                self.config = config
-            except Exception, ex:
-                self._logError('Reading config file %s: %s',
-                               self.config_path, str(ex))
-                raise
-        else:
-            self._logError('Config file does not exist: %s',
-                           self.config_path)
-            self.config = ['xend-config']
-
-    def get_config(self, name=None):
-        """Get the configuration element with the given name, or
-        the whole configuration if no name is given.
-
-        @param name: element name (optional)
-        @return: config or none
-        """
-        if name is None:
-            val = self.config
-        else:
-            val = sxp.child(self.config, name)
-        return val
-
-    def get_config_value(self, name, val=None):
-        """Get the value of an atomic configuration element.
-
-        @param name: element name
-        @param val:  default value (optional, defaults to None)
-        @return: value
-        """
-        return sxp.child_value(self.config, name, val=val)
+        raise NotImplementedError()
 
     def get_config_bool(self, name, val=None):
-        v = string.lower(str(self.get_config_value(name, val)))
-        if v in ['yes', 'y', '1', 'on',  'true',  't']:
-            return True
-        if v in ['no',  'n', '0', 'off', 'false', 'f']:
-            return False
-        raise XendError("invalid xend config %s: expected bool: %s" % (name, v))
-
+        raise NotImplementedError()
+         
     def get_config_int(self, name, val=None):
-        v = self.get_config_value(name, val)
-        try:
-            return int(v)
-        except Exception:
-            raise XendError("invalid xend config %s: expected int: %s" % (name, v))
+        raise NotImplementedError()
+
+    def get_config_string(self, name, val=None):
+        raise NotImplementedError()
 
     def get_xen_api_server(self):
-        """Get the Xen-API server configuration.
-        """
-        return self.get_config_value('xen-api-server',
-                                     self.xen_api_server_default)
+        raise NotImplementedError()
 
     def get_xend_http_server(self):
         """Get the flag indicating whether xend should run an http server.
@@ -232,7 +168,7 @@ class XendRoot:
                                    self.xend_relocation_port_default)
 
     def get_xend_relocation_hosts_allow(self):
-        return self.get_config_value("xend-relocation-hosts-allow",
+        return self.get_config_string("xend-relocation-hosts-allow",
                                      self.xend_relocation_hosts_allow_default)
 
     def get_xend_address(self):
@@ -241,7 +177,7 @@ class XendRoot:
         If this is set to 'localhost' only the localhost will be able to connect
         to the HTTP port.
         """
-        return self.get_config_value('xend-address', self.xend_address_default)
+        return self.get_config_string('xend-address', self.xend_address_default)
 
     def get_xend_relocation_address(self):
         """Get the address xend listens at for its relocation server port.
@@ -249,7 +185,7 @@ class XendRoot:
         If this is set to 'localhost' only the localhost will be able to connect
         to the relocation port.
         """
-        return self.get_config_value('xend-relocation-address', self.xend_relocation_address_default)
+        return self.get_config_string('xend-relocation-address', self.xend_relocation_address_default)
 
     def get_xend_unix_server(self):
         """Get the flag indicating whether xend should run a unix-domain server.
@@ -259,23 +195,23 @@ class XendRoot:
     def get_xend_unix_path(self):
         """Get the path the xend unix-domain server listens at.
         """
-        return self.get_config_value("xend-unix-path", self.xend_unix_path_default)
+        return self.get_config_string("xend-unix-path", self.xend_unix_path_default)
 
     def get_xend_domains_path(self):
         """ Get the path for persistent domain configuration storage
         """
-        return self.get_config_value("xend-domains-path", self.xend_domains_path_default)
+        return self.get_config_string("xend-domains-path", self.xend_domains_path_default)
 
     def get_xend_state_path(self):
         """ Get the path for persistent domain configuration storage
         """
-        return self.get_config_value("xend-state-path", self.xend_state_path_default)    
+        return self.get_config_string("xend-state-path", self.xend_state_path_default)    
 
     def get_network_script(self):
         """@return the script used to alter the network configuration when
         Xend starts and stops, or None if no such script is specified."""
         
-        s = self.get_config_value('network-script')
+        s = self.get_config_string('network-script')
 
         if s:
             result = s.split(" ")
@@ -286,13 +222,13 @@ class XendRoot:
 
     def get_external_migration_tool(self):
         """@return the name of the tool to handle virtual TPM migration."""
-        return self.get_config_value('external-migration-tool', self.external_migration_tool_default)
+        return self.get_config_string('external-migration-tool', self.external_migration_tool_default)
 
     def get_enable_dump(self):
         return self.get_config_bool('enable-dump', 'no')
 
     def get_vif_script(self):
-        return self.get_config_value('vif-script', 'vif-bridge')
+        return self.get_config_string('vif-script', 'vif-bridge')
 
     def get_dom0_min_mem(self):
         return self.get_config_int('dom0-min-mem', self.dom0_min_mem_default)
@@ -304,19 +240,128 @@ class XendRoot:
         return self.get_config_int('console-limit', 1024)
 
     def get_vnclisten_address(self):
-        return self.get_config_value('vnc-listen', self.xend_vnc_listen_default)
+        return self.get_config_string('vnc-listen', self.xend_vnc_listen_default)
 
     def get_vncpasswd_default(self):
-        return self.get_config_value('vncpasswd',
+        return self.get_config_string('vncpasswd',
                                      self.vncpasswd_default)
 
+class XendOptionsFile(XendOptions):
+
+    """Default path to the config file."""
+    config_default = "/etc/xen/xend-config.sxp"
+
+    """Environment variable used to override config_default."""
+    config_var     = "XEND_CONFIG"
+
+    def set_config(self):
+        """If the config file exists, read it. If not, ignore it.
+
+        The config file is a sequence of sxp forms.
+        """
+        self.config_path = os.getenv(self.config_var, self.config_default)
+        if os.path.exists(self.config_path):
+            try:
+                fin = file(self.config_path, 'rb')
+                try:
+                    config = sxp.parse(fin)
+                finally:
+                    fin.close()
+                if config is None:
+                    config = ['xend-config']
+                else:
+                    config.insert(0, 'xend-config')
+                self.config = config
+            except Exception, ex:
+                self._logError('Reading config file %s: %s',
+                               self.config_path, str(ex))
+                raise
+        else:
+            self._logError('Config file does not exist: %s',
+                           self.config_path)
+            self.config = ['xend-config']
+
+    def get_config_value(self, name, val=None):
+        """Get the value of an atomic configuration element.
+
+        @param name: element name
+        @param val:  default value (optional, defaults to None)
+        @return: value
+        """
+        return sxp.child_value(self.config, name, val=val)
+
+    def get_config_bool(self, name, val=None):
+        v = string.lower(str(self.get_config_value(name, val)))
+        if v in ['yes', 'y', '1', 'on',  'true',  't']:
+            return True
+        if v in ['no',  'n', '0', 'off', 'false', 'f']:
+            return False
+        raise XendError("invalid xend config %s: expected bool: %s" % (name, v))
+
+    def get_config_int(self, name, val=None):
+        v = self.get_config_value(name, val)
+        try:
+            return int(v)
+        except Exception:
+            raise XendError("invalid xend config %s: expected int: %s" % (name, v))
+
+    def get_config_string(self, name, val=None):
+        return get_config_value(self, name, val)
+
+    def get_xen_api_server(self):
+        """Get the Xen-API server configuration.
+        """
+        return self.get_config_value('xen-api-server',
+                                     self.xen_api_server_default)
+
+if os.uname()[0] == 'SunOS':
+    class XendOptionsSMF(XendOptions):
+
+        def set_config(self):
+            pass
+
+        def get_config_bool(self, name, val=None):
+            try:
+                return scf.get_bool(name)
+            except scf.error, e:
+                if e[0] == scf.SCF_ERROR_NOT_FOUND:
+                    return val
+                else:
+                    raise XendError("option %s: %s:%s" % (name, e[1], e[2]))
+
+        def get_config_int(self, name, val=None):
+            try:
+                return scf.get_int(name)
+            except scf.error, e:
+                if e[0] == scf.SCF_ERROR_NOT_FOUND:
+                    return val
+                else:
+                    raise XendError("option %s: %s:%s" % (name, e[1], e[2]))
+
+        def get_config_string(self, name, val=None):
+            try:
+                return scf.get_string(name)
+            except scf.error, e:
+                if e[0] == scf.SCF_ERROR_NOT_FOUND:
+                    return val
+                else:
+                    raise XendError("option %s: %s:%s" % (name, e[1], e[2]))
+
+        def get_xen_api_server(self):
+            # When the new server is a supported configuration, we should
+            # expand this.
+            return [["unix"]]
+
 def instance():
-    """Get an instance of XendRoot.
+    """Get an instance of XendOptions.
     Use this instead of the constructor.
     """
     global inst
     try:
         inst
     except:
-        inst = XendRoot()
+        if os.uname()[0] == 'SunOS':
+            inst = XendOptionsSMF()
+        else:
+            inst = XendOptionsFile()
     return inst