[Kimchi-devel] [PATCH 3/5] CDROM Management: Devices management model implementation
Rodrigo Trujillo
rodrigo.trujillo at linux.vnet.ibm.com
Fri Feb 14 17:50:50 UTC 2014
Reviewed-by: Rodrigo Trujillo <rodrigo.trujillo at linux.vnet.ibm.com>
On 02/14/2014 03:26 PM, Aline Manera wrote:
> From: Rodrigo Trujillo <rodrigo.trujillo at linux.vnet.ibm.com>
>
> 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>
>
> Using lxml and objectify to parse the XML info.
> Pep8 adjustments in model/vms.py
> Removing hard disk support from model/vms.py
> Including xml creation using lxml in _get_storage_xml
> Including existing device name verification
> Changed model/vms.py to use the new exception model.
>
> Signed-off-by: Daniel Henrique Barboza <danielhb at linux.vnet.ibm.com>
> ---
> src/kimchi/model/vmstorages.py | 221 ++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 221 insertions(+)
> create mode 100644 src/kimchi/model/vmstorages.py
>
> diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py
> new file mode 100644
> index 0000000..1597d49
> --- /dev/null
> +++ b/src/kimchi/model/vmstorages.py
> @@ -0,0 +1,221 @@
> +#
> +# Project Kimchi
> +#
> +# Copyright IBM, Corp. 2014
> +#
> +# Authors:
> +# Rodrigo Trujillo <rodrigo.trujillo at linux.vnet.ibm.com>
> +# Daniel Henrique Barboza <danielhb at linux.vnet.ibm.com>
> +#
> +# This library is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU Lesser General Public
> +# License as published by the Free Software Foundation; either
> +# version 2.1 of the License, or (at your option) any later version.
> +#
> +# This library is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> +# Lesser General Public License for more details.
> +#
> +# You should have received a copy of the GNU Lesser General Public
> +# 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 os
> +import re
> +import socket
> +import string
> +import urlparse
> +
> +import libvirt
> +import lxml.etree as ET
> +from lxml import etree, objectify
> +from lxml.builder import E
> +
> +from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
> +from kimchi.exception import OperationFailed
> +from kimchi.model.vms import DOM_STATE_MAP, VMModel
> +from kimchi.utils import check_url_path
> +
> +DEV_TYPE_SRC_ATTR_MAP = {'file': 'file',
> + 'block': 'dev'}
> +
> +
> +class VMStoragesModel(object):
> + def __init__(self, **kargs):
> + self.conn = kargs['conn']
> +
> + def _get_storage_xml(self, params):
> + src_type = params.get('src_type')
> + disk = E.disk(type=src_type, device=params.get('type'))
> + disk.append(E.driver(name='qemu', type='raw'))
> + # Working with url paths
> + if src_type == 'network':
> + output = urlparse.urlparse(params.get('path'))
> + host = E.host(name=output.hostname, port=
> + output.port or socket.getservbyname(output.scheme))
> + source = E.source(protocol=output.scheme, name=output.path)
> + source.append(host)
> + disk.append(source)
> + else:
> + # Fixing source attribute
> + source = E.source()
> + source.set(DEV_TYPE_SRC_ATTR_MAP[src_type], params.get('path'))
> + disk.append(source)
> +
> + disk.append(E.target(dev=params.get('dev'), bus='ide'))
> + return ET.tostring(disk)
> +
> + def create(self, vm_name, params):
> + dom = VMModel.get_vm(vm_name, self.conn)
> + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff':
> + raise InvalidOperation('KCHCDROM0011E')
> +
> + # Use device name passed or pick next
> + dev_name = params.get('dev', None)
> + if dev_name is None:
> + params['dev'] = self._get_storage_device_name(vm_name)
> + else:
> + devices = self.get_list(vm_name)
> + if dev_name in devices:
> + raise OperationFailed('KCHCDROM0004E', {'dev_name': dev_name,
> + 'vm_name': vm_name})
> +
> + # Path will never be blank due to API.json verification.
> + # There is no need to cover this case here.
> + path = params['path']
> +
> + # Check if path is an url
> + if check_url_path(path):
> + params['src_type'] = 'network'
> + # Check if path is a valid local path
> + elif os.path.exists(path):
> + if os.path.isfile(path):
> + params['src_type'] = 'file'
> + else:
> + # Check if path is a valid cdrom drive
> + with open('/proc/sys/dev/cdrom/info') as cdinfo:
> + content = cdinfo.read()
> +
> + cds = re.findall("drive name:\t\t(.*)", content)
> + if not cds:
> + raise InvalidParameter("KCHCDROM0003E", {'value': path})
> +
> + drives = [os.path.join('/dev', p) for p in cds[0].split('\t')]
> + if path not in drives:
> + raise InvalidParameter("KCHCDROM0003E", {'value': path})
> +
> + params['src_type'] = 'block'
> + else:
> + raise InvalidParameter("KCHCDROM0003E", {'value': path})
> +
> + # 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:
> + raise OperationFailed("KCHCDROM0008E", {'error': 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)
> + devices = objectify.fromstring(xml).devices
> + storages = [disk.target.attrib['dev']
> + for disk in devices.xpath("./disk[@device='disk']")]
> + storages += [disk.target.attrib['dev']
> + for disk in devices.xpath("./disk[@device='cdrom']")]
> + return storages
> +
> +
> +class VMStorageModel(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)
> + xml = dom.XMLDesc(0)
> + devices = objectify.fromstring(xml).devices
> + disk = devices.xpath("./disk/target[@dev='%s']/.." % dev_name)
> + if not disk:
> + return None
> + return disk[0]
> +
> + 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:
> + raise NotFoundError("KCHCDROM0007E", {'dev_name': dev_name,
> + 'vm_name': vm_name})
> + source = disk.source
> + path = ""
> + if source is not None:
> + src_type = disk.attrib['type']
> + if src_type == 'network':
> + host = source.host
> + path = (source.attrib['protocol'] + '://' +
> + host.attrib['name'] + ':' +
> + host.attrib['port'] + source.attrib['name'])
> + else:
> + path = source.attrib[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:
> + raise NotFoundError("KCHCDROM0007E",
> + {'dev_name': dev_name,
> + 'vm_name': vm_name})
> +
> + dom = VMModel.get_vm(vm_name, self.conn)
> + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff':
> + raise InvalidOperation('KCHCDROM0011E')
> +
> + try:
> + conn = self.conn.get()
> + dom = conn.lookupByName(vm_name)
> + dom.detachDeviceFlags(etree.tostring(disk),
> + libvirt.VIR_DOMAIN_AFFECT_CURRENT)
> + except Exception as e:
> + raise OperationFailed("KCHCDROM0010E", {'error': e.message})
> +
> + def update(self, vm_name, dev_name, params):
> + dom = VMModel.get_vm(vm_name, self.conn)
> + if DOM_STATE_MAP[dom.info()[0]] != 'shutoff':
> + raise InvalidOperation('KCHCDROM0011E')
> +
> + info = self.lookup(vm_name, dev_name)
> + backup_params = info.copy()
> +
> + info.update(params)
> + kargs = {'conn': self.conn}
> + stgModel = VMStoragesModel(**kargs)
> + try:
> + self.delete(vm_name, dev_name)
> + return stgModel.create(vm_name, info)
> + except Exception as e:
> + try:
> + stgModel.create(vm_name, backup_params)
> + except:
> + pass
> +
> + raise OperationFailed("KCHCDROM0009E", {'error': e.message})
More information about the Kimchi-devel
mailing list