2013-07-13 22:09:00 +08:00
|
|
|
#
|
|
|
|
# Storage lookup/creation helpers
|
|
|
|
#
|
2013-10-28 04:59:47 +08:00
|
|
|
# Copyright 2013 Red Hat, Inc.
|
2013-07-13 22:09:00 +08:00
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
2013-10-28 04:59:47 +08:00
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
2013-07-13 22:09:00 +08:00
|
|
|
#
|
|
|
|
# This program 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 General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
|
|
# MA 02110-1301 USA.
|
|
|
|
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
import statvfs
|
|
|
|
|
|
|
|
import libvirt
|
|
|
|
|
2014-09-13 03:59:22 +08:00
|
|
|
from . import util
|
|
|
|
from .storage import StoragePool, StorageVolume
|
2013-07-13 22:09:00 +08:00
|
|
|
|
|
|
|
|
|
|
|
def check_if_path_managed(conn, path):
|
|
|
|
"""
|
|
|
|
Determine if we can use libvirt storage APIs to create or lookup
|
|
|
|
the passed path. If we can't, throw an error
|
|
|
|
"""
|
|
|
|
vol = None
|
|
|
|
pool = None
|
|
|
|
verr = None
|
|
|
|
|
|
|
|
def lookup_vol_by_path():
|
|
|
|
try:
|
|
|
|
vol = conn.storageVolLookupByPath(path)
|
|
|
|
vol.info()
|
|
|
|
return vol, None
|
|
|
|
except libvirt.libvirtError, e:
|
|
|
|
if (hasattr(libvirt, "VIR_ERR_NO_STORAGE_VOL")
|
|
|
|
and e.get_error_code() != libvirt.VIR_ERR_NO_STORAGE_VOL):
|
|
|
|
raise
|
|
|
|
return None, e
|
|
|
|
|
|
|
|
def lookup_vol_name(name):
|
|
|
|
try:
|
|
|
|
name = os.path.basename(path)
|
|
|
|
if pool and name in pool.listVolumes():
|
|
|
|
return pool.lookupByName(name)
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
return None
|
|
|
|
|
|
|
|
vol = lookup_vol_by_path()[0]
|
|
|
|
if not vol:
|
2013-09-23 05:34:53 +08:00
|
|
|
pool = StoragePool.lookup_pool_by_path(conn, os.path.dirname(path))
|
2013-07-13 22:09:00 +08:00
|
|
|
|
2014-06-01 04:29:29 +08:00
|
|
|
# Ensure pool is running
|
2013-07-13 22:09:00 +08:00
|
|
|
if pool and pool.info()[0] != libvirt.VIR_STORAGE_POOL_RUNNING:
|
2014-06-04 04:25:48 +08:00
|
|
|
pool.create(0)
|
2013-07-13 22:09:00 +08:00
|
|
|
|
|
|
|
# Attempt to lookup path as a storage volume
|
|
|
|
if pool and not vol:
|
|
|
|
try:
|
|
|
|
# Pool may need to be refreshed, but if it errors,
|
|
|
|
# invalidate it
|
|
|
|
pool.refresh(0)
|
|
|
|
vol, verr = lookup_vol_by_path()
|
|
|
|
if verr:
|
|
|
|
vol = lookup_vol_name(os.path.basename(path))
|
|
|
|
except Exception, e:
|
|
|
|
vol = None
|
|
|
|
pool = None
|
|
|
|
verr = str(e)
|
|
|
|
|
2014-02-09 05:36:45 +08:00
|
|
|
if not vol and not pool and verr:
|
|
|
|
raise ValueError(_("Cannot use storage %(path)s: %(err)s") %
|
|
|
|
{'path' : path, 'err' : verr})
|
|
|
|
|
2014-12-05 08:52:32 +08:00
|
|
|
return vol, pool
|
2013-07-13 22:09:00 +08:00
|
|
|
|
|
|
|
|
2014-02-09 05:36:45 +08:00
|
|
|
def _can_auto_manage(path):
|
|
|
|
path = path or ""
|
|
|
|
skip_prefixes = ["/dev", "/sys", "/proc"]
|
|
|
|
|
|
|
|
for prefix in skip_prefixes:
|
|
|
|
if path.startswith(prefix + "/") or path == prefix:
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def manage_path(conn, path):
|
|
|
|
"""
|
|
|
|
If path is not managed, try to create a storage pool to probe the path
|
|
|
|
"""
|
2014-12-05 08:52:32 +08:00
|
|
|
vol, pool = check_if_path_managed(conn, path)
|
2014-02-09 05:36:45 +08:00
|
|
|
if vol or pool or not _can_auto_manage(path):
|
2014-12-05 08:36:26 +08:00
|
|
|
return vol, pool
|
2014-02-09 05:36:45 +08:00
|
|
|
|
|
|
|
dirname = os.path.dirname(path)
|
2014-04-30 02:09:14 +08:00
|
|
|
poolname = os.path.basename(dirname).replace(" ", "_")
|
|
|
|
if not poolname:
|
|
|
|
poolname = "dirpool"
|
|
|
|
poolname = StoragePool.find_free_name(conn, poolname)
|
2014-02-09 05:36:45 +08:00
|
|
|
logging.debug("Attempting to build pool=%s target=%s", poolname, dirname)
|
|
|
|
|
|
|
|
poolxml = StoragePool(conn)
|
2014-04-30 02:09:14 +08:00
|
|
|
poolxml.name = poolname
|
2014-02-09 05:36:45 +08:00
|
|
|
poolxml.type = poolxml.TYPE_DIR
|
|
|
|
poolxml.target_path = dirname
|
|
|
|
pool = poolxml.install(build=False, create=True, autostart=True)
|
|
|
|
conn.clear_cache(pools=True)
|
|
|
|
|
|
|
|
vol = None
|
|
|
|
for checkvol in pool.listVolumes():
|
|
|
|
if checkvol == os.path.basename(path):
|
|
|
|
vol = pool.storageVolLookupByName(checkvol)
|
|
|
|
break
|
|
|
|
|
2014-12-05 08:36:26 +08:00
|
|
|
return vol, pool
|
2013-07-13 22:09:00 +08:00
|
|
|
|
|
|
|
|
2013-07-26 04:21:30 +08:00
|
|
|
def build_vol_install(conn, path, pool, size, sparse):
|
2013-07-13 22:09:00 +08:00
|
|
|
# Path wasn't a volume. See if base of path is a managed
|
|
|
|
# pool, and if so, setup a StorageVolume object
|
|
|
|
if size is None:
|
|
|
|
raise ValueError(_("Size must be specified for non "
|
|
|
|
"existent volume path '%s'" % path))
|
|
|
|
|
|
|
|
logging.debug("Path '%s' is target for pool '%s'. "
|
|
|
|
"Creating volume '%s'.",
|
|
|
|
os.path.dirname(path), pool.name(),
|
|
|
|
os.path.basename(path))
|
|
|
|
|
|
|
|
cap = (size * 1024 * 1024 * 1024)
|
|
|
|
if sparse:
|
|
|
|
alloc = 0
|
|
|
|
else:
|
|
|
|
alloc = cap
|
|
|
|
|
2013-09-20 08:18:12 +08:00
|
|
|
volinst = StorageVolume(conn)
|
|
|
|
volinst.pool = pool
|
|
|
|
volinst.name = os.path.basename(path)
|
|
|
|
volinst.capacity = cap
|
|
|
|
volinst.allocation = alloc
|
2013-07-13 22:09:00 +08:00
|
|
|
return volinst
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class _StorageBase(object):
|
|
|
|
def get_size(self):
|
|
|
|
raise NotImplementedError()
|
|
|
|
def get_dev_type(self):
|
|
|
|
raise NotImplementedError()
|
|
|
|
def is_managed(self):
|
|
|
|
raise NotImplementedError()
|
|
|
|
def get_driver_type(self):
|
|
|
|
raise NotImplementedError()
|
|
|
|
|
|
|
|
|
|
|
|
class StorageCreator(_StorageBase):
|
|
|
|
def __init__(self, conn, path, pool,
|
2013-09-29 23:23:59 +08:00
|
|
|
vol_install, clone_path, backing_store,
|
2013-07-13 22:09:00 +08:00
|
|
|
size, sparse, fmt):
|
|
|
|
_StorageBase.__init__(self)
|
|
|
|
|
|
|
|
self._conn = conn
|
|
|
|
self._pool = pool
|
|
|
|
self._vol_install = vol_install
|
|
|
|
self._path = path
|
|
|
|
self._size = size
|
|
|
|
self._sparse = sparse
|
|
|
|
self._clone_path = clone_path
|
|
|
|
self.fake = False
|
|
|
|
|
|
|
|
if not self._vol_install and self._pool:
|
2013-07-26 04:21:30 +08:00
|
|
|
self._vol_install = build_vol_install(conn, path, pool,
|
2013-07-13 22:09:00 +08:00
|
|
|
size, sparse)
|
|
|
|
self._set_format(fmt)
|
2013-09-29 23:23:59 +08:00
|
|
|
self._set_backing_store(backing_store)
|
|
|
|
|
2013-07-13 22:09:00 +08:00
|
|
|
if self._vol_install:
|
|
|
|
self._path = None
|
|
|
|
self._size = None
|
|
|
|
|
|
|
|
# Cached bits
|
|
|
|
self._dev_type = None
|
|
|
|
|
|
|
|
|
|
|
|
###############
|
|
|
|
# Private API #
|
|
|
|
###############
|
|
|
|
|
|
|
|
def _set_format(self, val):
|
|
|
|
if val is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
if self._vol_install:
|
2013-09-20 08:18:12 +08:00
|
|
|
if not self._vol_install.supports_property("format"):
|
2013-07-13 22:09:00 +08:00
|
|
|
raise ValueError(_("Storage type does not support format "
|
|
|
|
"parameter."))
|
2013-09-20 08:18:12 +08:00
|
|
|
if self._vol_install.format != val:
|
|
|
|
self._vol_install.format = val
|
2013-07-13 22:09:00 +08:00
|
|
|
|
|
|
|
elif val != "raw":
|
|
|
|
raise RuntimeError(_("Format cannot be specified for "
|
|
|
|
"unmanaged storage."))
|
|
|
|
|
2013-09-29 23:23:59 +08:00
|
|
|
def _set_backing_store(self, val):
|
|
|
|
if val is None:
|
|
|
|
return
|
|
|
|
if not self._vol_install:
|
|
|
|
raise RuntimeError(_("Cannot set backing store for unmanaged "
|
|
|
|
"storage."))
|
|
|
|
self._vol_install.backing_store = val
|
|
|
|
|
2013-07-13 22:09:00 +08:00
|
|
|
|
|
|
|
##############
|
|
|
|
# Public API #
|
|
|
|
##############
|
|
|
|
|
|
|
|
def _get_path(self):
|
|
|
|
if self._vol_install and not self._path:
|
2013-09-23 05:34:53 +08:00
|
|
|
xmlobj = StoragePool(self._conn,
|
|
|
|
parsexml=self._vol_install.pool.XMLDesc(0))
|
|
|
|
self._path = (xmlobj.target_path + "/" + self._vol_install.name)
|
2013-07-13 22:09:00 +08:00
|
|
|
return self._path
|
|
|
|
path = property(_get_path)
|
|
|
|
|
|
|
|
def get_vol_install(self):
|
|
|
|
return self._vol_install
|
|
|
|
def get_sparse(self):
|
|
|
|
return self._sparse
|
|
|
|
|
|
|
|
def get_size(self):
|
2014-02-09 05:36:45 +08:00
|
|
|
if self._size is None:
|
2013-07-13 22:09:00 +08:00
|
|
|
self._size = (float(self._vol_install.capacity) /
|
|
|
|
1024.0 / 1024.0 / 1024.0)
|
|
|
|
return self._size
|
|
|
|
|
|
|
|
def get_dev_type(self):
|
|
|
|
if not self._dev_type:
|
|
|
|
if self._vol_install:
|
|
|
|
if self._vol_install.file_type == libvirt.VIR_STORAGE_VOL_FILE:
|
|
|
|
self._dev_type = "file"
|
|
|
|
else:
|
|
|
|
self._dev_type = "block"
|
|
|
|
else:
|
|
|
|
self._dev_type = "file"
|
|
|
|
return self._dev_type
|
|
|
|
|
|
|
|
def get_driver_type(self):
|
|
|
|
if self._vol_install:
|
2013-09-20 08:18:12 +08:00
|
|
|
if self._vol_install.supports_property("format"):
|
2013-07-13 22:09:00 +08:00
|
|
|
return self._vol_install.format
|
|
|
|
return "raw"
|
|
|
|
|
|
|
|
def is_managed(self):
|
|
|
|
return bool(self._vol_install)
|
|
|
|
|
|
|
|
def validate(self, device, devtype):
|
|
|
|
if device in ["floppy", "cdrom"]:
|
|
|
|
raise ValueError(_("Cannot create storage for %s device.") %
|
|
|
|
device)
|
|
|
|
|
|
|
|
if self.is_managed():
|
2013-09-20 08:18:12 +08:00
|
|
|
return self._vol_install.validate()
|
2013-07-13 22:09:00 +08:00
|
|
|
if devtype == "block":
|
|
|
|
raise ValueError(_("Local block device path '%s' must "
|
|
|
|
"exist.") % self.path)
|
|
|
|
if self._size is None:
|
|
|
|
raise ValueError(_("size is required for non-existent disk "
|
|
|
|
"'%s'" % self.path))
|
|
|
|
|
|
|
|
def is_size_conflict(self):
|
|
|
|
if self._vol_install:
|
|
|
|
return self._vol_install.is_size_conflict()
|
|
|
|
|
|
|
|
ret = False
|
|
|
|
msg = None
|
|
|
|
vfs = os.statvfs(os.path.dirname(self._path))
|
|
|
|
avail = vfs[statvfs.F_FRSIZE] * vfs[statvfs.F_BAVAIL]
|
|
|
|
need = long(self._size * 1024L * 1024L * 1024L)
|
|
|
|
if need > avail:
|
|
|
|
if self._sparse:
|
|
|
|
msg = _("The filesystem will not have enough free space"
|
|
|
|
" to fully allocate the sparse file when the guest"
|
|
|
|
" is running.")
|
|
|
|
else:
|
|
|
|
ret = True
|
|
|
|
msg = _("There is not enough free space to create the disk.")
|
|
|
|
|
|
|
|
|
|
|
|
if msg:
|
|
|
|
msg += (_(" %d M requested > %d M available") %
|
|
|
|
((need / (1024 * 1024)), (avail / (1024 * 1024))))
|
|
|
|
return (ret, msg)
|
|
|
|
|
|
|
|
|
|
|
|
#############################
|
|
|
|
# Storage creation routines #
|
|
|
|
#############################
|
|
|
|
|
|
|
|
def create(self, progresscb):
|
|
|
|
if self.fake:
|
|
|
|
raise RuntimeError("Storage creator is fake but creation "
|
|
|
|
"requested.")
|
2014-02-09 05:36:45 +08:00
|
|
|
|
2013-07-13 22:09:00 +08:00
|
|
|
# If a clone_path is specified, but not vol_install.input_vol,
|
|
|
|
# that means we are cloning unmanaged -> managed, so skip this
|
|
|
|
if (self._vol_install and
|
|
|
|
(not self._clone_path or self._vol_install.input_vol)):
|
|
|
|
return self._vol_install.install(meter=progresscb)
|
|
|
|
|
2014-02-09 05:36:45 +08:00
|
|
|
if not self._clone_path:
|
|
|
|
raise RuntimeError("Local storage creation requested, "
|
|
|
|
"this shouldn't happen.")
|
|
|
|
|
|
|
|
text = (_("Cloning %(srcfile)s") %
|
|
|
|
{'srcfile' : os.path.basename(self._clone_path)})
|
2013-07-13 22:09:00 +08:00
|
|
|
|
2014-02-09 05:36:45 +08:00
|
|
|
size_bytes = long(self.get_size() * 1024L * 1024L * 1024L)
|
2013-07-13 22:09:00 +08:00
|
|
|
progresscb.start(filename=self._path, size=long(size_bytes),
|
|
|
|
text=text)
|
|
|
|
|
2014-02-09 05:36:45 +08:00
|
|
|
# Plain file clone
|
|
|
|
self._clone_local(progresscb, size_bytes)
|
2013-07-13 22:09:00 +08:00
|
|
|
|
|
|
|
def _clone_local(self, meter, size_bytes):
|
|
|
|
if self._clone_path == "/dev/null":
|
|
|
|
# Not really sure why this check is here,
|
|
|
|
# but keeping for compat
|
|
|
|
logging.debug("Source dev was /dev/null. Skipping")
|
|
|
|
return
|
|
|
|
if self._clone_path == self._path:
|
|
|
|
logging.debug("Source and destination are the same. Skipping.")
|
|
|
|
return
|
|
|
|
|
|
|
|
# if a destination file exists and sparse flg is True,
|
|
|
|
# this priority takes a existing file.
|
|
|
|
|
|
|
|
if (not os.path.exists(self._path) and self._sparse):
|
|
|
|
clone_block_size = 4096
|
|
|
|
sparse = True
|
|
|
|
fd = None
|
|
|
|
try:
|
2014-06-29 14:46:36 +08:00
|
|
|
fd = os.open(self._path, os.O_WRONLY | os.O_CREAT, 0640)
|
2013-07-13 22:09:00 +08:00
|
|
|
os.ftruncate(fd, size_bytes)
|
|
|
|
finally:
|
|
|
|
if fd:
|
|
|
|
os.close(fd)
|
|
|
|
else:
|
|
|
|
clone_block_size = 1024 * 1024 * 10
|
|
|
|
sparse = False
|
|
|
|
|
|
|
|
logging.debug("Local Cloning %s to %s, sparse=%s, block_size=%s",
|
|
|
|
self._clone_path, self._path, sparse, clone_block_size)
|
|
|
|
|
|
|
|
zeros = '\0' * 4096
|
|
|
|
|
|
|
|
src_fd, dst_fd = None, None
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
src_fd = os.open(self._clone_path, os.O_RDONLY)
|
2014-06-29 14:46:36 +08:00
|
|
|
dst_fd = os.open(self._path, os.O_WRONLY | os.O_CREAT, 0640)
|
2013-07-13 22:09:00 +08:00
|
|
|
|
|
|
|
i = 0
|
|
|
|
while 1:
|
|
|
|
l = os.read(src_fd, clone_block_size)
|
|
|
|
s = len(l)
|
|
|
|
if s == 0:
|
|
|
|
meter.end(size_bytes)
|
|
|
|
break
|
|
|
|
# check sequence of zeros
|
|
|
|
if sparse and zeros == l:
|
|
|
|
os.lseek(dst_fd, s, 1)
|
|
|
|
else:
|
|
|
|
b = os.write(dst_fd, l)
|
|
|
|
if s != b:
|
|
|
|
meter.end(i)
|
|
|
|
break
|
|
|
|
i += s
|
|
|
|
if i < size_bytes:
|
|
|
|
meter.update(i)
|
|
|
|
except OSError, e:
|
|
|
|
raise RuntimeError(_("Error cloning diskimage %s to %s: %s") %
|
|
|
|
(self._clone_path, self._path, str(e)))
|
|
|
|
finally:
|
|
|
|
if src_fd is not None:
|
|
|
|
os.close(src_fd)
|
|
|
|
if dst_fd is not None:
|
|
|
|
os.close(dst_fd)
|
|
|
|
|
|
|
|
|
|
|
|
class StorageBackend(_StorageBase):
|
|
|
|
"""
|
|
|
|
Class that carries all the info about any existing storage that
|
|
|
|
the disk references
|
|
|
|
"""
|
2014-12-05 08:36:26 +08:00
|
|
|
def __init__(self, conn, path, vol_object):
|
2013-07-13 22:09:00 +08:00
|
|
|
_StorageBase.__init__(self)
|
|
|
|
|
|
|
|
self._conn = conn
|
|
|
|
self._vol_object = vol_object
|
|
|
|
self._path = path
|
|
|
|
|
|
|
|
if self._vol_object is not None:
|
|
|
|
self._path = None
|
|
|
|
|
|
|
|
# Cached bits
|
|
|
|
self._vol_xml = None
|
|
|
|
self._exists = None
|
|
|
|
self._size = None
|
|
|
|
self._dev_type = None
|
|
|
|
|
|
|
|
|
|
|
|
################
|
|
|
|
# Internal API #
|
|
|
|
################
|
|
|
|
|
|
|
|
def _get_vol_xml(self):
|
|
|
|
if self._vol_xml is None:
|
2013-09-23 05:34:53 +08:00
|
|
|
self._vol_xml = StorageVolume(self._conn,
|
|
|
|
parsexml=self._vol_object.XMLDesc(0))
|
2013-07-13 22:09:00 +08:00
|
|
|
return self._vol_xml
|
|
|
|
|
|
|
|
|
|
|
|
##############
|
|
|
|
# Public API #
|
|
|
|
##############
|
|
|
|
|
|
|
|
def _get_path(self):
|
|
|
|
if self._vol_object:
|
2014-09-13 01:11:40 +08:00
|
|
|
return self._get_vol_xml().target_path
|
2013-07-13 22:09:00 +08:00
|
|
|
return self._path
|
|
|
|
path = property(_get_path)
|
|
|
|
|
|
|
|
def get_vol_object(self):
|
|
|
|
return self._vol_object
|
|
|
|
|
|
|
|
def get_size(self):
|
|
|
|
"""
|
|
|
|
Return size of existing storage
|
|
|
|
"""
|
|
|
|
if self._size is None:
|
|
|
|
ret = 0
|
|
|
|
if self._vol_object:
|
2013-09-23 05:34:53 +08:00
|
|
|
ret = self._get_vol_xml().capacity
|
2013-07-13 22:09:00 +08:00
|
|
|
elif self._path:
|
|
|
|
ignore, ret = util.stat_disk(self.path)
|
|
|
|
self._size = (float(ret) / 1024.0 / 1024.0 / 1024.0)
|
|
|
|
return self._size
|
|
|
|
|
2014-02-09 05:36:45 +08:00
|
|
|
def exists(self, auto_check=True):
|
2013-07-13 22:09:00 +08:00
|
|
|
if self._exists is None:
|
|
|
|
if self.path is None:
|
|
|
|
self._exists = True
|
2014-12-05 08:36:26 +08:00
|
|
|
elif self._vol_object:
|
2013-07-13 22:09:00 +08:00
|
|
|
self._exists = True
|
|
|
|
elif not self._conn.is_remote() and os.path.exists(self._path):
|
|
|
|
self._exists = True
|
2014-02-09 05:36:45 +08:00
|
|
|
elif (auto_check and
|
|
|
|
self._conn.is_remote() and
|
|
|
|
not _can_auto_manage(self._path)):
|
|
|
|
# This allows users to pass /dev/sdX and we don't try to
|
|
|
|
# validate it exists on the remote connection, since
|
|
|
|
# autopooling /dev is perilous. Libvirt will error if
|
|
|
|
# the device doesn't exist.
|
|
|
|
self._exists = True
|
2013-07-13 22:09:00 +08:00
|
|
|
else:
|
|
|
|
self._exists = False
|
|
|
|
return self._exists
|
|
|
|
|
|
|
|
def get_dev_type(self):
|
|
|
|
"""
|
|
|
|
Return disk 'type' value per storage settings
|
|
|
|
"""
|
|
|
|
if self._dev_type is None:
|
|
|
|
if self._vol_object:
|
|
|
|
t = self._vol_object.info()[0]
|
|
|
|
if t == libvirt.VIR_STORAGE_VOL_FILE:
|
|
|
|
self._dev_type = "file"
|
|
|
|
elif t == libvirt.VIR_STORAGE_VOL_BLOCK:
|
|
|
|
self._dev_type = "block"
|
|
|
|
else:
|
|
|
|
self._dev_type = "file"
|
|
|
|
|
2014-02-09 05:36:45 +08:00
|
|
|
elif self._path and not self._conn.is_remote():
|
2013-07-13 22:09:00 +08:00
|
|
|
if os.path.isdir(self._path):
|
|
|
|
self._dev_type = "dir"
|
|
|
|
elif util.stat_disk(self._path)[0]:
|
|
|
|
self._dev_type = "file"
|
|
|
|
else:
|
|
|
|
self._dev_type = "block"
|
|
|
|
|
|
|
|
if not self._dev_type:
|
|
|
|
self._dev_type = "block"
|
|
|
|
return self._dev_type
|
|
|
|
|
|
|
|
def get_driver_type(self):
|
|
|
|
if self._vol_object:
|
2013-09-23 05:34:53 +08:00
|
|
|
return self._get_vol_xml().format
|
2013-07-13 22:09:00 +08:00
|
|
|
return None
|
|
|
|
|
|
|
|
def is_managed(self):
|
2014-12-05 08:36:26 +08:00
|
|
|
return bool(self._vol_object)
|