import ast
import logging
import os.path
import ConfigParser
import StringIO
[docs]class ConfigError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return self.msg
[docs]class ConfigNoOptionError(ConfigError):
def __init__(self, option, path):
self.option = option
self.path = path
def __str__(self):
return "There's no option %s in config file %s." % (
self.option, self.path)
[docs]class LibvirtConfigUnknownKeyTypeError(ConfigError):
def __init__(self, key, key_type):
self.key = key
self.key_type = key_type
def __str__(self):
return "Unknown type %s for key %s." % (self.key, self.key_type)
[docs]class LibvirtConfigUnknownKeyError(ConfigError):
def __init__(self, key):
self.key = key
def __str__(self):
return 'Unknown config key %s' % self.key
[docs]class SectionlessConfig(object):
"""
This is a wrapper class for python's internal library ConfigParser except
allows manipulating sectionless configuration file with a dict-like way.
Example config file test.conf:
># This is a comment line.
>a = 1
>b = [hi, there]
>c = hello
>d = "hi, there"
>e = [hi,
> there]
Example script using `try...finally...` statement:
>>> from virttest import utils_config
>>> config = utils_config.SectionlessConfig('test.conf')
>>> try:
... print len(config)
... print config
... print config['a']
... del config['a']
... config['f'] = 'test'
... print config
... finally:
... config.restore()
Example script using `with` statement:
>>> from virttest import utils_config
>>> with utils_config.SectionlessConfig('test.conf') as config:
... print len(config)
... print config
... print config['a']
... del config['a']
... config['f'] = 'test'
... print config
"""
def __init__(self, path):
self.path = path
self.parser = ConfigParser.ConfigParser()
# Prevent of converting option names to lower case
self.parser.optionxform = str
self.backup_content = open(path, 'r').read()
read_fp = StringIO.StringIO('[root]\n' + self.backup_content)
self.parser.readfp(read_fp)
def __sync_file(self):
out_file = open(self.path, 'w')
try:
out_file.write(self.__str__())
finally:
out_file.close()
def __len__(self):
return len(self.parser.items('root'))
def __getitem__(self, option):
try:
return self.parser.get('root', option)
except ConfigParser.NoOptionError:
raise ConfigNoOptionError(option, self.path)
def __setitem__(self, option, value):
self.parser.set('root', option, value)
self.__sync_file()
def __delitem__(self, option):
res = self.parser.remove_option('root', option)
if res:
self.__sync_file()
else:
raise ConfigNoOptionError(option, self.path)
def __contains__(self, item):
return self.parser.has_option('root', item)
def __str__(self):
write_fp = StringIO.StringIO()
self.parser.write(write_fp)
return write_fp.getvalue().split('\n', 1)[1]
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
self.restore()
[docs] def restore(self):
out_file = open(self.path, 'w')
try:
out_file.write(self.backup_content)
finally:
out_file.close()
[docs] def set_raw(self, option, value):
self[option] = "%s" % value
[docs] def set_string(self, option, value):
self[option] = '"%s"' % value
[docs] def set_int(self, option, value):
self[option] = '%d' % int(value)
[docs] def set_float(self, option, value):
self[option] = '%s' % float(value)
[docs] def set_boolean(self, option, value):
if type(value) == str:
value = int(value)
if bool(value):
self[option] = '1'
else:
self[option] = '0'
[docs] def set_list(self, option, value):
# TODO: line separation
value = ['"%s"' % i for i in list(value)]
self[option] = '[%s]' % ', '.join(value)
[docs] def get_raw(self, option):
return self[option]
[docs] def get_string(self, option):
raw_str = self[option].strip()
if raw_str.startswith('"') and raw_str.endswith('"'):
raw_str = raw_str[1:-1]
elif raw_str.startswith("'") and raw_str.endswith("'"):
raw_str = raw_str[1:-1]
else:
raise ValueError("Invalid value for string: %s" % raw_str)
return raw_str
[docs] def get_int(self, option):
return int(self.get_raw(option))
[docs] def get_float(self, option):
return float(self.get_raw(option))
[docs] def get_boolean(self, option):
try:
bool_str = self.get_string(option).lower()
except ValueError:
bool_str = str(self.get_int(option))
if bool_str in ["1", "yes", "true", "on"]:
return True
if bool_str in ["0", "no", "false", "off"]:
return False
raise ValueError("Invalid value for boolean: %s" % bool_str)
[docs] def get_list(self, option):
list_str = self.get_raw(option)
return [str(i) for i in ast.literal_eval(list_str)]
[docs]class LibvirtConfigCommon(SectionlessConfig):
"""
A abstract class to manipulate options of a libvirt related configure files
in a property's way.
Variables "__option_types__" and "conf_path" must be setup in the
inherented classes before use.
"__option_types__" is a dict contains every possible option as keys and
their type ("boolean", "int", "string", "float" or "list") as values.
Basic usage:
1) Create a config file object:
>>> # LibvirtdConfig is a subclass of LibvirtConfigCommon.
>>> config = LibvirtdConfig()
2) Set or update an option:
>>> config.listen_tcp = True
>>> config.listen_tcp = 1
>>> config.listen_tcp = "1" # All three have the same effect.
>>> # If the setting value don't meet the specified type.
>>> config.listen_tcp = "invalid"
>>> # It'll thown an warning message and set a raw string instead.
>>> # Use set_* methods when need to customize the result.
>>> config.set_raw("'1'")
3) Get an option:
>>> is_listening = config.listen_tcp
>>> print is_listening
True
4) Delete an option from the config file:
>>> del config.listen_tcp
5) Make the changes take effect in libvirt by restart libvirt daemon.
>>> from virttest import utils_libvirtd
>>> utils_libvirtd.Libvirtd().restart()
6) Restore the content of the config file.
>>> config.restore()
"""
__option_types__ = {}
conf_path = ''
def __init__(self, path=''):
if path:
self.conf_path = path
if not self.conf_path:
raise ConfigError("Path for config file is not set up.")
if not self.__option_types__:
raise ConfigError("__option_types__ is not set up.")
if not os.path.isfile(self.conf_path):
raise ConfigError("Path for config file %s don't exists."
% self.conf_path)
super(LibvirtConfigCommon, self).__init__(self.conf_path)
def __getattr__(self, key):
if key in self.__option_types__:
key_type = self.__option_types__[key]
if key_type not in ['boolean', 'int', 'float', 'string', 'list']:
raise LibvirtConfigUnknownKeyTypeError(key, key_type)
else:
get_func = eval('self.get_' + key_type)
try:
return get_func(key)
except ConfigNoOptionError:
return None
else:
raise LibvirtConfigUnknownKeyError(key)
def __setattr__(self, key, value):
if key in self.__option_types__:
key_type = self.__option_types__[key]
if key_type not in ['boolean', 'int', 'float', 'string', 'list']:
raise LibvirtConfigUnknownKeyTypeError(key, key_type)
else:
set_func = eval('self.set_' + key_type)
try:
set_func(key, value)
except ValueError:
logging.warning("Key %s might not have type %s. Set raw "
"string instead.", key, key_type)
self.set_raw(key, value)
super(LibvirtConfigCommon, self).__setattr__(key, value)
def __delattr__(self, key):
if key in self.__option_types__:
key_type = self.__option_types__[key]
if key_type not in ['boolean', 'int', 'float', 'string', 'list']:
raise LibvirtConfigUnknownKeyTypeError(key, key_type)
else:
try:
del self[key]
except ConfigNoOptionError:
pass
super(LibvirtConfigCommon, self).__setattr__(key, None)
else:
raise LibvirtConfigUnknownKeyError(key)
[docs]class LibvirtdConfig(LibvirtConfigCommon):
"""
Class for libvirt daemon config file.
"""
conf_path = '/etc/libvirt/libvirtd.conf'
__option_types__ = {
'listen_tls': 'boolean',
'listen_tcp': 'boolean',
'tls_port': 'string',
'tcp_port': 'string',
'listen_addr': 'string',
'mdns_adv': 'boolean',
'mdns_name': 'string',
'unix_sock_group': 'string',
'unix_sock_ro_perms': 'string',
'unix_sock_rw_perms': 'string',
'unix_sock_dir': 'string',
'auth_unix_ro': 'string',
'auth_unix_rw': 'string',
'auth_tcp': 'string',
'auth_tls': 'string',
'access_drivers': 'list',
'key_file': 'string',
'cert_file': 'string',
'ca_file': 'string',
'crl_file': 'string',
'tls_no_sanity_certificate': 'boolean',
'tls_no_verify_certificate': 'boolean',
'tls_allowed_dn_list': 'list',
'sasl_allowed_username_list': 'list',
'max_clients': 'int',
'max_queued_clients': 'int',
'min_workers': 'int',
'max_workers': 'int',
'prio_workers': 'int',
'max_requests': 'int',
'max_client_requests': 'int',
'log_level': 'int',
'log_filters': 'string',
'log_outputs': 'string',
'log_buffer_size': 'int',
'audit_level': 'int',
'audit_logging': 'int',
'host_uuid': 'string',
'keepalive_interval': 'int',
'keepalive_count': 'int',
'keepalive_required': 'boolean',
}
[docs]class LibvirtQemuConfig(LibvirtConfigCommon):
"""
Class for libvirt qemu config file.
"""
conf_path = '/etc/libvirt/qemu.conf'
__option_types__ = {
'vnc_listen': 'string',
'vnc_auto_unix_socket': 'boolean',
'vnc_tls': 'boolean',
'vnc_tls_x509_cert_dir': 'string',
'vnc_tls_x509_verify': 'boolean',
'vnc_password': 'string',
'vnc_sasl': 'boolean',
'vnc_sasl_dir': 'string',
'vnc_allow_host_audio': 'boolean',
'spice_listen': 'string',
'spice_tls': 'boolean',
'spice_tls_x509_cert_dir': 'string',
'spice_password': 'string',
'remote_display_port_min': 'int',
'remote_display_port_max': 'int',
'remote_websocket_port_min': 'int',
'remote_websocket_port_max': 'int',
'security_driver': 'list',
'security_default_confined': 'boolean',
'security_require_confined': 'boolean',
'user': 'string',
'group': 'string',
'dynamic_ownership': 'boolean',
'cgroup_controllers': 'list',
'cgroup_device_acl': 'list',
'save_image_format': 'string',
'dump_image_format': 'string',
'snapshot_image_format': 'string',
'auto_dump_path': 'string',
'auto_dump_bypass_cache': 'boolean',
'auto_start_bypass_cache': 'boolean',
'hugetlbfs_mount': 'list',
'bridge_helper': 'string',
'clear_emulator_capabilities': 'boolean',
'set_process_name': 'boolean',
'max_processes': 'int',
'max_files': 'int',
'mac_filter': 'boolean',
'relaxed_acs_check': 'boolean',
'allow_disk_format_probing': 'boolean',
'lock_manager': 'string',
'max_queued': 'int',
'keepalive_interval': 'int',
'keepalive_count': 'int',
'seccomp_sandbox': 'int',
'migration_address': 'string',
'migration_port_min': 'int',
'migration_port_max': 'int',
}
[docs]class LibvirtdSysConfig(LibvirtConfigCommon):
"""
Class for sysconfig libvirtd config file.
"""
conf_path = '/etc/sysconfig/libvirtd'
__option_types__ = {
'LIBVIRTD_CONFIG': 'string',
'LIBVIRTD_ARGS': 'string',
'KRB5_KTNAME': 'string',
'QEMU_AUDIO_DRV': 'string',
'SDL_AUDIODRIVER': 'string',
'LIBVIRTD_NOFILES_LIMIT': 'int',
}
[docs]class LibvirtGuestsConfig(LibvirtConfigCommon):
"""
Class for sysconfig libvirt-guests config file.
"""
conf_path = '/etc/sysconfig/libvirt-guests'
__option_types__ = {
'URIS': 'string',
'ON_BOOT': 'string',
'START_DELAY': 'int',
'ON_SHUTDOWN': 'string',
'PARALLEL_SHUTDOWN': 'int',
'SHUTDOWN_TIMEOUT': 'int',
'BYPASS_CACHE': 'boolean'
}