[virt-tools-list] [PATCH] Add glusterfs volumes support to virt-install

Anatoly Belikov wormblood at gmail.com
Tue Dec 2 09:35:58 UTC 2014


---
 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




More information about the virt-tools-list mailing list