[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