--- tests/xmlparse-xml/change-disk-in.xml | 8 +++++++ tests/xmlparse-xml/change-disk-out.xml | 8 +++++++ tests/xmlparse.py | 9 ++++++++ virtinst/cli.py | 28 ++++++++++++++++++++---- virtinst/devicedisk.py | 23 +++++++++++--------- virtinst/diskbackend.py | 39 +++++++++++++++++++++++++++------- virtinst/storage.py | 14 +++++++++--- 7 files changed, 104 insertions(+), 25 deletions(-) diff --git a/tests/xmlparse-xml/change-disk-in.xml b/tests/xmlparse-xml/change-disk-in.xml index 9725e9b..16c1140 100644 --- a/tests/xmlparse-xml/change-disk-in.xml +++ b/tests/xmlparse-xml/change-disk-in.xml @@ -68,6 +68,14 @@ <target dev='vda' bus='virtio'/> <readonly/> </disk> + <disk type='network' device='disk'> + <driver name='qemu' type='raw'/> + <source protocol='sheepdog' name='sheepdog-pool/test-sheepdog.raw'> + <host name='test.domain'/> + </source> + <target dev='vdc' bus='virtio'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/> + </disk> <input type="mouse" bus="ps2"/> <graphics type="vnc" display=":3.4" xauth="/tmp/.Xauthority"/> <console type="pty"/> diff --git a/tests/xmlparse-xml/change-disk-out.xml b/tests/xmlparse-xml/change-disk-out.xml index d9af1e3..5acd34a 100644 --- a/tests/xmlparse-xml/change-disk-out.xml +++ b/tests/xmlparse-xml/change-disk-out.xml @@ -70,6 +70,14 @@ <target dev="vda" bus="virtio"/> <readonly/> </disk> + <disk type="network" device="disk"> + <driver name="qemu" type="raw"/> + <source protocol="gluster" name="test-volume/test-gluster.raw"> + <host name="192.168.1.100"/> + </source> + <target dev="vdc" bus="virtio"/> + <address type="pci" domain="0x0000" bus="0x00" slot="0x05" function="0x0"/> + </disk> <input type="mouse" bus="ps2"/> <graphics type="vnc" display=":3.4" xauth="/tmp/.Xauthority"/> <console type="pty"/> diff --git a/tests/xmlparse.py b/tests/xmlparse.py index 50ae630..c158005 100644 --- a/tests/xmlparse.py +++ b/tests/xmlparse.py @@ -332,6 +332,7 @@ class XMLParseTest(unittest.TestCase): disk6 = disks[5] disk6.size = 1 disk9 = disks[8] + disk_gl = disks[9] check = self._make_checker(disk1) check("path", "/tmp/test.img", "/dev/null") @@ -374,6 +375,14 @@ class XMLParseTest(unittest.TestCase): check = self._make_checker(disk9) check("sourcePool", "defaultPool", "anotherPool") + check = self._make_checker(disk_gl) + + check("source_protocol", "sheepdog", "gluster") + + check("host_name", "test.domain", "192.168.1.100") + self.assertEquals(disk_gl.path, "sheepdog-pool/test-sheepdog.raw") + disk_gl.path = 'gluster://192.168.1.100/test-volume/test-gluster.raw' + self.assertEquals(disk_gl.path, "test-volume/test-gluster.raw") self._alter_compare(guest.get_xml_config(), outfile) def testSingleDisk(self): diff --git a/virtinst/cli.py b/virtinst/cli.py index 71daca5..f63f15b 100644 --- a/virtinst/cli.py +++ b/virtinst/cli.py @@ -53,7 +53,7 @@ from .devicewatchdog import VirtualWatchdog from .domainnumatune import DomainNumatune from .nodedev import NodeDevice from .osxml import OSXML -from .storage import StoragePool, StorageVolume +from .storage import StoragePool, StorageVolume, NETWORK_STORAGE_PROTOCOLS force = False @@ -1463,7 +1463,6 @@ def _parse_disk_source(guest, path, pool, vol, size, fmt, sparse): if optcount == 0 and size: # Saw something like --disk size=X, have it imply pool=default pool = "default" - if path: abspath = os.path.abspath(path) if os.path.dirname(abspath) == "/var/lib/libvirt/images": @@ -1515,6 +1514,12 @@ def _parse_disk_source(guest, path, pool, vol, size, fmt, sparse): return abspath, volinst, volobj +def _parse_network_protocol(path): + for net_protocol in NETWORK_STORAGE_PROTOCOLS: + if path.startswith(net_protocol + '://'): + return net_protocol + + class ParserDisk(VirtCLIParser): def _init_params(self): self.devclass = VirtualDisk @@ -1531,6 +1536,8 @@ class ParserDisk(VirtCLIParser): self.set_param(None, "format", setter_cb=noset_cb) self.set_param(None, "sparse", setter_cb=noset_cb) + self.set_param("source_protocol", "source_protocol") + self.set_param("host_name", "host_name") self.set_param("path", "path") self.set_param("device", "device") self.set_param("bus", "bus") @@ -1590,11 +1597,21 @@ class ParserDisk(VirtCLIParser): size = parse_size(opts.get_opt_param("size")) fmt = opts.get_opt_param("format") sparse = _on_off_convert("sparse", opts.get_opt_param("sparse")) + host_name = None + protocol = None abspath, volinst, volobj = _parse_disk_source( self.guest, path, pool, vol, size, fmt, sparse) - path = volobj and volobj.path() or abspath + if volobj and volobj.path(): + protocol = _parse_network_protocol(volobj.path()) + if protocol is not None: + tmp = volobj.path()[len(protocol + '://'):] + host_name = tmp.split('/')[0] + path = volobj.path() + else: + path = abspath + if had_path or path: opts.opts["path"] = path or "" @@ -1605,7 +1622,10 @@ class ParserDisk(VirtCLIParser): if any(create_kwargs.values()): inst.set_create_storage(**create_kwargs) inst.cli_size = size - + if protocol: + inst.source_protocol = protocol + if host_name: + inst.host_name = host_name if not inst.target: skip_targets = [d.target for d in self.guest.get_devices("disk")] inst.generate_target(skip_targets) diff --git a/virtinst/devicedisk.py b/virtinst/devicedisk.py index 1d764d9..060cdf6 100644 --- a/virtinst/devicedisk.py +++ b/virtinst/devicedisk.py @@ -104,19 +104,18 @@ def _distill_storage(conn, do_create, nomanaged, """ Validates and updates params when the backing storage is changed """ + pool = None path_is_pool = False storage_capable = conn.check_support(conn.SUPPORT_CONN_STORAGE) - if vol_object: pass elif not storage_capable: pass - elif path and not nomanaged: + elif path and not (nomanaged or diskbackend.is_network_protocol(path)): path = os.path.abspath(path) (vol_object, pool, path_is_pool) = diskbackend.manage_path(conn, path) - creator = None backend = diskbackend.StorageBackend(conn, path, vol_object, path_is_pool and pool or None) @@ -139,7 +138,7 @@ def _distill_storage(conn, do_create, nomanaged, return backend, creator -_TARGET_PROPS = ["file", "dev", "dir"] +_TARGET_PROPS = ["file", "dev", "dir", "name"] class VirtualDisk(VirtualDevice): @@ -183,7 +182,8 @@ class VirtualDisk(VirtualDevice): TYPE_BLOCK = "block" TYPE_DIR = "dir" TYPE_VOLUME = "volume" - types = [TYPE_FILE, TYPE_BLOCK, TYPE_DIR, TYPE_VOLUME] + TYPE_NETWORK = "network" + types = [TYPE_FILE, TYPE_BLOCK, TYPE_DIR, TYPE_NETWORK, TYPE_VOLUME] IO_MODE_NATIVE = "native" IO_MODE_THREADS = "threads" @@ -215,6 +215,8 @@ class VirtualDisk(VirtualDevice): return "dev" elif disk_type == VirtualDisk.TYPE_DIR: return "dir" + elif disk_type == VirtualDisk.TYPE_NETWORK: + return "name" elif disk_type == VirtualDisk.TYPE_VOLUME: return "volume" return "file" @@ -509,12 +511,11 @@ class VirtualDisk(VirtualDevice): num += (ord(c) - ord('a') + k) * (26 ** i) return num - _XML_PROP_ORDER = [ "type", "device", "driver_name", "driver_type", "driver_cache", "driver_discard", "driver_io", "error_policy", - "_xmlpath", "target", "bus", + "_xmlpath", "source_protocol", "host_name", "target", "bus", ] def __init__(self, *args, **kwargs): @@ -535,14 +536,15 @@ class VirtualDisk(VirtualDevice): if self._storage_creator: return self._storage_creator.path return self._storage_backend.path + def _set_path(self, val): if self._storage_creator: raise ValueError("Can't change disk path if storage creation info " "has been set.") self._change_backend(val, None) self._xmlpath = self.path - path = property(_get_path, _set_path) + path = property(_get_path, _set_path) def get_sparse(self): if self._storage_creator: @@ -610,6 +612,9 @@ class VirtualDisk(VirtualDevice): _TARGET_PROPS]) sourcePool = XMLProperty("./source/@pool") + source_protocol = XMLProperty("./source/@protocol") + host_name = XMLProperty("./source/host/@name") + sourceStartupPolicy = XMLProperty("./source/@startupPolicy") device = XMLProperty("./@device", default_cb=lambda s: s.DEVICE_DISK) @@ -618,8 +623,6 @@ class VirtualDisk(VirtualDevice): default_cb=_get_default_driver_name) driver_type = XMLProperty("./driver/@type", default_cb=_get_default_driver_type) - - bus = XMLProperty("./target/@bus") target = XMLProperty("./target/@dev") removable = XMLProperty("./target/@removable", is_onoff=True) diff --git a/virtinst/diskbackend.py b/virtinst/diskbackend.py index 786da79..3cb28d0 100644 --- a/virtinst/diskbackend.py +++ b/virtinst/diskbackend.py @@ -22,10 +22,12 @@ import logging import os import statvfs +import libxml2 import libvirt +import re from . import util -from .storage import StoragePool, StorageVolume +from .storage import StoragePool, StorageVolume, NETWORK_STORAGE_PROTOCOLS def check_if_path_managed(conn, path): @@ -38,9 +40,9 @@ def check_if_path_managed(conn, path): verr = None path_is_pool = False - def lookup_vol_by_path(): + def lookup_vol_by_path(a_vol_path): try: - vol = conn.storageVolLookupByPath(path) + vol = conn.storageVolLookupByPath(a_vol_path) vol.info() return vol, None except libvirt.libvirtError, e: @@ -58,7 +60,7 @@ def check_if_path_managed(conn, path): pass return None - vol = lookup_vol_by_path()[0] + vol = lookup_vol_by_path(path)[0] if not vol: pool = StoragePool.lookup_pool_by_path(conn, os.path.dirname(path)) @@ -72,7 +74,16 @@ def check_if_path_managed(conn, path): # Pool may need to be refreshed, but if it errors, # invalidate it pool.refresh(0) - vol, verr = lookup_vol_by_path() + + pool_xml = libxml2.parseDoc(pool.XMLDesc()) + pool_type = pool_xml.getRootElement().prop('type') + if pool_type in NETWORK_STORAGE_PROTOCOLS: + ctxt = pool_xml.xpathNewContext() + host_name = ctxt.xpathEval("//host[@name]")[0].prop('name') + vol_path = pool_type + '://' + host_name + '/' + path + else: + vol_path = path + vol, verr = lookup_vol_by_path(vol_path) if verr: vol = lookup_vol_name(os.path.basename(path)) except Exception, e: @@ -162,6 +173,9 @@ def build_vol_install(conn, path, pool, size, sparse): return volinst +def is_network_protocol(uri): + return any(uri.startswith(protocol + '://') for protocol in NETWORK_STORAGE_PROTOCOLS) + class _StorageBase(object): def get_size(self): @@ -420,7 +434,8 @@ class StorageBackend(_StorageBase): self._vol_object = vol_object self._pool_object = pool_object self._path = path - + if path and is_network_protocol(path) and not vol_object: + self._vol_object = conn.storageVolLookupByPath(path) if self._vol_object is not None: self._pool_object = None self._path = None @@ -453,15 +468,21 @@ class StorageBackend(_StorageBase): parsexml=self._vol_object.XMLDesc(0)) return self._vol_xml - ############## # Public API # ############## def _get_path(self): if self._vol_object: - return self._get_vol_xml().target_path + path = self._get_vol_xml().target_path + if any(path.startswith(x + '://') for x in NETWORK_STORAGE_PROTOCOLS): + search = re.search("(.*(" + '|'.join(NETWORK_STORAGE_PROTOCOLS) + "):\/\/.*\/)(.*\/.*)", path) + if search: + return search.group(3) + raise RuntimeError('internal error: can\'t parse path: "{0}"'.format(path)) + return path return self._path + path = property(_get_path) def get_vol_object(self): @@ -513,6 +534,8 @@ class StorageBackend(_StorageBase): self._dev_type = "file" elif t == libvirt.VIR_STORAGE_VOL_BLOCK: self._dev_type = "block" + elif t == libvirt.VIR_STORAGE_VOL_NETWORK: + self._dev_type = "network" else: self._dev_type = "file" diff --git a/virtinst/storage.py b/virtinst/storage.py index d090ce4..cd852fa 100644 --- a/virtinst/storage.py +++ b/virtinst/storage.py @@ -35,6 +35,10 @@ DEFAULT_DIR_TARGET_BASE = "/var/lib/libvirt/images/" DEFAULT_SCSI_TARGET = "/dev/disk/by-path" DEFAULT_MPATH_TARGET = "/dev/mapper" +GLUSTER = 'gluster' +SHEEPDOG = 'sheepdog' +NETWORK_STORAGE_PROTOCOLS = (GLUSTER, SHEEPDOG) + class _StoragePermissions(XMLBuilder): _XML_ROOT_NAME = "permissions" @@ -254,6 +258,10 @@ class StoragePool(_StorageObject): if use_source: xml_path = pool.source_path else: + if pool.type == StoragePool.TYPE_GLUSTER: + if pool.source_name == path: + return True + return False xml_path = pool.target_path if xml_path is not None and os.path.abspath(xml_path) == path: return True @@ -291,14 +299,14 @@ class StoragePool(_StorageObject): pass if pool: raise ValueError(_("Name '%s' already in use by another pool." % - name)) + name)) def _get_default_target_path(self): if not self.supports_property("target_path"): return None if (self.type == self.TYPE_DIR or - self.type == self.TYPE_NETFS or - self.type == self.TYPE_FS): + self.type == self.TYPE_NETFS or + self.type == self.TYPE_FS): return (DEFAULT_DIR_TARGET_BASE + self.name) if self.type == self.TYPE_LOGICAL: name = self.name -- 2.1.0 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list