core-extra/daemon/core/xen/xenconfig.py
2013-08-29 14:21:13 +00:00

265 lines
11 KiB
Python

#
# CORE
# Copyright (c)2011-2012 the Boeing Company.
# See the LICENSE file included in this distribution.
#
# author: Jeff Ahrenholz <jeffrey.m.ahrenholz@boeing.com>
#
'''
xenconfig.py: Implementation of the XenConfigManager class for managing
configurable items for XenNodes.
Configuration for a XenNode is available at these three levels:
Global config: XenConfigManager.configs[0] = (type='xen', values)
Nodes of this machine type have this config. These are the default values.
XenConfigManager.default_config comes from defaults + xen.conf
Node type config: XenConfigManager.configs[0] = (type='mytype', values)
All nodes of this type have this config.
Node-specific config: XenConfigManager.configs[nodenumber] = (type, values)
The node having this specific number has this config.
'''
import sys, os, threading, subprocess, time, string
import ConfigParser
from xml.dom.minidom import parseString, Document
from core.constants import *
from core.api import coreapi
from core.conf import ConfigurableManager, Configurable
class XenConfigManager(ConfigurableManager):
''' Xen controller object. Lives in a Session instance and is used for
building Xen profiles.
'''
_name = "xen"
_type = coreapi.CORE_TLV_REG_EMULSRV
def __init__(self, session):
ConfigurableManager.__init__(self, session)
self.verbose = self.session.getcfgitembool('verbose', False)
self.default_config = XenDefaultConfig(session, objid=None)
self.loadconfigfile()
def setconfig(self, nodenum, conftype, values):
''' add configuration values for a node to a dictionary; values are
usually received from a Configuration Message, and may refer to a
node for which no object exists yet
'''
if nodenum is None:
nodenum = 0 # used for storing the global default config
return ConfigurableManager.setconfig(self, nodenum, conftype, values)
def getconfig(self, nodenum, conftype, defaultvalues):
''' get configuration values for a node; if the values don't exist in
our dictionary then return the default values supplied; if conftype
is None then we return a match on any conftype.
'''
if nodenum is None:
nodenum = 0 # used for storing the global default config
return ConfigurableManager.getconfig(self, nodenum, conftype,
defaultvalues)
def clearconfig(self, nodenum):
''' remove configuration values for a node
'''
ConfigurableManager.clearconfig(self, nodenum)
if 0 in self.configs:
self.configs.pop(0)
def configure(self, session, msg):
''' Handle configuration messages for global Xen config.
'''
return self.default_config.configure(self, msg)
def loadconfigfile(self, filename=None):
''' Load defaults from the /etc/core/xen.conf file into dict object.
'''
if filename is None:
filename = os.path.join(CORE_CONF_DIR, 'xen.conf')
cfg = ConfigParser.SafeConfigParser()
if filename not in cfg.read(filename):
self.session.warn("unable to read Xen config file: %s" % filename)
return
section = "xen"
if not cfg.has_section(section):
self.session.warn("%s is missing a xen section!" % filename)
return
self.configfile = dict(cfg.items(section))
# populate default config items from config file entries
vals = list(self.default_config.getdefaultvalues())
names = self.default_config.getnames()
for i in range(len(names)):
if names[i] in self.configfile:
vals[i] = self.configfile[names[i]]
# this sets XenConfigManager.configs[0] = (type='xen', vals)
self.setconfig(None, self.default_config._name, vals)
def getconfigitem(self, name, model=None, node=None, value=None):
''' Get a config item of the given name, first looking for node-specific
configuration, then model specific, and finally global defaults.
If a value is supplied, it will override any stored config.
'''
if value is not None:
return value
n = None
if node:
n = node.objid
(t, v) = self.getconfig(nodenum=n, conftype=model, defaultvalues=None)
if n is not None and v is None:
# get item from default config for the node type
(t, v) = self.getconfig(nodenum=None, conftype=model,
defaultvalues=None)
if v is None:
# get item from default config for the machine type
(t, v) = self.getconfig(nodenum=None,
conftype=self.default_config._name,
defaultvalues=None)
confignames = self.default_config.getnames()
if v and name in confignames:
i = confignames.index(name)
return v[i]
else:
# name may only exist in config file
if name in self.configfile:
return self.configfile[name]
else:
#self.warn("missing config item '%s'" % name)
return None
class XenConfig(Configurable):
''' Manage Xen configuration profiles.
'''
@classmethod
def configure(cls, xen, msg):
''' Handle configuration messages for setting up a model.
Similar to Configurable.configure(), but considers opaque data
for indicating node types.
'''
reply = None
nodenum = msg.gettlv(coreapi.CORE_TLV_CONF_NODE)
objname = msg.gettlv(coreapi.CORE_TLV_CONF_OBJ)
conftype = msg.gettlv(coreapi.CORE_TLV_CONF_TYPE)
opaque = msg.gettlv(coreapi.CORE_TLV_CONF_OPAQUE)
nodetype = objname
if opaque is not None:
opaque_items = opaque.split(':')
if len(opaque_items) != 2:
xen.warn("xen config: invalid opaque data in conf message")
return None
nodetype = opaque_items[1]
if xen.verbose:
xen.info("received configure message for %s" % nodetype)
if conftype == coreapi.CONF_TYPE_FLAGS_REQUEST:
if xen.verbose:
xen.info("replying to configure request for %s " % nodetype)
# when object name is "all", the reply to this request may be None
# if this node has not been configured for this model; otherwise we
# reply with the defaults for this model
if objname == "all":
typeflags = coreapi.CONF_TYPE_FLAGS_UPDATE
else:
typeflags = coreapi.CONF_TYPE_FLAGS_NONE
values = xen.getconfig(nodenum, nodetype, defaultvalues=None)[1]
if values is None:
# get defaults from default "xen" config which includes
# settings from both cls._confdefaultvalues and xen.conf
defaults = cls.getdefaultvalues()
values = xen.getconfig(nodenum, cls._name, defaults)[1]
if values is None:
return None
# reply with config options
if nodenum is None:
nodenum = 0
reply = cls.toconfmsg(0, nodenum, typeflags, nodetype, values)
elif conftype == coreapi.CONF_TYPE_FLAGS_RESET:
if objname == "all":
xen.clearconfig(nodenum)
#elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
else:
# store the configuration values for later use, when the XenNode
# object has been created
if objname is None:
xen.info("no configuration object for node %s" % nodenum)
return None
values_str = msg.gettlv(coreapi.CORE_TLV_CONF_VALUES)
if values_str is None:
# use default or preconfigured values
defaults = cls.getdefaultvalues()
values = xen.getconfig(nodenum, cls._name, defaults)[1]
else:
# use new values supplied from the conf message
values = values_str.split('|')
xen.setconfig(nodenum, nodetype, values)
return reply
@classmethod
def toconfmsg(cls, flags, nodenum, typeflags, nodetype, values):
''' Convert this class to a Config API message. Some TLVs are defined
by the class, but node number, conf type flags, and values must
be passed in.
'''
values_str = string.join(values, '|')
tlvdata = ""
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_NODE, nodenum)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OBJ,
cls._name)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_TYPE,
typeflags)
datatypes = tuple( map(lambda x: x[1], cls._confmatrix) )
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_DATA_TYPES,
datatypes)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_VALUES,
values_str)
captions = reduce( lambda a,b: a + '|' + b, \
map(lambda x: x[4], cls._confmatrix))
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_CAPTIONS,
captions)
possiblevals = reduce( lambda a,b: a + '|' + b, \
map(lambda x: x[3], cls._confmatrix))
tlvdata += coreapi.CoreConfTlv.pack(
coreapi.CORE_TLV_CONF_POSSIBLE_VALUES, possiblevals)
if cls._bitmap is not None:
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_BITMAP,
cls._bitmap)
if cls._confgroups is not None:
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_GROUPS,
cls._confgroups)
opaque = "%s:%s" % (cls._name, nodetype)
tlvdata += coreapi.CoreConfTlv.pack(coreapi.CORE_TLV_CONF_OPAQUE,
opaque)
msg = coreapi.CoreConfMessage.pack(flags, tlvdata)
return msg
class XenDefaultConfig(XenConfig):
''' Global default Xen configuration options.
'''
_name = "xen"
# Configuration items:
# ('name', 'type', 'default', 'possible-value-list', 'caption')
_confmatrix = [
('ram_size', coreapi.CONF_DATA_TYPE_STRING, '256', '',
'ram size (MB)'),
('disk_size', coreapi.CONF_DATA_TYPE_STRING, '256M', '',
'disk size (use K/M/G suffix)'),
('iso_file', coreapi.CONF_DATA_TYPE_STRING, '', '',
'iso file'),
('mount_path', coreapi.CONF_DATA_TYPE_STRING, '', '',
'mount path'),
('etc_path', coreapi.CONF_DATA_TYPE_STRING, '', '',
'etc path'),
('persist_tar_iso', coreapi.CONF_DATA_TYPE_STRING, '', '',
'iso persist tar file'),
('persist_tar', coreapi.CONF_DATA_TYPE_STRING, '', '',
'persist tar file'),
('root_password', coreapi.CONF_DATA_TYPE_STRING, 'password', '',
'root password'),
]
_confgroups = "domU properties:1-%d" % len(_confmatrix)