"""
Module simplifying manipulation of XML described at
http://libvirt.org/formatstorage.html#StoragePool
"""
import os
import logging
import tempfile
from autotest.client.shared import error
from virttest import libvirt_storage
from virttest.libvirt_xml import base, xcepts, accessors
[docs]class SourceXML(base.LibvirtXMLBase):
"""
Source block in pool xml, optionally containing different elements and
attributes which dependent on pool type.
"""
__slots__ = ('device_path', 'vg_name', 'host_name', 'dir_path',
'adp_type', 'adp_name', 'adp_parent', 'adp_wwnn',
'adp_wwpn', 'format_type', 'hosts', 'auth_type',
'auth_username', 'secret_usage', 'secret_uuid')
def __init__(self, virsh_instance=base.virsh):
"""
Create new SourceXML instance.
"""
accessors.XMLAttribute(property_name='device_path',
libvirtxml=self,
parent_xpath='/',
tag_name='device',
attribute='path')
accessors.XMLElementText(property_name='vg_name',
libvirtxml=self,
parent_xpath='/',
tag_name='name')
accessors.XMLAttribute(property_name='host_name',
libvirtxml=self,
parent_xpath='/',
tag_name='host',
attribute='name')
accessors.XMLAttribute(property_name='dir_path',
libvirtxml=self,
parent_xpath='/',
tag_name='dir',
attribute='path')
accessors.XMLAttribute(property_name='adp_type',
libvirtxml=self,
parent_xpath='/',
tag_name='adapter',
attribute='type')
accessors.XMLAttribute(property_name='adp_name',
libvirtxml=self,
parent_xpath='/',
tag_name='adapter',
attribute='name')
accessors.XMLAttribute(property_name='adp_parent',
libvirtxml=self,
parent_xpath='/',
tag_name='adapter',
attribute='parent')
accessors.XMLAttribute(property_name='adp_wwnn',
libvirtxml=self,
parent_xpath='/',
tag_name='adapter',
attribute='wwnn')
accessors.XMLAttribute(property_name='adp_wwpn',
libvirtxml=self,
parent_xpath='/',
tag_name='adapter',
attribute='wwpn')
accessors.XMLAttribute(property_name='format_type',
libvirtxml=self,
parent_xpath='/',
tag_name='format',
attribute='type')
accessors.XMLElementList('hosts', self, parent_xpath='/',
marshal_from=self.marshal_from_host,
marshal_to=self.marshal_to_host)
accessors.XMLAttribute(property_name='auth_type',
libvirtxml=self,
parent_xpath='/',
tag_name='auth',
attribute='type')
accessors.XMLAttribute(property_name='auth_username',
libvirtxml=self,
parent_xpath='/',
tag_name='auth',
attribute='username')
accessors.XMLAttribute(property_name='secret_usage',
libvirtxml=self,
parent_xpath='/auth',
tag_name='secret',
attribute='usage')
accessors.XMLAttribute(property_name='secret_uuid',
libvirtxml=self,
parent_xpath='/auth',
tag_name='secret',
attribute='uuid')
super(SourceXML, self).__init__(virsh_instance=virsh_instance)
self.xml = u"<source></source>"
@staticmethod
[docs] def marshal_from_host(item, index, libvirtxml):
"""Convert a dictionary into a tag + attributes"""
del index # not used
del libvirtxml # not used
if not isinstance(item, dict):
raise xcepts.LibvirtXMLError("Expected a dictionary of host "
"attributes, not a %s"
% str(item))
return ('host', dict(item)) # return copy of dict, not reference
@staticmethod
[docs] def marshal_to_host(tag, attr_dict, index, libvirtxml):
"""Convert a tag + attributes into a dictionary"""
del index # not used
del libvirtxml # not used
if tag != 'host':
return None # skip this one
return dict(attr_dict) # return copy of dict, not reference
[docs]class PoolXMLBase(base.LibvirtXMLBase):
"""
Accessor methods for PoolXML class.
Properties:
pool_type:
string, pool type
name:
string, pool name
uuid:
string, pool uuid
capacity:
integer, pool total capacity
allocation:
integer, pool allocated capacity
available:
integer, pool available capacity
source:
PoolSourceXML instanc
target:
string, target path of pool
"""
__slots__ = ('pool_type', 'name', 'uuid', 'capacity',
'allocation', 'available', 'source', 'target_path',
'mode', 'owner', 'group')
__uncompareable__ = base.LibvirtXMLBase.__uncompareable__
__schema_name__ = "pool"
def __init__(self, virsh_instance=base.virsh):
accessors.XMLAttribute(property_name='pool_type',
libvirtxml=self,
parent_xpath='/',
tag_name='pool',
attribute='type')
accessors.XMLElementText(property_name='name',
libvirtxml=self,
parent_xpath='/',
tag_name='name')
accessors.XMLElementText(property_name='uuid',
libvirtxml=self,
parent_xpath='/',
tag_name='uuid')
accessors.XMLElementInt(property_name='capacity',
libvirtxml=self,
parent_xpath='/',
tag_name='capacity')
accessors.XMLElementInt(property_name='allocation',
libvirtxml=self,
parent_xpath='/',
tag_name='allocation')
accessors.XMLElementInt(property_name='available',
libvirtxml=self,
parent_xpath='/',
tag_name='available')
accessors.XMLElementText(property_name='target_path',
libvirtxml=self,
parent_xpath='/target',
tag_name='path')
accessors.XMLElementText(property_name='mode',
libvirtxml=self,
parent_xpath='/target/permissions',
tag_name='mode')
accessors.XMLElementInt(property_name='owner',
libvirtxml=self,
parent_xpath='/target/permissions',
tag_name='owner')
accessors.XMLElementInt(property_name='group',
libvirtxml=self,
parent_xpath='/target/permissions',
tag_name='group')
super(PoolXMLBase, self).__init__(virsh_instance=virsh_instance)
[docs] def get_source(self):
xmltreefile = self.__dict_get__('xml')
try:
source_root = xmltreefile.reroot('/source')
except KeyError, detail:
raise xcepts.LibvirtXMLError(detail)
sourcexml = SourceXML(virsh_instance=self.__dict_get__('virsh'))
sourcexml.xmltreefile = source_root
return sourcexml
[docs] def del_source(self):
xmltreefile = self.__dict_get__('xml')
element = xmltreefile.find('/source')
if element is not None:
xmltreefile.remove(element)
xmltreefile.write()
[docs] def set_source(self, value):
if not issubclass(type(value), SourceXML):
raise xcepts.LibvirtXMLError(
"Value must be a SourceXML or subclass")
xmltreefile = self.__dict_get__('xml')
self.del_source()
root = xmltreefile.getroot()
root.append(value.xmltreefile.getroot())
xmltreefile.write()
[docs]class PoolXML(PoolXMLBase):
"""
Manipulators of a libvirt Pool through it's XML definition.
"""
__slots__ = []
def __init__(self, pool_type='dir', virsh_instance=base.virsh):
"""
Initialize new instance with empty XML
"""
super(PoolXML, self).__init__(virsh_instance=virsh_instance)
self.xml = u"<pool type='%s'></pool>" % pool_type
@staticmethod
[docs] def new_from_dumpxml(name, virsh_instance=base.virsh):
"""
Return new PoolXML instance from virsh pool-dumpxml command
:param name: Name of pool to pool-dumpxml
:param virsh_instance: Virsh module or instance to use
:return: new initialized PoolXML instance
"""
pool_xml = PoolXML(virsh_instance=virsh_instance)
pool_xml['xml'] = virsh_instance.pool_dumpxml(name)
return pool_xml
@staticmethod
[docs] def get_type(name, virsh_instance=base.virsh):
"""
Return pool type by pool name
:param name: pool name
:return: pool type
"""
pool_xml = PoolXML.new_from_dumpxml(name, virsh_instance)
return pool_xml.pool_type
@staticmethod
[docs] def get_pool_details(name, virsh_instance=base.virsh):
"""
Return pool details by pool name.
:param name: pool name
:return: a dict which include a series of pool details
"""
pool_xml = PoolXML.new_from_dumpxml(name, virsh_instance)
pool_details = {}
pool_details['type'] = pool_xml.pool_type
pool_details['uuid'] = pool_xml.uuid
pool_details['capacity'] = pool_xml.capacity
pool_details['allocation'] = pool_xml.allocation
pool_details['available'] = pool_xml.available
if pool_xml.pool_type != "gluster":
pool_details['target_path'] = pool_xml.target_path
return pool_details
[docs] def pool_undefine(self):
"""
Undefine pool with libvirt retaining XML in instance
"""
try:
self.virsh.pool_undefine(self.name, ignore_status=False)
except error.CmdError:
logging.error("Undefine pool '%s' failed.", self.name)
return False
[docs] def pool_define(self):
"""
Define pool with virsh from this instance
"""
result = self.virsh.pool_define(self.xml)
if result.exit_status:
logging.error("Define %s failed.\n"
"Detail: %s.", self.name, result.stderr)
return False
return True
@staticmethod
[docs] def pool_rename(name, new_name, uuid=None, virsh_instance=base.virsh):
"""
Rename a pool from pool XML.
:param name: Original pool name.
:param new_name: new name of pool.
:param uuid: new pool uuid, if None libvirt will generate automatically.
:return: True/False or raise LibvirtXMLError
"""
pool_ins = libvirt_storage.StoragePool()
if not pool_ins.is_pool_persistent(name):
logging.error("Cannot rename for transient pool")
return False
start_pool = False
if pool_ins.is_pool_active(name):
start_pool = True
poolxml = PoolXML.new_from_dumpxml(name, virsh_instance)
backup = poolxml.copy()
def _cleanup(details=""):
# cleanup if rename failed
backup.pool_define()
if start_pool:
pool_ins.start_pool(name)
raise xcepts.LibvirtXMLError("%s" % details)
if not pool_ins.delete_pool(name):
_cleanup(details="Delete pool %s failed" % name)
# Alter the XML
poolxml.name = new_name
if uuid is None:
del poolxml.uuid
else:
poolxml.uuid = uuid
# Re-define XML to libvirt
logging.debug("Rename pool: %s to %s.", name, new_name)
if not poolxml.pool_define():
logging.info("Pool xml: %s" % poolxml.get('xml'))
_cleanup(details="Define pool %s failed" % new_name)
if start_pool:
pool_ins.start_pool(new_name)
return True
@staticmethod
[docs] def backup_xml(name, virsh_instance=base.virsh):
"""
Backup the pool xml file.
"""
try:
xml_file = tempfile.mktemp(dir="/tmp")
virsh_instance.pool_dumpxml(name, to_file=xml_file)
return xml_file
except Exception, detail:
if os.path.exists(xml_file):
os.remove(xml_file)
logging.error("Failed to backup xml file:\n%s", detail)
return ""
[docs] def debug_xml(self):
"""
Dump contents of XML file for debugging
"""
xml = str(self)
for debug_line in str(xml).splitlines():
logging.debug("Pool XML: %s", debug_line)