core-extra/daemon/core/xen/xenconfig.py

274 lines
11 KiB
Python

"""
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 ConfigParser
import os
import string
from core import constants
from core.api import coreapi
from core.conf import Configurable
from core.conf import ConfigurableManager
from core.enumerations import ConfigDataTypes
from core.enumerations import ConfigFlags
from core.enumerations import ConfigTlvs
from core.enumerations import RegisterTlvs
from core.misc import log
logger = log.get_logger(__name__)
class XenConfigManager(ConfigurableManager):
"""
Xen controller object. Lives in a Session instance and is used for
building Xen profiles.
"""
name = "xen"
config_type = RegisterTlvs.EMULATION_SERVER.value
def __init__(self, session):
"""
Creates a XenConfigManager instance.
:param core.session.Session session: session this manager is tied to
:return: nothing
"""
ConfigurableManager.__init__(self)
self.default_config = XenDefaultConfig(session, object_id=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, config_data):
"""
Handle configuration messages for global Xen config.
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
"""
return self.default_config.configure(self, config_data)
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(constants.CORE_CONF_DIR, 'xen.conf')
cfg = ConfigParser.SafeConfigParser()
if filename not in cfg.read(filename):
logger.warn("unable to read Xen config file: %s" % filename)
return
section = "xen"
if not cfg.has_section(section):
logger.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:
# logger.warn("missing config item '%s'" % name)
return None
class XenConfig(Configurable):
"""
Manage Xen configuration profiles.
"""
@classmethod
def configure(cls, xen, config_data):
"""
Handle configuration messages for setting up a model.
Similar to Configurable.configure(), but considers opaque data
for indicating node types.
:param xen: xen instance to configure
:param core.conf.ConfigData config_data: configuration data for carrying out a configuration
"""
reply = None
node_id = config_data.node
object_name = config_data.object
config_type = config_data.type
opaque = config_data.opaque
values_str = config_data.data_values
nodetype = object_name
if opaque is not None:
opaque_items = opaque.split(':')
if len(opaque_items) != 2:
logger.warn("xen config: invalid opaque data in conf message")
return None
nodetype = opaque_items[1]
logger.info("received configure message for %s", nodetype)
if config_type == ConfigFlags.REQUEST.value:
logger.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 object_name == "all":
typeflags = ConfigFlags.UPDATE.value
else:
typeflags = ConfigFlags.NONE.value
values = xen.getconfig(node_id, 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(node_id, cls.name, defaults)[1]
if values is None:
return None
# reply with config options
if node_id is None:
node_id = 0
reply = cls.config_data(0, node_id, typeflags, nodetype, values)
elif config_type == ConfigFlags.RESET.value:
if object_name == "all":
xen.clearconfig(node_id)
# elif conftype == coreapi.CONF_TYPE_FLAGS_UPDATE:
else:
# store the configuration values for later use, when the XenNode
# object has been created
if object_name is None:
logger.info("no configuration object for node %s" % node_id)
return None
if values_str is None:
# use default or preconfigured values
defaults = cls.getdefaultvalues()
values = xen.getconfig(node_id, cls.name, defaults)[1]
else:
# use new values supplied from the conf message
values = values_str.split('|')
xen.setconfig(node_id, nodetype, values)
return reply
@classmethod
def config_data(cls, flags, node_id, type_flags, 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.CoreConfigTlv.pack(ConfigTlvs.NODE.value, node_id)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OBJECT.value, cls.name)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.TYPE.value, type_flags)
datatypes = tuple(map(lambda x: x[1], cls.config_matrix))
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.DATA_TYPES.value, datatypes)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.VALUES.value, values_str)
captions = reduce(lambda a, b: a + '|' + b, map(lambda x: x[4], cls.config_matrix))
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.CAPTIONS, captions)
possiblevals = reduce(lambda a, b: a + '|' + b, map(lambda x: x[3], cls.config_matrix))
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.POSSIBLE_VALUES.value, possiblevals)
if cls.bitmap is not None:
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.BITMAP.value, cls.bitmap)
if cls.config_groups is not None:
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.GROUPS.value, cls.config_groups)
opaque = "%s:%s" % (cls.name, nodetype)
tlvdata += coreapi.CoreConfigTlv.pack(ConfigTlvs.OPAQUE.value, 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')
config_matrix = [
('ram_size', ConfigDataTypes.STRING.value, '256', '',
'ram size (MB)'),
('disk_size', ConfigDataTypes.STRING.value, '256M', '',
'disk size (use K/M/G suffix)'),
('iso_file', ConfigDataTypes.STRING.value, '', '',
'iso file'),
('mount_path', ConfigDataTypes.STRING.value, '', '',
'mount path'),
('etc_path', ConfigDataTypes.STRING.value, '', '',
'etc path'),
('persist_tar_iso', ConfigDataTypes.STRING.value, '', '',
'iso persist tar file'),
('persist_tar', ConfigDataTypes.STRING.value, '', '',
'persist tar file'),
('root_password', ConfigDataTypes.STRING.value, 'password', '',
'root password'),
]
config_groups = "domU properties:1-%d" % len(config_matrix)