diff --git a/tests/cli-test-xml/compare/virt-install-many-devices.xml b/tests/cli-test-xml/compare/virt-install-many-devices.xml
index 637c91ecd..1aee27919 100644
--- a/tests/cli-test-xml/compare/virt-install-many-devices.xml
+++ b/tests/cli-test-xml/compare/virt-install-many-devices.xml
@@ -77,6 +77,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
@@ -226,6 +238,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/clitest.py b/tests/clitest.py
index 32e73645b..2d010147b 100644
--- a/tests/clitest.py
+++ b/tests/clitest.py
@@ -562,6 +562,8 @@ c.add_compare("""--hvm --pxe \
--disk device=cdrom,bus=sata,read_bytes_sec=1,read_iops_sec=2,total_bytes_sec=10,total_iops_sec=20,write_bytes_sec=5,write_iops_sec=6 \
--disk size=1 \
--disk source_pool=rbd-ceph,source_volume=some-rbd-vol \
+--disk source_protocol=http,source_host_name=example.com,source_host_port=8000,source_name=/path/to/my/file,bus=scsi \
+--disk source_protocol=nbd,source_host_transport=unix,source_host_socket=/tmp/socket,bus=scsi \
--serial tcp,host=:2222,mode=bind,protocol=telnet \
--filesystem /source,/target,mode=squash \
--network user,mac=12:34:56:78:11:22,portgroup=foo \
diff --git a/tests/xmlparse-xml/change-disk-in.xml b/tests/xmlparse-xml/change-disk-in.xml
index 2136be955..ba5df8283 100644
--- a/tests/xmlparse-xml/change-disk-in.xml
+++ b/tests/xmlparse-xml/change-disk-in.xml
@@ -68,6 +68,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/xmlparse-xml/change-disk-out.xml b/tests/xmlparse-xml/change-disk-out.xml
index e6b60b952..421ac5ce6 100644
--- a/tests/xmlparse-xml/change-disk-out.xml
+++ b/tests/xmlparse-xml/change-disk-out.xml
@@ -36,9 +36,12 @@
-
+
+
+
+
@@ -66,10 +69,26 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/xmlparse.py b/tests/xmlparse.py
index f36b57c91..99d4b30ec 100644
--- a/tests/xmlparse.py
+++ b/tests/xmlparse.py
@@ -347,6 +347,14 @@ class XMLParseTest(unittest.TestCase):
check("bus", "ide", "fdc")
check("error_policy", "stop", None)
+ disk = _get_disk("sda")
+ check = self._make_checker(disk)
+ check("source_protocol", None, "http")
+ check("source_name", None, "/my/file")
+ check("source_host_name", None, "exaaaaample.com")
+ disk.sync_path_props()
+ check("path", "http://exaaaaample.com/my/file")
+
disk = _get_disk("fda")
check = self._make_checker(disk)
check("path", None, "/dev/default-pool/default-vol")
@@ -369,6 +377,22 @@ class XMLParseTest(unittest.TestCase):
disk = _get_disk("vdb")
check = self._make_checker(disk)
check("source_pool", "defaultPool", "anotherPool")
+ check("source_volume", "foobar", "newvol")
+
+ disk = _get_disk("vdc")
+ check = self._make_checker(disk)
+ check("source_protocol", "rbd", "gluster")
+ check("source_name", "pool/image", "new-val/vol")
+ check("source_host_name", "mon1.example.org", "diff.example.org")
+ check("source_host_port", 6321, 1234)
+ check("path", "gluster://diff.example.org:1234/new-val/vol")
+
+ disk = _get_disk("vdd")
+ check = self._make_checker(disk)
+ check("source_protocol", "nbd")
+ check("source_host_transport", "unix")
+ check("source_host_socket", "/var/run/nbdsock")
+ check("path", "nbd+unix:///var/run/nbdsock")
self._alter_compare(guest.get_xml_config(), outfile)
diff --git a/virtinst/cli.py b/virtinst/cli.py
index b683c2619..e3b1b794a 100644
--- a/virtinst/cli.py
+++ b/virtinst/cli.py
@@ -1486,6 +1486,13 @@ class ParserDisk(VirtCLIParser):
self.set_param("source_pool", "source_pool")
self.set_param("source_volume", "source_volume")
+ self.set_param("source_name", "source_name")
+ self.set_param("source_protocol", "source_protocol")
+ self.set_param("source_host_name", "source_host_name")
+ self.set_param("source_host_port", "source_host_port")
+ self.set_param("source_host_socket", "source_host_socket")
+ self.set_param("source_host_transport", "source_host_transport")
+
self.set_param("path", "path")
self.set_param("device", "device")
self.set_param("bus", "bus")
diff --git a/virtinst/devicedisk.py b/virtinst/devicedisk.py
index f6689a4ae..28ee04232 100644
--- a/virtinst/devicedisk.py
+++ b/virtinst/devicedisk.py
@@ -139,7 +139,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]
IO_MODE_NATIVE = "native"
IO_MODE_THREADS = "threads"
@@ -490,7 +491,9 @@ class VirtualDisk(VirtualDevice):
"driver_name", "driver_type",
"driver_cache", "driver_discard", "driver_io", "error_policy",
"_source_file", "_source_dev", "_source_dir",
- "source_volume", "source_pool",
+ "source_volume", "source_pool", "source_protocol", "source_name",
+ "source_host_name", "source_host_port",
+ "source_host_transport", "source_host_socket",
"target", "bus",
]
@@ -506,6 +509,10 @@ class VirtualDisk(VirtualDevice):
#############################
def _get_path(self):
+ if self.type == VirtualDisk.TYPE_NETWORK:
+ # Fill in a completed URL for virt-manager UI, path comparison, etc
+ return self._url_from_network_source()
+
if self._storage_creator:
return self._storage_creator.path
return self._storage_backend.path
@@ -585,11 +592,42 @@ class VirtualDisk(VirtualDevice):
source_pool = XMLProperty("./source/@pool")
source_volume = XMLProperty("./source/@volume")
- def _get_default_type(self):
+ source_name = XMLProperty("./source/@name")
+ source_protocol = XMLProperty("./source/@protocol")
+ # Technically multiple host lines can be listed
+ source_host_name = XMLProperty("./source/host/@name")
+ source_host_port = XMLProperty("./source/host/@port", is_int=True)
+ source_host_transport = XMLProperty("./source/host/@transport")
+ source_host_socket = XMLProperty("./source/host/@socket")
+
+ def _url_from_network_source(self):
+ ret = self.source_protocol
+ if self.source_host_transport:
+ ret += "+%s" % self.source_host_transport
+ ret += "://"
+ if self.source_host_name:
+ ret += self.source_host_name
+ if self.source_host_port:
+ ret += ":" + str(self.source_host_port)
+ if self.source_name:
+ if not self.source_name.startswith("/"):
+ ret += "/"
+ ret += self.source_name
+ elif self.source_host_socket:
+ if not self.source_host_socket.startswith("/"):
+ ret += "/"
+ ret += self.source_host_socket
+ return ret
+
+ def _get_default_type(self, skip_backend=False):
if self.source_pool or self.source_volume:
return VirtualDisk.TYPE_VOLUME
+ if self.source_protocol:
+ return VirtualDisk.TYPE_NETWORK
if self._storage_creator:
return self._storage_creator.get_dev_type()
+ if not self.__storage_backend and skip_backend:
+ return self.TYPE_FILE
return self._storage_backend.get_dev_type()
type = XMLProperty("./@type", default_cb=_get_default_type)
@@ -602,6 +640,12 @@ class VirtualDisk(VirtualDevice):
self._source_dir = None
self.source_volume = None
self.source_pool = None
+ self.source_name = None
+ self.source_protocol = None
+ self.source_host_name = None
+ self.source_host_port = None
+ self.source_host_transport = None
+ self.source_host_socket = None
def _disk_type_to_object_prop_name(self):
disk_type = self.type
@@ -673,8 +717,11 @@ class VirtualDisk(VirtualDevice):
path = None
vol_object = None
parent_pool = None
+ is_network = False
+ typ = self._get_default_type(skip_backend=True)
+ is_network = (typ == VirtualDisk.TYPE_NETWORK)
- if self.source_pool and self.source_volume:
+ if typ == VirtualDisk.TYPE_VOLUME:
conn = self.conn
is_weak = "weakref" in str(type(conn))
if is_weak:
@@ -695,7 +742,7 @@ class VirtualDisk(VirtualDevice):
path = self._get_xmlpath()
return diskbackend.StorageBackend(self.conn, path,
- vol_object, parent_pool)
+ vol_object, parent_pool, is_network=is_network)
def _get_storage_backend(self):
if self.__storage_backend is None:
@@ -747,7 +794,8 @@ class VirtualDisk(VirtualDevice):
self.driver_type = self._get_default_driver_type()
# Need to retrigger this if self.type changed
- self._set_xmlpath(path)
+ if path:
+ self._set_xmlpath(path)
def wants_storage_creation(self):
"""
diff --git a/virtinst/diskbackend.py b/virtinst/diskbackend.py
index bbad5880f..8e795b6bb 100644
--- a/virtinst/diskbackend.py
+++ b/virtinst/diskbackend.py
@@ -373,12 +373,14 @@ class StorageBackend(_StorageBase):
Class that carries all the info about any existing storage that
the disk references
"""
- def __init__(self, conn, path, vol_object, parent_pool):
+ def __init__(self, conn, path, vol_object, parent_pool,
+ is_network=False):
_StorageBase.__init__(self, conn)
self._vol_object = vol_object
self._parent_pool = parent_pool
self._path = path
+ self._is_network = is_network
if self._vol_object is not None:
self._path = None
@@ -437,10 +439,14 @@ class StorageBackend(_StorageBase):
self._exists = True
elif self._vol_object:
self._exists = True
- elif not self._conn.is_remote() and os.path.exists(self._path):
+ elif (not self._is_network and
+ not self._conn.is_remote() and
+ os.path.exists(self._path)):
self._exists = True
elif self._parent_pool:
self._exists = False
+ elif self._is_network:
+ self._exists = True
elif (self._conn.is_remote() and
not _can_auto_manage(self._path)):
# This allows users to pass /dev/sdX and we don't try to
@@ -466,7 +472,9 @@ class StorageBackend(_StorageBase):
else:
self._dev_type = "file"
- elif self._path and not self._conn.is_remote():
+ elif (not self._is_network and
+ self._path and
+ not self._conn.is_remote()):
if os.path.isdir(self._path):
self._dev_type = "dir"
elif util.stat_disk(self._path)[0]: