diff --git a/virttest/virt_storage/__init__.py b/virttest/virt_storage/__init__.py new file mode 100755 index 00000000000..e69de29bb2d diff --git a/virttest/virt_storage/backend/__init__.py b/virttest/virt_storage/backend/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/virttest/virt_storage/backend/base.py b/virttest/virt_storage/backend/base.py new file mode 100644 index 00000000000..154c8c2d323 --- /dev/null +++ b/virttest/virt_storage/backend/base.py @@ -0,0 +1,133 @@ +import re +import uuid + +from virttest.virt_storage import utils +from virttest.virt_storage import virt_source +from virttest.virt_storage import virt_target + + +class BaseStoragePool(object): + TYPE = "none" + + def __init__(self, name): + self.name = name + self.uuid = uuid.uuid1() + self.source = None + self.target = None + self._capacity = None + self._available = None + self._helper = None + self._volumes = set() + + @property + def capacity(self): + if self._capacity is None: + self._capacity = self.helper.capacity + return self._capacity + + @property + def available(self): + if self._available is None: + self._available = self.helper.available + return self._available + + @property + def helper(self): + if self._helper is None: + self._helper = utils.get_pool_helper(self) + return self._helper + + @classmethod + def pool_define_by_params(cls, name, params): + inst = cls(name) + inst.target = virt_target.PoolTarget.target_define_by_params(params) + if params.get("source"): + source_params = params.object_params(params.get("source")) + inst.source = virt_source.PoolSource.source_define_by_params( + params.get("source"), source_params) + inst.set_special_opts_by_params(params) + return inst + + def set_special_opts_by_params(self, params): + pattern = re.compile(r"(\w+)\s*=(\w+)\s*") + options = params.get("config_opts", "").split(",") + for option in options: + match = pattern.search(option) + if match: + key, val = match.groups() + setattr(self, key, val) + + def start(self): + raise NotImplementedError + + def stop(self): + raise NotImplementedError + + def destroy(self): + """Destroy storage pools""" + self.stop() + self._volumes.clear() + + def find_sources(self): + raise NotImplementedError + + def create_volume(self, volume): + raise NotImplementedError + + def refresh(self): + raise NotImplementedError + + def remove_volume(self, volume): + raise NotImplementedError + + def find_volume_by_name(self, name): + """find volume by name""" + return self.__find_volume_by_attr("name", name) + + def find_volume_by_path(self, path): + """find volume by path""" + return self.__find_volume_by_attr("path", path) + + def find_volume_by_key(self, key): + """find volume by key""" + return self.__find_volume_by_attr("key", key) + + def find_volume_by_url(self, url): + """find volume by url""" + return self.__find_volume_by_attr("url", url) + + def __find_volume_by_attr(self, attr, val): + """ + Find the volume attribute is match given value + + :param attr: attribute name + :param val: attribute value + :return: StorageVolume object or None + :raise: + """ + + matched_volumes = filter( + lambda x: str( + getattr( + x, + attr)) == str(val), + self.get_volumes()) + return matched_volumes[0] if matched_volumes else None + + def get_volumes(self): + return self._volumes + + def add_volume(self, volume): + self._volumes.add(volume) + + def acquire_volume(self, volume): + if volume.is_allocated: + return + self.create_volume(volume) + self.refresh() + + def info(self): + return utils.get_instance_info(self) + + def __str__(self): + return "%s:%s" % (self.__class__.__name__, self.name) diff --git a/virttest/virt_storage/backend/direct_iscsi.py b/virttest/virt_storage/backend/direct_iscsi.py new file mode 100644 index 00000000000..91eaa01339e --- /dev/null +++ b/virttest/virt_storage/backend/direct_iscsi.py @@ -0,0 +1,66 @@ +from avocado.core import exceptions + +from virttest.virt_storage import storage_volume +from virttest.virt_storage.backend import base +from virttest.virt_storage.utils import storage_util + + +class IscsiDriectPool(base.BaseStoragePool): + TYPE = "iscsi-direct" + + def find_sources(self): + """find lun in iscsi target""" + # Used host path as key of the volume + return self.helper.list_disks() + + def start(self): + self.helper.login() + self.refresh() + + def stop(self): + self.helper.logout() + + def refresh(self): + for path in self.find_sources(): + if self.find_volume_by_path(path): + continue + else: + self.create_volume_from_path(path) + + def create_volume_from_path(self, path): + capacity = self.helper.get_size(path) + url = self.helper.path_to_url(path) + volume = storage_volume.StorageVolume(self) + volume.path = path + volume.url = url + volume.capacity = capacity + volume.is_allocated = True + return volume + + def create_volume(self, volume): + """map exists lun to volume object""" + vol = self.__find_appropriate_lun(volume) + volume.path = vol.path + volume.url = vol.url + self._volumes.remove(vol) + storage_util.create_volume(volume) + volume.is_allocated = True + return volume + + def remove_volume(self, volume): + self._volumes.remove(volume) + + def __find_appropriate_lun(self, vol): + """find appropriate lun for logical volume""" + volumes = filter( + lambda x: x.capacity - vol.capacity >= 0 and x.name is None, self._volumes) + try: + return sorted(volumes, key=lambda x: x.capacity)[0] + except Exception: + raise exceptions.TestError( + "No appropriate lun found for volume %s: %s" % + (vol.name, vol.info())) + + @property + def available(self): + return sum(filter(lambda x: x.url and x.name is None, self._volumes)) diff --git a/virttest/virt_storage/backend/directory.py b/virttest/virt_storage/backend/directory.py new file mode 100644 index 00000000000..e7127805eac --- /dev/null +++ b/virttest/virt_storage/backend/directory.py @@ -0,0 +1,51 @@ +from virttest.virt_storage import storage_volume +from virttest.virt_storage.backend import base +from virttest.virt_storage.utils import storage_util + + +class DirectoryPool(base.BaseStoragePool): + TYPE = "directory" + + def find_sources(self): + return self.helper.list_files() + + def start(self): + self.helper.create() + self.refresh() + + def stop(self): + pass + + def delete(self): + self.helper.remove() + + def refresh(self): + files = filter( + lambda x: not self.find_volume_by_path, + self.find_sources()) + return map(self.create_volume_from_local, files) + + def create_volume_from_local(self, path): + """ + Create logical volume from local file + file size maybe mismatch, but need to resize in here + it will be recreate by qemu-img in next step. + + """ + volume = storage_volume.StorageVolume(self) + volume.path = path + volume.url = self.helper.get_url(path) + volume.capacity = self.helper.get_size(path) + volume.is_allocated = True + return volume + + def create_volume(self, volume): + if volume.path is None: + volume.path = self.helper.get_path_by_name(volume.name) + storage_util.create_volume(volume) + volume.is_allocated = True + return volume + + def remove_volume(self, volume): + self.helper.remove_file(volume.path) + self._volumes.remove(volume) diff --git a/virttest/virt_storage/backend/gluster.py b/virttest/virt_storage/backend/gluster.py new file mode 100644 index 00000000000..d9c23191e6f --- /dev/null +++ b/virttest/virt_storage/backend/gluster.py @@ -0,0 +1,32 @@ +from virttest.virt_storage import storage_volume +from virttest.virt_storage.backend import nfs +from virttest.virt_storage.utils import storage_util + + +class GlusterPool(nfs.NfsPool): + TYPE = "gluster" + + def find_sources(self): + urls = list() + for path in self.helper.list_files(): + urls.append(self.helper.get_url(path)) + return urls + + def create_volume_from_remote(self, url): + volume = storage_volume.StorageVolume(self) + volume.url = volume.path = url + volume.capacity = self.helper.get_size(url) + volume.is_allocated = True + return volume + + def remove_volume(self, volume): + self.helper.remove_image(volume.url) + self._volumes.remove(volume) + + def create_volume(self, volume): + if volume.url is None: + url = self.helper.get_url_by_name(volume.name) + volume.url = volume.path = url + storage_util.create_volume(volume) + volume.is_allocated = True + return volume diff --git a/virttest/virt_storage/backend/nfs.py b/virttest/virt_storage/backend/nfs.py new file mode 100644 index 00000000000..f09bc6f6ac6 --- /dev/null +++ b/virttest/virt_storage/backend/nfs.py @@ -0,0 +1,45 @@ +from virttest.virt_storage import storage_volume +from virttest.virt_storage.backend import directory +from virttest.virt_storage.utils import storage_util + + +class NfsPool(directory.DirectoryPool): + TYPE = "nfs" + + def find_sources(self): + files = super(NfsPool, self).find_sources() + return map(self.helper.get_url, files) + + def start(self): + self.helper.mount() + self.refresh() + + def stop(self): + return self.helper.umount() + + def delete(self): + self.helper.remove() + + def refresh(self): + urls = filter( + lambda x: not self.find_volume_by_url, + self.find_sources()) + return map(self.create_volume_from_remote, urls) + + def create_volume_from_remote(self, url): + volume = storage_volume.StorageVolume(self) + volume.url = url + volume.path = self.helper.get_path(url) + volume.capacity = self.helper.get_size(volume.path) + volume.is_allocated = True + return volume + + def create_volume(self, volume): + if volume.url is None: + url = self.helper.get_url_by_name(volume.name) + volume.url = url + path = self.helper.get_path(volume.url) + volume.path = path + storage_util.create_volume(volume) + volume.is_allocated = True + return volume diff --git a/virttest/virt_storage/backend/rbd.py b/virttest/virt_storage/backend/rbd.py new file mode 100755 index 00000000000..a663de8272c --- /dev/null +++ b/virttest/virt_storage/backend/rbd.py @@ -0,0 +1,42 @@ +from virttest.virt_storage import storage_volume +from virttest.virt_storage.backend import base +from virttest.virt_storage.utils import storage_util + + +class RbdPool(base.BaseStoragePool): + TYPE = 'rbd' + + def find_sources(self): + return map(self.helper.get_url_by_name, self.helper.list_images()) + + def start(self): + self.helper.connect() + self.refresh() + + def stop(self): + return self.helper.shutdown() + + def refresh(self): + urls = filter(lambda x: not self.find_volume_by_url(x), self.find_sources()) + return list(map(self.create_volume_from_remote, urls)) + + def create_volume_from_remote(self, url): + volume = storage_volume.StorageVolume(self) + volume.path = volume.url = url + volume.capacity = self.helper.get_size(url) + volume.is_allocated = True + return volume + + def create_volume(self, volume): + if volume.url is None: + url = self.helper.get_url_by_name(volume.name) + volume.url = volume.path = url + if volume.is_allocated: + self.helper.remove_image(volume.url) + storage_util.create_volume(volume) + volume.is_allocated = True + return volume + + def remove_volume(self, volume): + self.helper.remove_image(volume.url) + self._volumes.remove(volume) diff --git a/virttest/virt_storage/driver/__init__.py b/virttest/virt_storage/driver/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/virttest/virt_storage/driver/fscli.py b/virttest/virt_storage/driver/fscli.py new file mode 100755 index 00000000000..44a604282f6 --- /dev/null +++ b/virttest/virt_storage/driver/fscli.py @@ -0,0 +1,88 @@ +import logging +import os +import shutil + +from avocado.utils import process + + +def get_pool_helper(pool): + return FsCli(pool.target.path) + + +class FsCli(object): + + def __init__(self, dir_path): + self.dir_path = dir_path + self._is_export = None + + def create(self): + if not self.is_exists: + os.makedirs(self.dir_path) + else: + logging.warn("Dir '%s' is exists!" % self.dir_path) + self._is_export = True + + def remove(self): + if os.path.isdir(self.dir_path): + shutil.rmtree(self.dir_path) + self._is_export = False + + @staticmethod + def remove_file(path): + return process.system("rm -f %s" % path, shell=True) + + def get_path_by_name(self, name): + return os.path.join(self.dir_path, name) + + def get_key_by_name(self, name): + return self.get_path_by_name(name) + + def list_files(self, _root=None): + """List all files in top directory""" + + def _list_files(_dir): + for root, dirs, files in os.walk(_dir): + for f in files: + yield os.path.join(root, f) + for d in dirs: + _d = os.path.join(root, d) + _list_files(_d) + + root_dir = _root or self.dir_path + return _list_files(root_dir) + + @staticmethod + def get_size(f): + """Get file size""" + try: + return os.path.getsize(f) + except OSError: + return 0 + + @staticmethod + def get_path(f): + """Get file realpath""" + return os.path.realpath(f) + + @staticmethod + def get_url(f): + """Get url schema path""" + return "file://%s" % os.path.realpath(f) + + @property + def is_exists(self): + if self._is_export is None: + self._is_export = os.path.isdir(self.dir_path) + return self._is_export + + @property + def capacity(self): + cmd = "df -k --output=size %s |tail -n1" % self.dir_path + output = process.system_output(cmd, shell=True) + return int(output) * 1024 + + @property + def available(self): + cmd = "df -k --output=avail %s |tail -n1" % self.dir_path + output = process.system_output(cmd, shell=True) + return int(output) * 1024 diff --git a/virttest/virt_storage/driver/glustercli.py b/virttest/virt_storage/driver/glustercli.py new file mode 100755 index 00000000000..9063f39efa8 --- /dev/null +++ b/virttest/virt_storage/driver/glustercli.py @@ -0,0 +1,96 @@ +import os + +from gluster import gfapi + + +def get_pool_helper(pool): + volname = pool.source.name + root_dir = pool.source.dir_path or '/' + host = pool.source.hosts[0].hostname + port = pool.source.hosts[0].port or 24007 + return GlusterCli(host, volname, port, root_dir) + + +class GlusterCli(object): + + def __init__(self, host, volname, port=24007, root_dir='/'): + self.root_dir = root_dir + self.host = host + self.volume = gfapi.Volume(host, volname, port=port) + self._is_mounted = None + self._base_url = "gluster://%s:%s/%s" % ( + self.host, self.volume.port, self.volume.volname) + + def list_files(self): + def _list_files(_dir): + for root, dirs, files in self.volume.walk(_dir): + for f in files: + yield os.path.join(root, f) + for d in dirs: + _list_files(os.path.join(root, d)) + + self.mount() + if not self.volume.isdir(self.root_dir): + self.volume.makedirs(self.root_dir) + return _list_files(self.root_dir) + + def get_path(self, url): + return url[len(self._base_url):] + + def get_url(self, path): + return self._base_url + path + + def get_url_by_name(self, name): + path = os.path.join(self.root_dir, name) + return "%s%s" % (self._base_url, path) + + def get_key_by_name(self, name): + return self.get_url_by_name(name) + + def get_size(self, url): + path = url.lstrip(self._base_url) + try: + return self.volume.getsize(path) + except OSError: + return 0 + + def refresh(self): + if self.volume.mounted: + self.volume.umount() + self.volume.mount() + self.volume.umount() + + def mount(self): + if not self.is_mounted: + self.volume.mount() + self._is_mounted = True + + def umount(self): + if self.is_mounted: + self.volume.umount() + self._is_mounted = False + + def remove(self): + self.mount() + self.volume.rmtree(self.root_dir, ignore_errors=True) + + def remove_image(self, url): + self.mount() + path = self.get_path(url) + self.volume.remove(path) + + @property + def is_mounted(self): + if self._is_mounted is None: + self._is_mounted = self.volume.mounted + return self._is_mounted + + @property + def capacity(self): + stat = self.volume.statvfs(self.root_dir) + return stat.f_blocks * stat.f_frsize + + @property + def available(self): + stat = self.volume.statvfs(self.root_dir) + return stat.f_bfree * stat.f_frsize diff --git a/virttest/virt_storage/driver/iscsicli.py b/virttest/virt_storage/driver/iscsicli.py new file mode 100755 index 00000000000..12d24cc4e4c --- /dev/null +++ b/virttest/virt_storage/driver/iscsicli.py @@ -0,0 +1,127 @@ +import re + +from avocado.utils import genio +from avocado.utils import process +from avocado.utils import service + + +def get_pool_helper(pool): + host = pool.source.hosts[0].hostname + port = pool.source.hosts[0].port or 3260 + target = pool.source.devices[0].path + initiator = pool.source.initiator + auth = pool.source.auth + return IscsiCli(host, port, target, initiator, auth) + + +class IscsiCli(object): + dev_root = '/dev/disk/by-path/' + initiator_file = "/etc/iscsi/initiatorname.iscsi" + iscsi_service = service.SpecificServiceManager("iscsi") + + def __init__(self, host, port=3260, + target=None, initiator=None, auth=None): + self.portal = "%s:%s" % (host, port) + self.target = target + self.initiator = initiator + self.auth = auth + self._is_logged_in = False + if self.auth: + if self.auth.type == "chap": + cmd = ("iscsiadm -m node --targetname %s -p %s -o update -n discovery.sendtargets.auth.authmethod" + " -v %s" % (self.target, self.portal, "CHAP")) + process.system(cmd, shell=True, verbose=True) + if self.auth.username: + cmd = "iscsiadm -m node --targetname %s -p %s -o update -n node.session.auth.username -v %s" % ( + self.target, self.portal, self.auth.username) + process.system(cmd, shell=True, verbose=True) + if self.auth.password: + cmd = "iscsiadm -m node --targetname %s -p %s -o update -n node.session.auth.password -v %s" % ( + self.target, self.portal, self.auth.password) + process.system(cmd, shell=True, verbose=True) + if self.initiator: + context = "InitiatorName=%s" % self.initiator + genio.write_one_line(self.initiator_file, context) + self.iscsi_service.restart() + + def discovery_all_targets(self): + targets = list() + cmd = "iscsiadm -m discovery --type sendtargets -p %s" % self.portal + for line in process.system_output( + cmd, shell=True, verbose=True).splitlines(): + target = line.split()[1] + targets.append(target) + return targets + + def login(self): + if not self.is_logged: + targets = self.discovery_all_targets() + if self.target is None and targets: + self.target = targets[0] + assert self.target in self.discovery_all_targets( + ), "No target '%s' not discovey" % self.target + cmd = "iscsiadm -m node --targetname %s -p %s -l" % ( + self.target, self.portal) + process.system(cmd, shell=True, verbose=True) + self._is_logged_in = True + + def list_disks(self): + self.login() + cmd = "ls %s" % self.dev_root + dev_regex = r"ip-%s-iscsi-%s-lun-\d+" % (self.portal, self.target) + output = process.system_output(cmd, shell=True, verbose=True) + dev_pattern = re.compile(dev_regex, re.M | re.I) + return map(lambda x: "%s/%s" % + (self.dev_root, x), dev_pattern.findall(output)) + + def logout(self): + if self.is_logged: + cmd = "iscsiadm -m node --targetname %s -p %s -u" % ( + self.target, self.portal) + process.system(cmd, shell=True, verbose=True) + self._is_logged_in = False + + @staticmethod + def get_size(path): + cmd = "blockdev --getsize64 '%s'" % path + try: + return int(process.system_output(cmd, shell=True, verbose=True)) + except process.CmdError: + return 0 + + def path_to_url(self, path): + match = re.search( + r"ip-(?P.*)-iscsi-(?P.*)-lun-(?P\d+)", path) + if match: + portal = match.groupdict().get("portal") + target = match.groupdict().get("target") + lun = match.groupdict().get("lun") + secret = "" + if self.auth: + if self.auth.username: + secret += self.auth.username + if self.auth.password: + secret += ":%s" % self.auth.password + if secret: + secret += "@" + return "iscsi://%s%s/%s/%s" % (secret, portal, target, lun) + + return None + + @property + def is_logged(self): + if self._is_logged_in is None: + cmd = "iscsiadm -m session " + output = process.system_output( + cmd, shell=True, ignore_status=True, verbose=True) + for line in output.splitlines(): + if self.portal in line and self.target in line: + self._is_logged_in = True + break + else: + self._is_logged_in = False + return self._is_logged_in + + @property + def capacity(self): + return sum(map(self.get_size, self.list_disks())) diff --git a/virttest/virt_storage/driver/nfscli.py b/virttest/virt_storage/driver/nfscli.py new file mode 100755 index 00000000000..849bb427b56 --- /dev/null +++ b/virttest/virt_storage/driver/nfscli.py @@ -0,0 +1,94 @@ +import os +import tempfile + +from avocado.utils import process + +import fscli + + +def get_pool_helper(pool): + target = pool.target.path + dir_path = pool.source.dir_path + host = pool.source.hosts[0].hostname + return NfsCli(host, dir_path, target) + + +class NfsCli(fscli.FsCli): + + def __init__(self, host, dir_path, target=None): + self.host = host + self.export_dir = dir_path + self._target = target + self._is_mounted = None + self._is_export = None + super(NfsCli, self).__init__(self.target) + + @property + def target(self): + if self._target is None: + self._target = tempfile.mkdtemp() + if not os.path.isdir(self._target): + os.makedirs(self._target) + return self._target + + @target.setter + def target(self, _target): + self._target = _target + + @property + def src_path(self): + return "%s:%s" % (self.host, self.export_dir) + + @property + def is_mounted(self): + if self._is_mounted is None: + cmd = "grep '%s' /proc/mounts |grep %s" % ( + self.src_path, self.target) + ret = process.system(cmd, shell=True, ignore_status=True) + self._is_mounted = ret == 0 + return self._is_mounted + + def umount(self): + if self.is_mounted: + cmd = "umount -f %s" % self.target + process.system(cmd, shell=True, ignore_status=False) + self._is_mounted = False + + def mount(self): + assert self.is_export, "'%s' not export in host '%s'" % ( + self.export_dir, self.host) + if not os.path.isdir(self.target): + os.makedirs(self.target) + if self.is_mounted: + return + cmd = "mount -t nfs %s %s" % (self.src_path, self.target) + process.system(cmd, shell=True, ignore_status=False) + self._is_mounted = True + + def get_url(self, f): + return f.replace(self.target, "nfs://%s" % self.src_path) + + def get_path(self, url): + return url.replace("nfs://%s" % self.src_path, self.target) + + def get_url_by_name(self, name): + return "nfs://%s/%s" % (self.src_path, name) + + def get_key_by_name(self, name): + return self.get_url_by_name(name) + + @property + def is_export(self): + if self._is_export is None: + cmd = "showmount --no-headers -e %s |grep %s" % ( + self.host, self.export_dir) + ret = process.system(cmd, shell=True, ignore_status=True) + self._is_export = ret == 0 + return self._is_export + + def remove(self): + if self.is_mounted: + cmd = "rm -rf %s/*" % self.target + process.system(cmd, shell=True) + self.umount() + super(NfsCli, self).remove() diff --git a/virttest/virt_storage/driver/rbdcli.py b/virttest/virt_storage/driver/rbdcli.py new file mode 100755 index 00000000000..554d4b43491 --- /dev/null +++ b/virttest/virt_storage/driver/rbdcli.py @@ -0,0 +1,112 @@ +import tempfile +import time + +import rados +import rbd + + +def get_pool_helper(pool): + if not pool.source.hosts: + ceph_conf = '/etc/ceph/ceph.conf' + ceph_keyring = '/etc/ceph/ceph.keyring' + else: + key = pool.source.auth.password + mon_host = pool.source.hosts[0].hostname + user = pool.source.auth.username or "client.admin" + ceph_conf = tempfile.mktemp() + ceph_keyring = tempfile.mktemp() + + with open(ceph_conf, "w") as fd: + fd.writelines(["[global]", "mon host = %s" % mon_host]) + with open(ceph_keyring, "w") as fd: + fd.writelines(["[%s]" % user, "key = %s" % key]) + + conf = {"keyring": ceph_keyring} + pool_name = pool.source.name + return RbdCli(ceph_conf, conf, pool_name) + + +class RbdCli(object): + def __init__(self, conffile, conf, pool): + self.cluster = rados.Rados(conffile=conffile, conf=conf) + self._pool = pool + self._is_connect = False + self._base_url = "rbd:%s/" % self._pool + + def list_images(self): + self.connect() + if self.cluster.pool_exists(self._pool): + with self.cluster.open_ioctx(self._pool) as ioctx: + return sorted(rbd.RBD().list(ioctx)) + return [] + + def get_url_by_name(self, image): + return "%s%s" % (self._base_url, image) + + def get_size(self, image): + if not self.is_image_exists(image): + return 0 + name = self._get_image_name(image) + with self.cluster.open_ioctx(self._pool) as ioctx: + time.sleep(0.3) + with rbd.Image(ioctx, name) as rbd_image: + return rbd_image.size() + + def create_image(self, image, size): + if self.is_image_exists(image): + return + name = self._get_image_name(image) + with self.cluster.open_ioctx(self._pool) as ioctx: + rbd_inst = rbd.RBD() + rbd_inst.create(ioctx, name, size) + + def remove_image(self, image, timeout=120): + if not self.is_image_exists(image): + return + name = self._get_image_name(image) + with self.cluster.open_ioctx(self._pool) as ioctx: + rbd_inst = rbd.RBD() + start = time.time() + end = start + timeout + while time.time() < end: + try: + rbd_inst.remove(ioctx, name) + except rbd.ImageBusy: + time.sleep(0.3) + except rbd.ImageNotFound: + break + + def is_image_exists(self, path): + name = self._get_image_name(path) + return name in self.list_images() + + def _get_image_name(self, path): + if path.startswith(self._base_url): + return path.lstrip(self._base_url) + return path + + def connect(self): + if not self.is_connected: + self.cluster.connect() + self._is_connect = True + + def shutdown(self): + if self.is_connected: + self.cluster.shutdown() + self._is_connect = False + + @property + def is_connected(self): + return self._is_connect + + @property + def capacity(self): + self.connect() + stats = self.cluster.get_cluster_stats() + return stats['kb'] * 1024 + + @property + def available(self): + self.connect() + stats = self.cluster.get_cluster_stats() + return stats['kb_avail'] * 1024 diff --git a/virttest/virt_storage/exception.py b/virttest/virt_storage/exception.py new file mode 100755 index 00000000000..e377410a020 --- /dev/null +++ b/virttest/virt_storage/exception.py @@ -0,0 +1,10 @@ +class UnsupportedStoragePoolException(Exception): + + def __init__(self, sp_manager, sp_type): + self.sp_manager = sp_manager + self.sp_type = sp_type + self.message = "Unsupported StoragePool type '%s', supported type are: %s" % ( + self.sp_type, sp_manager.supported_storage_backend.keys()) + + def __str__(self): + return "UnsupportedStoragePoolException:%s" % self.message diff --git a/virttest/virt_storage/storage_admin.py b/virttest/virt_storage/storage_admin.py new file mode 100755 index 00000000000..a842d19bcc5 --- /dev/null +++ b/virttest/virt_storage/storage_admin.py @@ -0,0 +1,211 @@ +import logging +from functools import reduce + +from virttest.virt_storage import exception +from virttest.virt_storage import storage_volume +from virttest.virt_storage import virt_encryption +from virttest.virt_storage.backend import directory, direct_iscsi, nfs, rbd, gluster +from virttest.virt_storage.utils import state + + +class StoragePoolAdmin(object): + supported_storage_backend = { + "directory": directory.DirectoryPool, + "nfs": nfs.NfsPool, + "gluster": gluster.GlusterPool, + "iscsi-direct": direct_iscsi.IscsiDriectPool, + "rbd": rbd.RbdPool + } + + __pools = set() + + @classmethod + def _find_storage_driver(cls, backend_type): + try: + return cls.supported_storage_backend[backend_type] + except KeyError: + raise exception.UnsupportedStoragePoolException(cls, backend_type) + + @classmethod + def pool_define_by_params(cls, name, params): + """ + Define logical storage pool object by test params, + initial status of the pool is dead. + + :param params: pool params object + :param name: storage pool name + + :return StoragePool object + """ + driver = cls._find_storage_driver(params["storage_type"]) + pool = driver.pool_define_by_params(name, params) + pool.refresh() + state.register_pool_state_machine(pool) + cls.__pools.add(pool) + return pool + + @classmethod + def pools_define_by_params(cls, params): + lst_names = params.objects("storage_pools") + lst_params = map(params.object_params, lst_names) + return map(lambda x: cls.pool_define_by_params( + *x), zip(lst_names, lst_params)) + + @classmethod + def list_volumes(cls): + """List all volumes in host""" + return reduce(lambda x, y: x.union( + y), [p.get_volumes() for p in sp_admin.list_pools()]) + + @classmethod + def list_pools(cls): + return cls.__pools + + @classmethod + def find_pool_by_name(cls, name): + for pool in cls.__pools: + if pool.name == name: + return pool + return None + + @staticmethod + def find_pool_by_volume(volume): + return volume.pool + + @classmethod + def find_pool_by_path(cls, path): + try: + return filter(lambda x: x.target.path == path, cls.list_pools())[0] + except IndexError: + logging.warn("no storage pool with matching path '%s'" % path) + return None + + @staticmethod + def start_pool(pool): + return pool.start_pool() + + @staticmethod + def stop_pool(pool): + return pool.stop_pool() + + @staticmethod + def destroy_pool(pool): + return pool.destroy_pool() + + @staticmethod + def refresh_pool(pool): + return pool.refresh() + + @classmethod + def release_volume(cls, volume): + pool = cls.find_pool_by_volume(volume) + pool.release_volume(volume) + + @classmethod + def volumes_define_by_params(cls, params): + return map(lambda x: cls.volume_define_by_params( + x, params), params.objects("images")) + + @classmethod + def volume_define_by_params(cls, volume_name, test_params): + """ + params: full test params + """ + + def _get_pool_by_params(name, pool_params): + """Get pool by params if it's really defined return it else define a new pool""" + pool = cls.find_pool_by_name(name) + if not pool: + pool = cls.pool_define_by_params(name, pool_params) + return pool + + def _get_volume_by_params(pool, name, volume_params): + """Get volume object by test params if it's defined else return a new volume""" + volume = None + if pool.TYPE == "iscsi-direct": + url = volume_params.get("image_url") + if url: + volume = cls.get_volume_by_url(url) + elif pool.TYPE in ("gluster", "nfs"): + url = volume_params.get("image_url") + if not url: + url = pool.helper.get_url_by_name(name) + if url: + volume = cls.get_volume_by_url(url) + elif pool.TYPE == "directory": + path = volume_params.get("image_path") + if not path: + path = pool.helper.get_path_by_name(name) + if path: + volume = cls.get_volume_by_path(path) + if not volume: + volume = storage_volume.StorageVolume(pool, name=name) + return volume + + def _volume_define_by_params(name, params): + """Get volume object by params""" + + volume_params = params.object_params(name) + pool_name = volume_params.get("storage_pool") + pool_params = params.object_params(pool_name) + pool = _get_pool_by_params(pool_name, pool_params) + volume = _get_volume_by_params(pool, name, volume_params) + volume.name = name + volume.format = volume_params.get("image_format", "raw") + volume.capacity = volume_params.get("image_size", "100M") + volume.preallocation = volume_params.get("preallocation", "off") + + encryption_on = volume_params.get( + "image_encryption", "off") != "off" + if encryption_on: + encryption = virt_encryption.VolumeEncryption.encryption_define_by_params( + volume_params) + volume.encryption = encryption + + backing_name = volume_params.get("backing", None) + if backing_name: + backing_store = cls.get_volume_by_name(backing_name) + if not backing_store: + backing_store = _volume_define_by_params( + backing_name, params) + volume.backing_store = backing_store + else: + volume.backing_store = None + return volume + + return _volume_define_by_params(volume_name, test_params) + + @classmethod + def acquire_volume(cls, volume): + def _acquire_volume(vol): + if vol.is_allocated: + return + if vol.backing_store: + _acquire_volume(vol.backing_store) + pool = cls.find_pool_by_volume(vol) + pool.acquire_volume(vol) + + _acquire_volume(volume) + + @classmethod + def remove_volume(cls, volume): + pool = cls.find_pool_by_volume(volume) + pool.remove_volume(volume) + + @classmethod + def get_volume_by_name(cls, name): + volumes = filter(lambda x: x.name == name, cls.list_volumes()) + return volumes[0] if volumes else None + + @classmethod + def get_volume_by_path(cls, path): + volumes = filter(lambda x: x.path == path, cls.list_volumes()) + return volumes[0] if volumes else None + + @classmethod + def get_volume_by_url(cls, url): + volumes = filter(lambda x: x.url == url, cls.list_volumes()) + return volumes[0] if volumes else None + + +sp_admin = StoragePoolAdmin() diff --git a/virttest/virt_storage/storage_volume.py b/virttest/virt_storage/storage_volume.py new file mode 100755 index 00000000000..761172e75f4 --- /dev/null +++ b/virttest/virt_storage/storage_volume.py @@ -0,0 +1,88 @@ +from virttest import utils_misc +from virttest.virt_storage import utils + + +class StorageVolume(object): + + def __init__(self, pool, name=None, _format="raw"): + self.name = name + self.pool = pool + self.url = None + self.path = None + self.format = _format + self._capacity = None + self._backing_store = None + self._key = None + self._auth = None + self.is_allocated = None + self.encryption = None + self.preallocation = None + self.used_by = [] + self.pool.add_volume(self) + + @property + def key(self): + if self._key is None: + if self.pool.TYPE in ("directory", "nfs"): + self._key = self.path + else: + self._key = self.url + return self._key + + @key.setter + def key(self, key): + self._key = key + + @property + def capacity(self): + if self._capacity is None: + if self.key: + driver = self.pool.helper + self._capacity = driver.get_size(self.key) + return int(self._capacity) + + @capacity.setter + def capacity(self, size): + self._capacity = float( + utils_misc.normalize_data_size( + str(size), 'B', '1024')) + + @property + def backing_store(self): + return self._backing_store + + @backing_store.setter + def backing_store(self, backing): + if self.format == "qcow2": + self._backing_store = backing + else: + self._backing_store = None + + @property + def auth(self): + if self._auth is None: + self._auth = self.pool.source.auth + return self._auth + + def info(self): + return utils.get_instance_info(self) + + def generate_qemu_img_options(self): + options = " -f %s" % self.format + if self.format == "qcow2" and self.backing_store: + options += " -b %s" % self.backing_store.key + if self.encryption: + secret = self.encryption.secret + encryption_format = self.encryption.format + options += " --object secret,data=%s,id=%s" % ( + secret.data, secret.name) + options += " -o encrypt.format=%s,encrypt.key-secret=%s" % ( + encryption_format, secret.name) + return options + + def __str__(self): + return "%s: %s, %s" % (self.__class__.__name__, + self.name, str(self.key)) + + def __eq__(self, vol): + return self.info() == vol.info() diff --git a/virttest/virt_storage/unit_test.py b/virttest/virt_storage/unit_test.py new file mode 100755 index 00000000000..1acf2c72653 --- /dev/null +++ b/virttest/virt_storage/unit_test.py @@ -0,0 +1,201 @@ +import unittest + +from virttest import utils_params +from virttest.virt_storage.storage_admin import sp_admin + + +class VirtStorageTest(unittest.TestCase): + + def setUp(self): + params = utils_params.Params() + params["storage_pools"] = "sp1 sp2 sp3 sp4 sp5" + params["storage_type_sp1"] = "directory" + params["target_path_sp1"] = "/tmp/avocado" + + params["storage_type_sp2"] = "iscsi-direct" + params["source_sp2"] = "s2" + params["initiator_s2"] = "iqn.2018-01.redhat.tianxu" + params["storage_hosts_s2"] = "h1" + params["hostname_h1"] = '10.66.10.26' + params["port_h1"] = '3260' + params["devices_s2"] = "s2d1" + params["path_s2d1"] = "iqn.2019-09.com.example:t1" + params["authorization_s2"] = "chap" + params["username_s2"] = "admin" + params["password_s2"] = "password" + + params["storage_type_sp3"] = "nfs" + params["target_path_sp3"] = "/tmp/nfs" + params["source_sp3"] = "s3" + params["source_dir_s3"] = "/nfs" + params["storage_hosts_s3"] = "h3" + params["hostname_h3"] = "127.0.0.1" + + params["storage_type_sp4"] = "rbd" + params["source_sp4"] = "rbd" + params["ceph_keyring_rbd"] = "AQBPxXBdAh7OJRAAC0EEml6+slcYAkZRiWN52w==" + params["ceph_user_rbd"] = "client.admin" + params["hosts_rbd"] = "mon1" + params["hostname_mon1"] = "10.66.144.31" + + params["storage_type_sp5"] = "gluster" + params["source_sp5"] = "vol" + params["storage_hosts_vol"] = "h5" + params["hostname_h5"] = "10.66.8.135" + params["dir_path_gv1"] = "/" + + params["images"] = "img1 img2 img3 img4 img5 img6 img7" + params["image_size"] = "100M" + params["image_format"] = "raw" + params["storage_pool_img1"] = "sp1" + params["image_format_img1"] = "qcow2" + + params["storage_pool_img2"] = "sp2" + params["image_format_img2"] = "raw" + + params["storage_pool_img3"] = "sp3" + params["image_format_img3"] = "raw" + params["image_size_img3"] = "100M" + + params["storage_pool_img4"] = "sp1" + params["image_format_img4"] = "qcow2" + params["image_encryption_img4"] = "luks" + params["secret_name_img4"] = "sec0" + params["secret_data_sec0"] = "1234" + + params["storage_pool_img5"] = "sp1" + params["image_format_img5"] = "qcow2" + params["image_encryption_img5"] = "on" + params["secret_name_img5"] = "sec0" + + params["storage_pool_img6"] = "sp4" + params["image_format_img6"] = "raw" + + params["storage_pool_img7"] = "sp5" + params["image_format_img7"] = "qcow2" + params["image_size_img7"] = "1G" + + params["backing_img1"] = "img4" + params["backing_img4"] = "img5" + self.params = params + + def test_01_pools_define_by_params(self): + sp_admin.pools_define_by_params(self.params) + for name in self.params.objects("storage_pools"): + pool = sp_admin.find_pool_by_name(name) + unittest.TestCase.assertEqual( + self, name, pool.name, "pool name mismatch!") + + def test_02_pool_type(self): + for pool in sp_admin.list_pools(): + expect_val = self.params.get("storage_type_%s" % pool.name) + actual_val = pool.TYPE + unittest.TestCase.assertEqual( + self, expect_val, actual_val, "pool type mismatch!") + + def test_03_pool_state(self): + for pool in sp_admin.list_pools(): + sp_admin.start_pool(pool) + unittest.TestCase.assertEqual( + self, + "running", + pool.state, + "pool (%s) state mismatch" % + pool.name) + + def test_04_volumes_define_by_params(self): + sp_admin.volumes_define_by_params(self.params) + for name in self.params.objects("images"): + img = sp_admin.get_volume_by_name(name) + assert img, "%s can't find by name" % name + img1 = sp_admin.get_volume_by_name("img1") + img4 = sp_admin.get_volume_by_name("img4") + img5 = sp_admin.get_volume_by_name("img5") + unittest.TestCase.assertEqual( + self, + img1.backing_store.name, + img4.name, + "img1's backing_store mismatch!") + unittest.TestCase.assertEqual( + self, + img4.backing_store.name, + img5.name, + "img4's backing_store mismatch!") + + def test_05_acqurie_volume(self): + for volume in sp_admin.list_volumes(): + sp_admin.acquire_volume(volume) + unittest.TestCase.assertTrue(self, volume.is_allocated, + "volume(%s) is_allocated value mismatch!" % volume.name) + + def test_06_find_pool_by_volume(self): + for volume in sp_admin.list_volumes(): + if volume.name: + pool = sp_admin.find_pool_by_volume(volume) + vol_params = self.params.object_params(volume.name) + pool_name = vol_params.get("storage_pool") + unittest.TestCase.assertEqual( + self, pool.name, pool_name, "pool name mismatch!") + + def test_07_find_volume_by_key(self): + path_img1 = "/tmp/avocado/img1" + unittest.TestCase.assertIsNotNone( + self, + sp_admin.get_volume_by_name("img1"), + "can not found img1 by name!") + unittest.TestCase.assertIsNotNone( + self, + sp_admin.get_volume_by_path(path_img1), + "can not found img1 by path!") + unittest.TestCase.assertEqual(self, sp_admin.get_volume_by_path(path_img1), + sp_admin.get_volume_by_name("img1"), + "volumes are not same!") + + def test_08_find_pool_by_path(self): + path = "/tmp/avocado" + pool = sp_admin.find_pool_by_path(path) + unittest.TestCase.assertEqual( + self, "sp1", pool.name, "can not found pool by path!") + + def test_09_remove_volume(self): + for volume in sp_admin.list_volumes(): + if volume.is_allocated: + sp_admin.remove_volume(volume) + unittest.TestCase.assertNotIn(self, volume, sp_admin.list_volumes(), + "Volume (%s) is exists!" % volume.name) + + def test_10_stop_pool(self): + for pool in sp_admin.list_pools(): + sp_admin.stop_pool(pool) + unittest.TestCase.assertEqual( + self, + "ready", + pool.state, + "pool (%s) state mismatch" % + pool.name) + + def test_11_destory_pool(self): + for pool in sp_admin.list_pools(): + sp_admin.destroy_pool(pool) + unittest.TestCase.assertEqual( + self, + "dead", + pool.state, + "pool (%s) state mismatch" % + pool.name) + + +if __name__ == "__main__": + loader = unittest.TestLoader() + + + def ln(f): return getattr( + VirtStorageTest, + f).im_func.func_code.co_firstlineno + + + def lncmp(a, b): return (a > b) - (a < b) + + + loader.sortTestMethodsUsing = lncmp + unittest.main(testLoader=loader, verbosity=2) diff --git a/virttest/virt_storage/utils/__init__.py b/virttest/virt_storage/utils/__init__.py new file mode 100755 index 00000000000..e8a3fcbebfc --- /dev/null +++ b/virttest/virt_storage/utils/__init__.py @@ -0,0 +1,50 @@ +from virttest.virt_storage.driver import glustercli, rbdcli, iscsicli, fscli, nfscli + + +def get_module_by_driver(driver): + if "iscsi" in driver: + return iscsicli + elif driver == "gluster": + return glustercli + elif driver == "nfs": + return nfscli + elif driver == "rbd": + return rbdcli + elif driver == "directory": + return fscli + raise ValueError("unsupported driver %s" % driver) + + +def get_pool_helper(pool): + driver = get_module_by_driver(pool.TYPE) + func_name = "get_pool_helper" + func = getattr(driver, func_name) + return func(pool) + + +def get_instance_info(inst): + _info = dict() + for key, val in vars(inst).items(): + # skip method + if callable(val): + continue + # skip private attributes + if key.startswith("_"): + key = key.lstrip("_") + if hasattr(inst, key): + _info[key] = getattr(inst, key) + continue + + if isinstance(val, list): + val = list(map(str, val)) + _info[key] = val + continue + elif isinstance(val, dict): + for k, v in val.items(): + val[k] = str(v) + _info[key] = val + continue + else: + _info[key] = str(val) + continue + return _info diff --git a/virttest/virt_storage/utils/state.py b/virttest/virt_storage/utils/state.py new file mode 100755 index 00000000000..b7c3989fae7 --- /dev/null +++ b/virttest/virt_storage/utils/state.py @@ -0,0 +1,25 @@ +from transitions import Machine + + +def register_pool_state_machine(instance): + states = ['dead', 'ready', 'running'] + transitions = [ + {'trigger': 'start_pool', + 'source': ['dead', 'ready'], + 'dest': 'running', + 'after': 'start'}, + {'trigger': 'stop_pool', + 'source': 'running', + 'dest': 'ready', + 'after': 'stop'}, + {'trigger': 'destroy_pool', + 'source': ['stop', 'ready'], + 'dest': 'dead', + 'after': 'destroy'} + ] + machine = Machine( + model=instance, + states=states, + transitions=transitions, + initial="dead") + return machine diff --git a/virttest/virt_storage/utils/storage_util.py b/virttest/virt_storage/utils/storage_util.py new file mode 100755 index 00000000000..81a891e06d9 --- /dev/null +++ b/virttest/virt_storage/utils/storage_util.py @@ -0,0 +1,22 @@ +import logging + +from avocado.core import exceptions +from avocado.utils import process + + +def create_volume(volume): + if volume.preallocation == "full": + if volume.pool.available < volume.capacity: + raise exceptions.TestError( + "No enough free space, request '%s' but available in %s is '%s'" % ( + volume.capacity, volume.pool.name, volume.pool.available)) + else: + if volume.format == "qcow2": + if volume.pool.available * 1.2 < volume.capacity: + raise exceptions.TestError( + "No enough free space, request '%s' but available in %s is '%s'" % ( + volume.capacity, volume.pool.name, volume.pool.available)) + options = volume.generate_qemu_img_options() + cmd = "qemu-img create %s %s %sB" % (options, volume.key, volume.capacity) + logging.debug("create volume cmd: %s" % cmd) + process.system(cmd, shell=True, ignore_status=False) diff --git a/virttest/virt_storage/virt_auth.py b/virttest/virt_storage/virt_auth.py new file mode 100755 index 00000000000..d7188b74d16 --- /dev/null +++ b/virttest/virt_storage/virt_auth.py @@ -0,0 +1,45 @@ +import os + +import configparser + +from virttest.virt_storage.virt_secret import secret_admin + + +class StorageAuthation(object): + + def __init__(self, _type=None, username=None, password=None, secret=None): + self.type = _type + self.username = username + self.password = password + self.secret = secret + + @classmethod + def auth_define_by_params(cls, params): + instance = cls() + auth_type = params.get("authorization_method") + if auth_type == "chap": + instance.password = params.get("chap_password") + instance.username = params.get("chap_username") + elif auth_type == "ceph": + keyring = params.get("ceph_keyring") + username = params.get("ceph_user") + if keyring and os.path.isfile(keyring): + config = configparser.ConfigParser() + config.read(keyring) + if not username: + username = config.sections()[0] + password = config[username]["key"] + else: + password = params.get("ceph_key") + instance.username = username + instance.password = password + + secret_name = params.get("secret") + if secret_name: + secret = secret_admin.find_secret_by_name(secret_name) + if not secret: + secret_params = params.object_params(secret_name) + secret = secret_admin.secret_define_by_params( + secret_name, secret_params) + instance.secret = secret + return instance diff --git a/virttest/virt_storage/virt_device.py b/virttest/virt_storage/virt_device.py new file mode 100755 index 00000000000..235e7a000c5 --- /dev/null +++ b/virttest/virt_storage/virt_device.py @@ -0,0 +1,47 @@ +class HostAdapter(object): + + def __init__(self): + self.name = None + self.type = None + self.wwn = None + self.wwnn = None + self.wwpn = None + self.addr = None + + @classmethod + def adapter_define_by_params(cls, params): + inst = cls() + inst.name = params.get("apapter_name") + inst.type = params.get("adapter_type") + inst.wwn = params.get("wwn") + inst.wwpn = params.get("wwpn") + inst.wwnn = params.get("wwnn") + parent = params.get("parent") + if parent: + parent_params = params.object_params(parent) + parent_adapter = cls.adapter_define_by_params(parent_params) + inst.parent = parent_adapter + return inst + + +class StorageDevice(object): + + def __init__(self, path): + self.path = path + + @classmethod + def device_define_by_params(cls, params): + return cls(params.get("device_path")) + + +class StorageHost(object): + + def __init__(self, hostname, port=None): + self.hostname = hostname + self.port = port + + @classmethod + def host_define_by_params(cls, params): + hostname = params.get("hostname") + port = params.get("port") + return cls(hostname, port) diff --git a/virttest/virt_storage/virt_encryption.py b/virttest/virt_storage/virt_encryption.py new file mode 100755 index 00000000000..e49c27ef8b2 --- /dev/null +++ b/virttest/virt_storage/virt_encryption.py @@ -0,0 +1,25 @@ +from virttest.virt_storage.virt_secret import secret_admin + + +class VolumeEncryption(object): + + def __init__(self, encrypt_format=None, secret=None): + self.format = encrypt_format + self.secret = secret + + @classmethod + def encryption_define_by_params(cls, params): + instance = cls() + if params["image_encryption"] == "on": + encryption_format = "aes" + else: + encryption_format = params["image_encryption"] + instance.format = encryption_format + secret_name = params["secret_name"] + secret = secret_admin.find_secret_by_name(secret_name) + if not secret: + secret_params = params.object_params(secret_name) + secret = secret_admin.secret_define_by_params( + secret_name, secret_params) + instance.secret = secret + return instance diff --git a/virttest/virt_storage/virt_secret.py b/virttest/virt_storage/virt_secret.py new file mode 100755 index 00000000000..fc53f11f0dd --- /dev/null +++ b/virttest/virt_storage/virt_secret.py @@ -0,0 +1,72 @@ +import os +import tempfile +import uuid + +from avocado.utils import genio + + +class StorageSecret(object): + + def __init__(self, name, data, stype=None): + self.name = name + self._data = data + self._data_file = None + self.uuid = uuid.uuid1() + self.secret_type = stype + + @property + def data(self): + return self._data + + @data.setter + def data(self, data): + if os.path.isfile(data): + self.data_file = data + self._data = genio.read_one_line(self.data_file) + else: + self._data = data + + @property + def data_file(self): + if self._data_file is None: + self._data_file = tempfile.mktemp() + genio.write_one_line(self._data_file, self.data) + return self._data_file + + @data_file.setter + def data_file(self, data_file): + if os.path.isfile(data_file): + self._data = genio.read_one_line(self.data_file) + else: + genio.write_one_line(data_file, self.data) + + +class StorageSecretAdmin(object): + __secrets = list() + + @classmethod + def secret_define_by_params(cls, name, params): + data = params.get("secret_data", "") + cls.__secrets.append(StorageSecret(name, data)) + return cls.__secrets[-1] + + @classmethod + def secrets_define_by_params(cls, test_params): + for name in test_params.objects("image_secrets"): + params = test_params.object_params(name) + cls.secret_define_by_params(name, params) + + return filter(lambda x: x.name == name, cls.__secrets) + + @classmethod + def find_secret_by_name(cls, name): + secrets = filter(lambda x: x.name == name, cls.__secrets) + return secrets[0] if secrets else None + + @classmethod + def find_secret_by_uuid(cls, _uuid): + secrets = filter(lambda x: str(x.uuid) == _uuid, cls.__secrets) + return secrets[0] if secrets else None + + +secret_admin = StorageSecretAdmin() diff --git a/virttest/virt_storage/virt_source.py b/virttest/virt_storage/virt_source.py new file mode 100755 index 00000000000..6f72fff30ba --- /dev/null +++ b/virttest/virt_storage/virt_source.py @@ -0,0 +1,58 @@ +from virttest.virt_storage.virt_auth import StorageAuthation +from virttest.virt_storage.virt_device import HostAdapter +from virttest.virt_storage.virt_device import StorageDevice +from virttest.virt_storage.virt_device import StorageHost + + +class PoolSource(object): + + def __init__(self): + self.name = None + self.auth = None + self.hosts = [] + self.devices = [] + self.format = None + self.vendor = None + self.adapter = None + self.product = None + self.protocol = None + self.dir_path = None + self.initiator = None + + def hosts_define_by_params(self, params): + for item in params.objects("storage_hosts"): + _params = params.object_params(item) + host = StorageHost.host_define_by_params(_params) + self.hosts.append(host) + + def devices_define_by_params(self, params): + for item in params.objects("devices"): + _params = params.object_params(item) + device = StorageDevice.device_define_by_params(_params) + self.devices.append(device) + + def auth_define_by_params(self, params): + if params.get("authorization_method", "off") != "off": + self.auth = StorageAuthation.auth_define_by_params(params) + + def adapter_define_by_params(self, params): + self.adapter = HostAdapter.adapter_define_by_params(params) + + @classmethod + def source_define_by_params(cls, name, params): + instance = cls() + instance.name = name + instance.initiator = params.get("initiator") + instance.vendor = params.get("vendor") + instance.protocol = params.get("protocol") + instance.product = params.get("product") + instance.dir_path = params.get("source_dir") + instance.format = params.get("source_format") + instance.hosts_define_by_params(params) + instance.devices_define_by_params(params) + instance.auth_define_by_params(params) + instance.adapter_define_by_params(params) + return instance + + def __str__(self): + return "%s: %s" % (self.__class__.__name__, self.name) diff --git a/virttest/virt_storage/virt_target.py b/virttest/virt_storage/virt_target.py new file mode 100755 index 00000000000..a283b94cbd9 --- /dev/null +++ b/virttest/virt_storage/virt_target.py @@ -0,0 +1,15 @@ +class PoolTarget(object): + + def __init__(self): + self.path = None + self.format = None + + @classmethod + def target_define_by_params(cls, params): + instance = cls() + instance.path = params.get("target_path") + instance.format = params.get("target_format") + return instance + + def __str__(self): + return "%s: %s" % (self.__class__.__name__, self.path)