Merge #206 Add option to customize disc type
This commit is contained in:
commit
0e7ea91d9d
@ -202,18 +202,18 @@ Options
|
|||||||
-------
|
-------
|
||||||
|
|
||||||
There a couple common format specifiers available for both the options:
|
There a couple common format specifiers available for both the options:
|
||||||
* compose_id
|
* ``compose_id``
|
||||||
* release_short
|
* ``release_short``
|
||||||
* version
|
* ``version``
|
||||||
* date
|
* ``date``
|
||||||
* respin
|
* ``respin``
|
||||||
* type
|
* ``type``
|
||||||
* type_suffix
|
* ``type_suffix``
|
||||||
* label
|
* ``label``
|
||||||
* label_major_version
|
* ``label_major_version``
|
||||||
* variant
|
* ``variant``
|
||||||
* arch
|
* ``arch``
|
||||||
* disc_type
|
* ``disc_type``
|
||||||
|
|
||||||
**image_name_format** [optional]
|
**image_name_format** [optional]
|
||||||
(*str*) -- Python's format string to serve as template for image names
|
(*str*) -- Python's format string to serve as template for image names
|
||||||
@ -222,15 +222,15 @@ There a couple common format specifiers available for both the options:
|
|||||||
means ``createiso``, ``live_images`` and ``buildinstall``.
|
means ``createiso``, ``live_images`` and ``buildinstall``.
|
||||||
|
|
||||||
Available extra keys are:
|
Available extra keys are:
|
||||||
* disc_num
|
* ``disc_num``
|
||||||
* suffix
|
* ``suffix``
|
||||||
|
|
||||||
**image_volid_formats** [optional]
|
**image_volid_formats** [optional]
|
||||||
(*list*) -- A list of format strings for generating volume id.
|
(*list*) -- A list of format strings for generating volume id.
|
||||||
|
|
||||||
The extra available keys are:
|
The extra available keys are:
|
||||||
* base_product_short
|
* ``base_product_short``
|
||||||
* base_product_version
|
* ``base_product_version``
|
||||||
|
|
||||||
**image_volid_layered_product_formats** [optional]
|
**image_volid_layered_product_formats** [optional]
|
||||||
(*list*) -- A list of format strings for generating volume id for layered
|
(*list*) -- A list of format strings for generating volume id for layered
|
||||||
@ -239,6 +239,16 @@ There a couple common format specifiers available for both the options:
|
|||||||
**volume_id_substitutions** [optional]
|
**volume_id_substitutions** [optional]
|
||||||
(*dict*) -- A mapping of string replacements to shorten the volume id.
|
(*dict*) -- A mapping of string replacements to shorten the volume id.
|
||||||
|
|
||||||
|
**disc_types** [optional]
|
||||||
|
(*dict*) -- A mapping for customizing ``disc_type`` used in image names.
|
||||||
|
|
||||||
|
Available keys are:
|
||||||
|
* ``boot`` -- for ``boot.iso`` images created in *buildinstall* phase
|
||||||
|
* ``live`` -- for images created by *live_images* phase
|
||||||
|
* ``dvd`` -- for images created by *createiso* phase
|
||||||
|
|
||||||
|
Default values are the same as the keys.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
::
|
::
|
||||||
@ -259,6 +269,12 @@ Example
|
|||||||
'TC': 'T',
|
'TC': 'T',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disc_types = {
|
||||||
|
'boot': 'netinst',
|
||||||
|
'live': 'Live',
|
||||||
|
'dvd': 'DVD',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Signing
|
Signing
|
||||||
=======
|
=======
|
||||||
|
@ -189,7 +189,7 @@ class BuildinstallPhase(PhaseBase):
|
|||||||
label = ""
|
label = ""
|
||||||
volid = get_volid(self.compose, arch, variant, escape_spaces=False, disc_type="dvd")
|
volid = get_volid(self.compose, arch, variant, escape_spaces=False, disc_type="dvd")
|
||||||
tweak_buildinstall(buildinstall_dir, os_tree, arch, variant.uid, label, volid, kickstart_file)
|
tweak_buildinstall(buildinstall_dir, os_tree, arch, variant.uid, label, volid, kickstart_file)
|
||||||
symlink_boot_iso(self.compose, arch, variant)
|
link_boot_iso(self.compose, arch, variant)
|
||||||
|
|
||||||
|
|
||||||
def get_kickstart_file(compose):
|
def get_kickstart_file(compose):
|
||||||
@ -312,10 +312,12 @@ def tweak_buildinstall(src, dst, arch, variant, label, volid, kickstart_file=Non
|
|||||||
shutil.rmtree(tmp_dir)
|
shutil.rmtree(tmp_dir)
|
||||||
|
|
||||||
|
|
||||||
def symlink_boot_iso(compose, arch, variant):
|
def link_boot_iso(compose, arch, variant):
|
||||||
if arch == "src":
|
if arch == "src":
|
||||||
return
|
return
|
||||||
|
|
||||||
|
disc_type = compose.conf.get('disc_types', {}).get('boot', 'boot')
|
||||||
|
|
||||||
symlink_isos_to = compose.conf.get("symlink_isos_to", None)
|
symlink_isos_to = compose.conf.get("symlink_isos_to", None)
|
||||||
os_tree = compose.paths.compose.os_tree(arch, variant)
|
os_tree = compose.paths.compose.os_tree(arch, variant)
|
||||||
# TODO: find in treeinfo?
|
# TODO: find in treeinfo?
|
||||||
@ -323,8 +325,8 @@ def symlink_boot_iso(compose, arch, variant):
|
|||||||
if not os.path.isfile(boot_iso_path):
|
if not os.path.isfile(boot_iso_path):
|
||||||
return
|
return
|
||||||
|
|
||||||
msg = "Symlinking boot.iso (arch: %s, variant: %s)" % (arch, variant)
|
msg = "Linking boot.iso (arch: %s, variant: %s)" % (arch, variant)
|
||||||
filename = compose.get_image_name(arch, variant, disc_type="boot",
|
filename = compose.get_image_name(arch, variant, disc_type=disc_type,
|
||||||
disc_num=None, suffix=".iso")
|
disc_num=None, suffix=".iso")
|
||||||
new_boot_iso_path = compose.paths.compose.iso_path(arch, variant, filename,
|
new_boot_iso_path = compose.paths.compose.iso_path(arch, variant, filename,
|
||||||
symlink_to=symlink_isos_to)
|
symlink_to=symlink_isos_to)
|
||||||
|
@ -54,6 +54,7 @@ class CreateisoPhase(PhaseBase):
|
|||||||
def run(self):
|
def run(self):
|
||||||
iso = IsoWrapper(logger=self.compose._logger)
|
iso = IsoWrapper(logger=self.compose._logger)
|
||||||
symlink_isos_to = self.compose.conf.get("symlink_isos_to", None)
|
symlink_isos_to = self.compose.conf.get("symlink_isos_to", None)
|
||||||
|
disc_type = self.compose.conf.get('disc_types', {}).get('dvd', 'dvd')
|
||||||
deliverables = []
|
deliverables = []
|
||||||
|
|
||||||
commands = []
|
commands = []
|
||||||
@ -64,7 +65,7 @@ class CreateisoPhase(PhaseBase):
|
|||||||
self.compose.log_info("Skipping createiso for %s.%s due to config option" % (variant, arch))
|
self.compose.log_info("Skipping createiso for %s.%s due to config option" % (variant, arch))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
volid = get_volid(self.compose, arch, variant, disc_type='dvd')
|
volid = get_volid(self.compose, arch, variant, disc_type=disc_type)
|
||||||
os_tree = self.compose.paths.compose.os_tree(arch, variant)
|
os_tree = self.compose.paths.compose.os_tree(arch, variant)
|
||||||
|
|
||||||
iso_dir = self.compose.paths.compose.iso_dir(arch, variant, symlink_to=symlink_isos_to)
|
iso_dir = self.compose.paths.compose.iso_dir(arch, variant, symlink_to=symlink_isos_to)
|
||||||
@ -90,9 +91,8 @@ class CreateisoPhase(PhaseBase):
|
|||||||
for disc_num, iso_data in enumerate(split_iso_data):
|
for disc_num, iso_data in enumerate(split_iso_data):
|
||||||
disc_num += 1
|
disc_num += 1
|
||||||
|
|
||||||
# XXX: hardcoded disc_type
|
|
||||||
filename = self.compose.get_image_name(arch, variant,
|
filename = self.compose.get_image_name(arch, variant,
|
||||||
disc_type="dvd",
|
disc_type=disc_type,
|
||||||
disc_num=disc_num)
|
disc_num=disc_num)
|
||||||
iso_path = self.compose.paths.compose.iso_path(arch,
|
iso_path = self.compose.paths.compose.iso_path(arch,
|
||||||
variant,
|
variant,
|
||||||
|
@ -178,6 +178,8 @@ class LiveImagesPhase(PhaseBase):
|
|||||||
if self.compose.conf.get('live_images_no_rename', False):
|
if self.compose.conf.get('live_images_no_rename', False):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
disc_type = self.compose.conf.get('disc_types', {}).get('live', 'live')
|
||||||
|
|
||||||
format = "%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s"
|
format = "%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s"
|
||||||
# Custom name (prefix)
|
# Custom name (prefix)
|
||||||
if name:
|
if name:
|
||||||
@ -186,8 +188,8 @@ class LiveImagesPhase(PhaseBase):
|
|||||||
custom_iso_name += "-%s" % version
|
custom_iso_name += "-%s" % version
|
||||||
format = custom_iso_name + "-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s"
|
format = custom_iso_name + "-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s"
|
||||||
|
|
||||||
# XXX: hardcoded disc_type and disc_num
|
# XXX: hardcoded disc_num
|
||||||
return self.compose.get_image_name(arch, variant, disc_type="live",
|
return self.compose.get_image_name(arch, variant, disc_type=disc_type,
|
||||||
disc_num=None, format=format)
|
disc_num=None, format=format)
|
||||||
|
|
||||||
def stop(self, *args, **kwargs):
|
def stop(self, *args, **kwargs):
|
||||||
|
@ -10,7 +10,7 @@ import sys
|
|||||||
|
|
||||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
from pungi.phases.buildinstall import BuildinstallPhase, BuildinstallThread, symlink_boot_iso
|
from pungi.phases.buildinstall import BuildinstallPhase, BuildinstallThread, link_boot_iso
|
||||||
from tests.helpers import DummyCompose, PungiTestCase, touch
|
from tests.helpers import DummyCompose, PungiTestCase, touch
|
||||||
|
|
||||||
|
|
||||||
@ -312,14 +312,14 @@ class TestBuildinstallPhase(PungiTestCase):
|
|||||||
|
|
||||||
class TestCopyFiles(PungiTestCase):
|
class TestCopyFiles(PungiTestCase):
|
||||||
|
|
||||||
@mock.patch('pungi.phases.buildinstall.symlink_boot_iso')
|
@mock.patch('pungi.phases.buildinstall.link_boot_iso')
|
||||||
@mock.patch('pungi.phases.buildinstall.tweak_buildinstall')
|
@mock.patch('pungi.phases.buildinstall.tweak_buildinstall')
|
||||||
@mock.patch('pungi.phases.buildinstall.get_volid')
|
@mock.patch('pungi.phases.buildinstall.get_volid')
|
||||||
@mock.patch('os.listdir')
|
@mock.patch('os.listdir')
|
||||||
@mock.patch('os.path.isdir')
|
@mock.patch('os.path.isdir')
|
||||||
@mock.patch('pungi.phases.buildinstall.get_kickstart_file')
|
@mock.patch('pungi.phases.buildinstall.get_kickstart_file')
|
||||||
def test_copy_files_buildinstall(self, get_kickstart_file, isdir, listdir,
|
def test_copy_files_buildinstall(self, get_kickstart_file, isdir, listdir,
|
||||||
get_volid, tweak_buildinstall, symlink_boot_iso):
|
get_volid, tweak_buildinstall, link_boot_iso):
|
||||||
compose = BuildInstallCompose(self.topdir, {
|
compose = BuildInstallCompose(self.topdir, {
|
||||||
'buildinstall_method': 'buildinstall'
|
'buildinstall_method': 'buildinstall'
|
||||||
})
|
})
|
||||||
@ -349,19 +349,19 @@ class TestCopyFiles(PungiTestCase):
|
|||||||
self.topdir + '/compose/Client/amd64/os',
|
self.topdir + '/compose/Client/amd64/os',
|
||||||
'amd64', 'Client', '', 'Client.amd64', 'kickstart')])
|
'amd64', 'Client', '', 'Client.amd64', 'kickstart')])
|
||||||
self.assertItemsEqual(
|
self.assertItemsEqual(
|
||||||
symlink_boot_iso.mock_calls,
|
link_boot_iso.mock_calls,
|
||||||
[mock.call(compose, 'x86_64', compose.variants['Server']),
|
[mock.call(compose, 'x86_64', compose.variants['Server']),
|
||||||
mock.call(compose, 'amd64', compose.variants['Client']),
|
mock.call(compose, 'amd64', compose.variants['Client']),
|
||||||
mock.call(compose, 'amd64', compose.variants['Server'])])
|
mock.call(compose, 'amd64', compose.variants['Server'])])
|
||||||
|
|
||||||
@mock.patch('pungi.phases.buildinstall.symlink_boot_iso')
|
@mock.patch('pungi.phases.buildinstall.link_boot_iso')
|
||||||
@mock.patch('pungi.phases.buildinstall.tweak_buildinstall')
|
@mock.patch('pungi.phases.buildinstall.tweak_buildinstall')
|
||||||
@mock.patch('pungi.phases.buildinstall.get_volid')
|
@mock.patch('pungi.phases.buildinstall.get_volid')
|
||||||
@mock.patch('os.listdir')
|
@mock.patch('os.listdir')
|
||||||
@mock.patch('os.path.isdir')
|
@mock.patch('os.path.isdir')
|
||||||
@mock.patch('pungi.phases.buildinstall.get_kickstart_file')
|
@mock.patch('pungi.phases.buildinstall.get_kickstart_file')
|
||||||
def test_copy_files_lorax(self, get_kickstart_file, isdir, listdir,
|
def test_copy_files_lorax(self, get_kickstart_file, isdir, listdir,
|
||||||
get_volid, tweak_buildinstall, symlink_boot_iso):
|
get_volid, tweak_buildinstall, link_boot_iso):
|
||||||
compose = BuildInstallCompose(self.topdir, {
|
compose = BuildInstallCompose(self.topdir, {
|
||||||
'buildinstall_method': 'lorax'
|
'buildinstall_method': 'lorax'
|
||||||
})
|
})
|
||||||
@ -391,7 +391,7 @@ class TestCopyFiles(PungiTestCase):
|
|||||||
self.topdir + '/compose/Client/amd64/os',
|
self.topdir + '/compose/Client/amd64/os',
|
||||||
'amd64', 'Client', '', 'Client.amd64', 'kickstart')])
|
'amd64', 'Client', '', 'Client.amd64', 'kickstart')])
|
||||||
self.assertItemsEqual(
|
self.assertItemsEqual(
|
||||||
symlink_boot_iso.mock_calls,
|
link_boot_iso.mock_calls,
|
||||||
[mock.call(compose, 'x86_64', compose.variants['Server']),
|
[mock.call(compose, 'x86_64', compose.variants['Server']),
|
||||||
mock.call(compose, 'amd64', compose.variants['Client']),
|
mock.call(compose, 'amd64', compose.variants['Client']),
|
||||||
mock.call(compose, 'amd64', compose.variants['Server'])])
|
mock.call(compose, 'amd64', compose.variants['Server'])])
|
||||||
@ -576,7 +576,7 @@ class TestSymlinkIso(PungiTestCase):
|
|||||||
get_file_size.return_value = 1024
|
get_file_size.return_value = 1024
|
||||||
get_mtime.return_value = 13579
|
get_mtime.return_value = 13579
|
||||||
|
|
||||||
symlink_boot_iso(self.compose, 'x86_64', self.compose.variants['Server'])
|
link_boot_iso(self.compose, 'x86_64', self.compose.variants['Server'])
|
||||||
|
|
||||||
tgt = self.topdir + '/compose/Server/x86_64/iso/image-name'
|
tgt = self.topdir + '/compose/Server/x86_64/iso/image-name'
|
||||||
self.assertTrue(os.path.isfile(tgt))
|
self.assertTrue(os.path.isfile(tgt))
|
||||||
@ -611,6 +611,55 @@ class TestSymlinkIso(PungiTestCase):
|
|||||||
self.assertEqual(self.compose.im.add.mock_calls,
|
self.assertEqual(self.compose.im.add.mock_calls,
|
||||||
[mock.call('Server', 'x86_64', image)])
|
[mock.call('Server', 'x86_64', image)])
|
||||||
|
|
||||||
|
@mock.patch('pungi.phases.buildinstall.Image')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.get_mtime')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.get_file_size')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.IsoWrapper')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.run')
|
||||||
|
def test_hardlink_with_custom_type(self, run, IsoWrapperCls, get_file_size, get_mtime, ImageCls):
|
||||||
|
self.compose.conf = {
|
||||||
|
'buildinstall_symlink': False,
|
||||||
|
'disc_types': {'boot': 'netinst'},
|
||||||
|
}
|
||||||
|
IsoWrapper = IsoWrapperCls.return_value
|
||||||
|
get_file_size.return_value = 1024
|
||||||
|
get_mtime.return_value = 13579
|
||||||
|
|
||||||
|
link_boot_iso(self.compose, 'x86_64', self.compose.variants['Server'])
|
||||||
|
|
||||||
|
tgt = self.topdir + '/compose/Server/x86_64/iso/image-name'
|
||||||
|
self.assertTrue(os.path.isfile(tgt))
|
||||||
|
self.assertEqual(os.stat(tgt).st_ino,
|
||||||
|
os.stat(self.topdir + '/compose/Server/x86_64/os/images/boot.iso').st_ino)
|
||||||
|
|
||||||
|
self.assertItemsEqual(
|
||||||
|
self.compose.get_image_name.mock_calls,
|
||||||
|
[mock.call('x86_64', self.compose.variants['Server'],
|
||||||
|
disc_type='netinst', disc_num=None, suffix='.iso')])
|
||||||
|
self.assertItemsEqual(IsoWrapper.get_implanted_md5.mock_calls,
|
||||||
|
[mock.call(tgt)])
|
||||||
|
self.assertItemsEqual(IsoWrapper.get_manifest_cmd.mock_calls,
|
||||||
|
[mock.call('image-name')])
|
||||||
|
self.assertItemsEqual(IsoWrapper.get_volume_id.mock_calls,
|
||||||
|
[mock.call(tgt)])
|
||||||
|
self.assertItemsEqual(run.mock_calls,
|
||||||
|
[mock.call(IsoWrapper.get_manifest_cmd.return_value,
|
||||||
|
workdir=self.topdir + '/compose/Server/x86_64/iso')])
|
||||||
|
|
||||||
|
image = ImageCls.return_value
|
||||||
|
self.assertEqual(image.path, 'Server/x86_64/iso/image-name')
|
||||||
|
self.assertEqual(image.mtime, 13579)
|
||||||
|
self.assertEqual(image.size, 1024)
|
||||||
|
self.assertEqual(image.arch, 'x86_64')
|
||||||
|
self.assertEqual(image.type, "boot")
|
||||||
|
self.assertEqual(image.format, "iso")
|
||||||
|
self.assertEqual(image.disc_number, 1)
|
||||||
|
self.assertEqual(image.disc_count, 1)
|
||||||
|
self.assertEqual(image.bootable, True)
|
||||||
|
self.assertEqual(image.implant_md5, IsoWrapper.get_implanted_md5.return_value)
|
||||||
|
self.assertEqual(self.compose.im.add.mock_calls,
|
||||||
|
[mock.call('Server', 'x86_64', image)])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -58,6 +58,10 @@ class TestLiveImagesPhase(PungiTestCase):
|
|||||||
'ksurl': None},
|
'ksurl': None},
|
||||||
compose.variants['Client'],
|
compose.variants['Client'],
|
||||||
'amd64'))])
|
'amd64'))])
|
||||||
|
self.assertItemsEqual(
|
||||||
|
compose.get_image_name.mock_calls,
|
||||||
|
[mock.call('amd64', compose.variants['Client'], disc_num=None, disc_type='live',
|
||||||
|
format='%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s')])
|
||||||
|
|
||||||
@mock.patch('pungi.phases.live_images.ThreadPool')
|
@mock.patch('pungi.phases.live_images.ThreadPool')
|
||||||
def test_live_image_build_single_repo_from(self, ThreadPool):
|
def test_live_image_build_single_repo_from(self, ThreadPool):
|
||||||
@ -260,6 +264,54 @@ class TestLiveImagesPhase(PungiTestCase):
|
|||||||
self.assertEqual(resolve_git_url.mock_calls,
|
self.assertEqual(resolve_git_url.mock_calls,
|
||||||
[mock.call('https://git.example.com/kickstarts.git?#HEAD')])
|
[mock.call('https://git.example.com/kickstarts.git?#HEAD')])
|
||||||
|
|
||||||
|
@mock.patch('pungi.phases.live_images.ThreadPool')
|
||||||
|
def test_live_image_build_custom_type(self, ThreadPool):
|
||||||
|
compose = DummyCompose(self.topdir, {
|
||||||
|
'disc_types': {'live': 'Live'},
|
||||||
|
'live_images': [
|
||||||
|
('^Client$', {
|
||||||
|
'amd64': {
|
||||||
|
'kickstart': 'test.ks',
|
||||||
|
'additional_repos': ['http://example.com/repo/'],
|
||||||
|
'repo_from': ['Everything'],
|
||||||
|
'release': None,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
phase = LiveImagesPhase(compose)
|
||||||
|
|
||||||
|
phase.run()
|
||||||
|
|
||||||
|
# assert at least one thread was started
|
||||||
|
self.assertTrue(phase.pool.add.called)
|
||||||
|
self.maxDiff = None
|
||||||
|
self.assertItemsEqual(phase.pool.queue_put.mock_calls,
|
||||||
|
[mock.call((compose,
|
||||||
|
{'ks_file': 'test.ks',
|
||||||
|
'build_arch': 'amd64',
|
||||||
|
'dest_dir': self.topdir + '/compose/Client/amd64/iso',
|
||||||
|
'scratch': False,
|
||||||
|
'repos': [self.topdir + '/compose/Client/amd64/os',
|
||||||
|
'http://example.com/repo/',
|
||||||
|
self.topdir + '/compose/Everything/amd64/os'],
|
||||||
|
'label': '',
|
||||||
|
'name': None,
|
||||||
|
'filename': 'image-name',
|
||||||
|
'version': None,
|
||||||
|
'specfile': None,
|
||||||
|
'sign': False,
|
||||||
|
'type': 'live',
|
||||||
|
'release': '20151203.0',
|
||||||
|
'ksurl': None},
|
||||||
|
compose.variants['Client'],
|
||||||
|
'amd64'))])
|
||||||
|
self.assertItemsEqual(
|
||||||
|
compose.get_image_name.mock_calls,
|
||||||
|
[mock.call('amd64', compose.variants['Client'], disc_num=None, disc_type='Live',
|
||||||
|
format='%(compose_id)s-%(variant)s-%(arch)s-%(disc_type)s%(disc_num)s%(suffix)s')])
|
||||||
|
|
||||||
|
|
||||||
class TestCreateLiveImageThread(PungiTestCase):
|
class TestCreateLiveImageThread(PungiTestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user