[Kimchi-devel] [PATCH V2 3/4] CDROM Management: Devices management model implementation
Sheldon
shaohef at linux.vnet.ibm.com
Tue Feb 11 06:24:59 UTC 2014
On 02/11/2014 06:46 AM, Rodrigo Trujillo wrote:
> This patch adds the CREATE, LOOKUP, UPDATE and DELETE functionalities
> to guest storage devices support.
>
> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo at linux.vnet.ibm.com>
> ---
> src/kimchi/model/vms.py | 190 +++++++++++++++++++++++++++++++++++++++++++++++-
> src/kimchi/xmlutils.py | 5 ++
> 2 files changed, 194 insertions(+), 1 deletion(-)
>
> diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
> index 4623e28..dcde3b8 100644
> --- a/src/kimchi/model/vms.py
> +++ b/src/kimchi/model/vms.py
> @@ -20,8 +20,12 @@
> # License along with this library; if not, write to the Free Software
> # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>
> +import libvirt
> import os
> +import socket
> +import string
> import time
> +import urlparse
> import uuid
> from xml.etree import ElementTree
>
> @@ -36,7 +40,8 @@ from kimchi.model.config import CapabilitiesModel
> from kimchi.model.templates import TemplateModel
> from kimchi.model.utils import get_vm_name
> from kimchi.screenshot import VMScreenshot
> -from kimchi.utils import run_setfacl_set_attr, template_name_from_uri
> +from kimchi.utils import kimchi_log, run_setfacl_set_attr
> +from kimchi.utils import template_name_from_uri
>
>
> DOM_STATE_MAP = {0: 'nostate',
> @@ -47,6 +52,10 @@ DOM_STATE_MAP = {0: 'nostate',
> 5: 'shutoff',
> 6: 'crashed'}
>
> +DEV_TYPE_SRC_ATTR_MAP = {'file': 'file',
> + 'block': 'dev',
> + 'dir': 'dir'}
> +
> GUESTS_STATS_INTERVAL = 5
> VM_STATIC_UPDATE_PARAMS = {'name': './name'}
> VM_LIVE_UPDATE_PARAMS = {}
> @@ -474,3 +483,182 @@ class LibvirtVMScreenshot(VMScreenshot):
> stream.finish()
> finally:
> os.close(fd)
> +
> +
> +class StoragesModel(object):
> + def __init__(self, **kargs):
> + self.conn = kargs['conn']
> +
> + def _get_storage_xml(self, params):
> + storage = """
> + <disk type='%(src_type)s' device='%(dev_type)s'>
> + <driver name='qemu' type='raw'/>%(source)s
> + <target dev='%(dev)s' bus='ide'/>
> + </disk>
> + """
> + info = {}
> + info['dev'] = params.get('dev')
> + info['dev_type'] = params.get('type')
> + info['src_type'] = params.get('src_type')
> +
> + # Working with url paths
> + if info['src_type'] == 'network':
> + net = {}
> + output = urlparse.urlparse(params.get('path'))
> + net['protocol'] = output.scheme
> + net['port'] = output.port or socket.getservbyname(output.scheme)
> + net['hostname'] = output.hostname
> + net['url_path'] = output.path
> + info['source'] = """
> + <source protocol='%(protocol)s' name='%(url_path)s'>
> + <host name='%(hostname)s' port='%(port)s'/>
> + </source>""" % net
> + else:
> + # Fixing source attribute
> + info['source'] = """
> + <source %s='%s'/>""" % (DEV_TYPE_SRC_ATTR_MAP[info['src_type']],
> + params.get('path'))
> + return storage % info
> +
> + def create(self, vm_name, params):
> + #TODO: Check if device name is already use
> + path = params.get('path')
> +
> + # Checking if path exist, if not url
> + if path.startswith('/'):
> + if not os.path.exists(path):
> + msg = "Path specified for device is not valide"
> + raise InvalidParameter(msg)
> + elif path.endswith('.iso') or os.path.isfile(path):
> + params['src_type'] = 'file'
> + elif os.path.isdir(path):
> + params['src_type'] = 'dir'
> + else:
> + params['src_type'] = 'block'
> + else:
> + params['src_type'] = 'network'
> +
> + # Use device name passed or pick next
> + dev_name = params.get('dev')
> + if not dev_name:
> + params['dev'] = self._get_storage_device_name(vm_name)
> +
> + # Add device to VM
> + dev_xml = self._get_storage_xml(params)
> + try:
> + conn = self.conn.get()
> + dom = conn.lookupByName(vm_name)
> + dom.attachDeviceFlags(dev_xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT)
> + except Exception as e:
> + msg = 'Was not possible to attach storage device: %s' % e.message
> + kimchi_log.error(msg)
> + raise OperationFailed(e.message)
> + return params['dev']
> +
> + def _get_storage_device_name(self, vm_name):
> + dev_list = [dev for dev in self.get_list(vm_name) \
> + if dev.startswith('hd')]
> + if len(dev_list) == 0:
> + return 'hda'
> + dev_list.sort()
> + last_dev = dev_list.pop()
> + # TODO: Improve to device names "greater then" hdz
> + next_dev_letter_pos = string.ascii_lowercase.index(last_dev[2]) + 1
> + return 'hd' + string.ascii_lowercase[next_dev_letter_pos]
> +
> + def get_list(self, vm_name):
> + dom = VMModel.get_vm(vm_name, self.conn)
> + xml = dom.XMLDesc(0)
> + device_xml = xmlutils.xml_get_child(xml, './devices')
why add xml_get_child?
how about use lxml?
from lxml import objectify
devices = objectify.fromstring(xml).devices
return [disk.target.attrib['dev'] for disk in
devices.xpath("./disk[@device='disk']")]
or
return [disk.target.get('dev') for disk in
devices.xpath("./disk[@device='disk']")]
> + storages = self._parse_vm_disks(device_xml, ['disk', 'cdrom'])
> + return storages
> +
> + def _parse_vm_disks(self, xml_str, filter_list):
> + # xml_str: xml_str of device with all devices
> + # filter_list: List of which device type to retrieve
> + root = ElementTree.fromstring(xml_str)
> + ret = []
> + for opt in filter_list:
> + xpath = ".disk/[@device='%s']" % opt
> + for disk in root.findall(xpath):
> + name = disk.find('./target').attrib['dev']
> + ret.append(name)
> + return ret
> +
> +
> +class StorageModel(object):
> + def __init__(self, **kargs):
> + self.conn = kargs['conn']
> + self.kargs = kargs
> +
> + def _get_device_xml(self, vm_name, dev_name):
> + # Get VM xml and then devices xml
> + dom = VMModel.get_vm(vm_name, self.conn)
> + device_xml = xmlutils.xml_get_child(dom.XMLDesc(0), './devices')
> + dev_root = ElementTree.fromstring(device_xml)
> + xpath = "./disk/target[@dev='%s']/.." % dev_name
> + return dev_root.find(xpath)
ditto
> +
> + def lookup(self, vm_name, dev_name):
> + # Retrieve disk xml and format return dict
> + disk = self._get_device_xml(vm_name, dev_name)
> + if disk is None:
> + msg = 'The storage device "%s" does not exist in the guest "%s"' % (
> + dev_name,vm_name)
> + raise NotFoundError(msg)
> +
> + source = disk.find('./source')
> + path = ""
> + if source is not None:
> + src_type = disk.attrib.get('type')
> + if src_type == 'network':
> + host = source.find('./host')
> + path = source.attrib.get('protocol') + '://' +\
> + host.attrib.get('name') + ':' +\
> + host.attrib.get('port') + source.attrib.get('name')
> + else:
> + path = source.attrib.get(DEV_TYPE_SRC_ATTR_MAP[src_type])
> + dev_type = disk.attrib['device']
> + return { 'dev': dev_name,
> + 'type': dev_type,
> + 'path': path}
> +
> + def delete(self, vm_name, dev_name):
> + # Get storage device xml
> + disk = self._get_device_xml(vm_name, dev_name)
> + if disk is None:
> + msg = 'The storage device "%s" does not exist in the guest "%s"' % (
> + dev_name,vm_name)
> + raise NotFoundError(msg)
> + try:
> + conn = self.conn.get()
> + dom = conn.lookupByName(vm_name)
> + dom.detachDeviceFlags(ElementTree.tostring(disk),
> + libvirt.VIR_DOMAIN_AFFECT_CURRENT)
> + except Exception as e:
> + msg = 'Was not possible to detach storage device: %s' % e.message
> + kimchi_log.error(msg)
> + raise OperationFailed(e.message)
> +
> + def update(self, vm_name, dev_name, params):
> + info = self.lookup(vm_name, dev_name)
> + backup_params = info.copy()
> + try:
> + self.delete(vm_name, dev_name)
> + except:
> + msg = 'Was not possible to update storage device: %s' % e.message
> + kimchi_log.error(msg)
> + raise OperationFailed(e.message)
> +
> + info.update(params)
> + kargs = {'conn': self.conn}
> + stgModel = StoragesModel(**kargs)
> + try:
> + dev_name = stgModel.create(vm_name, info)
> + return dev_name
> + except Exception as e:
> + # Restoring previous device
> + dev_name = stgModel.create(vm_name, backup_params)
> + msg = 'Was not possible to update storage device: %s' % e.message
> + kimchi_log.error(msg)
> + raise OperationFailed(e.message)
> diff --git a/src/kimchi/xmlutils.py b/src/kimchi/xmlutils.py
> index 51ff0ec..176ceb1 100644
> --- a/src/kimchi/xmlutils.py
> +++ b/src/kimchi/xmlutils.py
> @@ -40,3 +40,8 @@ def xml_item_update(xml, xpath, value):
> item = root.find(xpath)
> item.text = value
> return ElementTree.tostring(root, encoding="utf-8")
> +
> +def xml_get_child(xml, xpath):
> + root = ElementTree.fromstring(xml)
> + item = root.find(xpath)
> + return ElementTree.tostring(item, encoding="utf-8")
--
Thanks and best regards!
Sheldon Feng(冯少合)<shaohef at linux.vnet.ibm.com>
IBM Linux Technology Center
More information about the Kimchi-devel
mailing list