tests: Add virt minion integration targets

The virt minion targets are used for integration testing of the virt
module. The targets are based on a container image with preinstalled
libvirt and qemu.

The salt source repository and configuration files are bind-mounted
within the container where a salt-minion instance is started.

The host's network stack (--network=host) is used to allow the
salt-minion inside the container to connect to the salt-master running
on the host.

Signed-off-by: Radostin Stoyanov <rstoyanov@fedoraproject.org>
This commit is contained in:
Radostin Stoyanov 2020-06-19 22:16:23 +01:00 committed by Daniel Wozniak
parent acdfae6b0e
commit 8e5748f590
10 changed files with 642 additions and 90 deletions

View file

@ -186,6 +186,12 @@ class TestDaemon(object):
self.colors = salt.utils.color.get_colors(
self.parser.options.no_colors is False
)
self.virt_minion_enabled = False
if salt.utils.path.which("docker"):
for test in self.parser.options.name:
if test.endswith("integration.modules.test_virt"):
self.virt_minion_enabled = True
break
if salt.utils.platform.is_windows():
# There's no shell color support on windows...
for key in self.colors:
@ -202,6 +208,9 @@ class TestDaemon(object):
self._enter_mockbin()
self.minion_targets = set(["minion", "sub_minion"])
if self.virt_minion_enabled:
self.minion_targets.add("virt_minion_0")
self.minion_targets.add("virt_minion_1")
if self.parser.options.transport == "zeromq":
self.start_zeromq_daemons()
@ -267,6 +276,9 @@ class TestDaemon(object):
)
self.log_server_process = threading.Thread(target=self.log_server.serve_forever)
self.log_server_process.start()
clear_current_line = "\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
try:
sys.stdout.write(
" * {LIGHT_YELLOW}Starting salt-master ... {ENDC}".format(**self.colors)
@ -285,30 +297,22 @@ class TestDaemon(object):
event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR,
start_timeout=120,
)
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_GREEN}Starting salt-master ... STARTED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
except (RuntimeWarning, RuntimeError):
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
except (RuntimeWarning, RuntimeError) as err:
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_RED}Starting salt-master ... FAILED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
raise TestDaemonStartFailed()
raise TestDaemonStartFailed(str(err))
try:
sys.stdout.write(
@ -328,30 +332,81 @@ class TestDaemon(object):
event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR,
start_timeout=120,
)
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_GREEN}Starting salt-minion ... STARTED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
except (RuntimeWarning, RuntimeError):
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
except (RuntimeWarning, RuntimeError) as err:
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_RED}Starting salt-minion ... FAILED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
raise TestDaemonStartFailed()
raise TestDaemonStartFailed(str(err))
if self.virt_minion_enabled:
try:
sys.stdout.write(
" * {LIGHT_YELLOW}Starting virt_minion_0 ... {ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
self.virt_minion_0_process = start_virt_daemon(
container_name="virt_minion_0",
container_img="quay.io/rst0git/virt-minion",
daemon_config_dir=RUNTIME_VARS.TMP_VIRT_MINION_0_CONF_DIR,
)
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_GREEN}Starting virt_minion_0 ... STARTED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
except (RuntimeWarning, RuntimeError) as err:
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_RED}Starting virt_minion_0 ... FAILED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
raise TestDaemonStartFailed(str(err))
try:
sys.stdout.write(
" * {LIGHT_YELLOW}Starting virt_minion_1 ... {ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
self.virt_minion_1_process = start_virt_daemon(
container_name="virt_minion_1",
container_img="quay.io/rst0git/virt-minion",
daemon_config_dir=RUNTIME_VARS.TMP_VIRT_MINION_1_CONF_DIR,
)
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_GREEN}Starting virt_minion_1 ... STARTED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
except (RuntimeWarning, RuntimeError) as err:
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_RED}Starting virt_minion_1 ... FAILED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
raise TestDaemonStartFailed(str(err))
try:
sys.stdout.write(
@ -375,30 +430,22 @@ class TestDaemon(object):
event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR,
start_timeout=120,
)
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_GREEN}Starting sub salt-minion ... STARTED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
except (RuntimeWarning, RuntimeError):
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
except (RuntimeWarning, RuntimeError) as err:
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_RED}Starting sub salt-minion ... FAILED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
raise TestDaemonStartFailed()
raise TestDaemonStartFailed(str(err))
try:
sys.stdout.write(
@ -423,30 +470,22 @@ class TestDaemon(object):
event_listener_config_dir=RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR,
start_timeout=120,
)
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_GREEN}Starting syndic salt-master ... STARTED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
except (RuntimeWarning, RuntimeError):
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
except (RuntimeWarning, RuntimeError) as err:
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_RED}Starting syndic salt-master ... FAILED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
raise TestDaemonStartFailed()
raise TestDaemonStartFailed(str(err))
try:
sys.stdout.write(
@ -466,30 +505,22 @@ class TestDaemon(object):
event_listener_config_dir=RUNTIME_VARS.TMP_CONF_DIR,
start_timeout=120,
)
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_GREEN}Starting salt-syndic ... STARTED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
except (RuntimeWarning, RuntimeError):
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
except (RuntimeWarning, RuntimeError) as err:
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_RED}Starting salt-syndic ... FAILED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
raise TestDaemonStartFailed()
raise TestDaemonStartFailed(str(err))
if self.parser.options.proxy:
self.minion_targets.add(self.proxy_opts["id"])
@ -512,30 +543,22 @@ class TestDaemon(object):
fail_hard=True,
start_timeout=120,
)
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_GREEN}Starting salt-proxy ... STARTED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
except (RuntimeWarning, RuntimeError):
sys.stdout.write(
"\r{0}\r".format(
" " * getattr(self.parser.options, "output_columns", PNUM)
)
)
except (RuntimeWarning, RuntimeError) as err:
sys.stdout.write(clear_current_line)
sys.stdout.write(
" * {LIGHT_RED}Starting salt-proxy ... FAILED!\n{ENDC}".format(
**self.colors
)
)
sys.stdout.flush()
raise TestDaemonStartFailed()
raise TestDaemonStartFailed(str(err))
start_tcp_daemons = start_zeromq_daemons
@ -795,6 +818,8 @@ class TestDaemon(object):
os.makedirs(RUNTIME_VARS.TMP)
os.makedirs(RUNTIME_VARS.TMP_ROOT_DIR)
os.makedirs(RUNTIME_VARS.TMP_CONF_DIR)
os.makedirs(RUNTIME_VARS.TMP_VIRT_MINION_0_CONF_DIR)
os.makedirs(RUNTIME_VARS.TMP_VIRT_MINION_1_CONF_DIR)
os.makedirs(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR)
os.makedirs(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR)
os.makedirs(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR)
@ -887,6 +912,27 @@ class TestDaemon(object):
if virtualenv_binary:
minion_opts["venv_bin"] = virtualenv_binary
# This libvirt + minion connects to master
virt_minion_0_opts = salt.config._read_conf_file(
os.path.join(RUNTIME_VARS.CONF_DIR, "virt_minion_0")
)
virt_minion_0_opts["user"] = "root"
virt_minion_0_opts["cachedir"] = "cache"
virt_minion_0_opts["root_dir"] = "/etc/salt"
virt_minion_0_opts["pki_dir"] = "pki"
virt_minion_0_opts["hosts.file"] = "hosts"
virt_minion_0_opts["aliases.file"] = "aliases"
virt_minion_1_opts = salt.config._read_conf_file(
os.path.join(RUNTIME_VARS.CONF_DIR, "virt_minion_1")
)
virt_minion_1_opts["user"] = "root"
virt_minion_1_opts["cachedir"] = "cache"
virt_minion_1_opts["root_dir"] = "/etc/salt"
virt_minion_1_opts["pki_dir"] = "pki"
virt_minion_1_opts["hosts.file"] = "hosts"
virt_minion_1_opts["aliases.file"] = "aliases"
# This sub_minion also connects to master
sub_minion_opts = salt.config._read_conf_file(
os.path.join(RUNTIME_VARS.CONF_DIR, "sub_minion")
@ -950,6 +996,8 @@ class TestDaemon(object):
if transport == "tcp":
master_opts["transport"] = "tcp"
minion_opts["transport"] = "tcp"
virt_minion_0_opts["transport"] = "tcp"
virt_minion_1_opts["transport"] = "tcp"
sub_minion_opts["transport"] = "tcp"
syndic_master_opts["transport"] = "tcp"
proxy_opts["transport"] = "tcp"
@ -1063,6 +1111,8 @@ class TestDaemon(object):
master_opts["runtests_conn_check_port"] = get_unused_localhost_port()
minion_opts["runtests_conn_check_port"] = get_unused_localhost_port()
virt_minion_0_opts["runtests_conn_check_port"] = get_unused_localhost_port()
virt_minion_1_opts["runtests_conn_check_port"] = get_unused_localhost_port()
sub_minion_opts["runtests_conn_check_port"] = get_unused_localhost_port()
syndic_opts["runtests_conn_check_port"] = get_unused_localhost_port()
syndic_master_opts["runtests_conn_check_port"] = get_unused_localhost_port()
@ -1098,6 +1148,8 @@ class TestDaemon(object):
if entry in (
"master",
"minion",
"virt_minion_0",
"virt_minion_1",
"sub_minion",
"syndic",
"syndic_master",
@ -1129,6 +1181,31 @@ class TestDaemon(object):
salt.utils.yaml.safe_dump(
computed_config, fp_, default_flow_style=False
)
virt_minion_0_computed_config = copy.deepcopy(virt_minion_0_opts)
with salt.utils.files.fopen(
os.path.join(RUNTIME_VARS.TMP_VIRT_MINION_0_CONF_DIR, "minion"), "w"
) as wfh:
salt.utils.yaml.safe_dump(
virt_minion_0_computed_config, wfh, default_flow_style=False
)
shutil.copyfile(
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master"),
os.path.join(RUNTIME_VARS.TMP_VIRT_MINION_0_CONF_DIR, "master"),
)
virt_minion_1_computed_config = copy.deepcopy(virt_minion_1_opts)
with salt.utils.files.fopen(
os.path.join(RUNTIME_VARS.TMP_VIRT_MINION_1_CONF_DIR, "minion"), "w"
) as wfh:
salt.utils.yaml.safe_dump(
virt_minion_1_computed_config, wfh, default_flow_style=False
)
shutil.copyfile(
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master"),
os.path.join(RUNTIME_VARS.TMP_VIRT_MINION_1_CONF_DIR, "master"),
)
sub_minion_computed_config = copy.deepcopy(sub_minion_opts)
with salt.utils.files.fopen(
os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, "minion"), "w"
@ -1168,6 +1245,12 @@ class TestDaemon(object):
minion_opts = salt.config.minion_config(
os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion")
)
virt_minion_0_opts = salt.config.minion_config(
os.path.join(RUNTIME_VARS.TMP_VIRT_MINION_0_CONF_DIR, "minion")
)
virt_minion_1_opts = salt.config.minion_config(
os.path.join(RUNTIME_VARS.TMP_VIRT_MINION_1_CONF_DIR, "minion")
)
syndic_opts = salt.config.syndic_config(
os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, "master"),
os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, "minion"),
@ -1185,6 +1268,8 @@ class TestDaemon(object):
RUNTIME_VARS.RUNTIME_CONFIGS["master"] = freeze(master_opts)
RUNTIME_VARS.RUNTIME_CONFIGS["minion"] = freeze(minion_opts)
RUNTIME_VARS.RUNTIME_CONFIGS["syndic"] = freeze(syndic_opts)
RUNTIME_VARS.RUNTIME_CONFIGS["virt_minion_0"] = freeze(virt_minion_0_opts)
RUNTIME_VARS.RUNTIME_CONFIGS["virt_minion_1"] = freeze(virt_minion_1_opts)
RUNTIME_VARS.RUNTIME_CONFIGS["sub_minion"] = freeze(sub_minion_opts)
RUNTIME_VARS.RUNTIME_CONFIGS["syndic_master"] = freeze(syndic_master_opts)
RUNTIME_VARS.RUNTIME_CONFIGS["proxy"] = freeze(proxy_opts)
@ -1211,16 +1296,28 @@ class TestDaemon(object):
os.path.join(minion_opts["pki_dir"], "accepted"),
os.path.join(minion_opts["pki_dir"], "rejected"),
os.path.join(minion_opts["pki_dir"], "pending"),
os.path.join(virt_minion_0_opts["pki_dir"], "accepted"),
os.path.join(virt_minion_0_opts["pki_dir"], "rejected"),
os.path.join(virt_minion_0_opts["pki_dir"], "pending"),
os.path.join(virt_minion_1_opts["pki_dir"], "accepted"),
os.path.join(virt_minion_1_opts["pki_dir"], "rejected"),
os.path.join(virt_minion_1_opts["pki_dir"], "pending"),
os.path.join(sub_minion_opts["pki_dir"], "accepted"),
os.path.join(sub_minion_opts["pki_dir"], "rejected"),
os.path.join(sub_minion_opts["pki_dir"], "pending"),
os.path.dirname(master_opts["log_file"]),
minion_opts["extension_modules"],
virt_minion_0_opts["extension_modules"],
virt_minion_0_opts["pki_dir"],
virt_minion_1_opts["extension_modules"],
virt_minion_1_opts["pki_dir"],
sub_minion_opts["extension_modules"],
sub_minion_opts["pki_dir"],
proxy_opts["pki_dir"],
master_opts["sock_dir"],
syndic_master_opts["sock_dir"],
virt_minion_0_opts["sock_dir"],
virt_minion_1_opts["sock_dir"],
sub_minion_opts["sock_dir"],
minion_opts["sock_dir"],
RUNTIME_VARS.TMP_STATE_TREE,
@ -1235,6 +1332,8 @@ class TestDaemon(object):
cls.master_opts = master_opts
cls.minion_opts = minion_opts
# cls.proxy_opts = proxy_opts
cls.virt_minion_0_opts = virt_minion_0_opts
cls.virt_minion_1_opts = virt_minion_1_opts
cls.sub_minion_opts = sub_minion_opts
cls.syndic_opts = syndic_opts
cls.syndic_master_opts = syndic_master_opts
@ -1245,11 +1344,27 @@ class TestDaemon(object):
"""
Kill the minion and master processes
"""
try:
if hasattr(self.virt_minion_0_process, "terminate"):
self.virt_minion_0_process.terminate()
else:
log.error("self.virt_minion_0_process can't be terminated.")
except AttributeError:
pass
try:
if hasattr(self.virt_minion_1_process, "terminate"):
self.virt_minion_1_process.terminate()
else:
log.error("self.virt_minion_1_process can't be terminated.")
except AttributeError:
pass
try:
if hasattr(self.sub_minion_process, "terminate"):
self.sub_minion_process.terminate()
else:
log.error("self.sub_minion_process can't be terminate.")
log.error("self.sub_minion_process can't be terminated.")
except AttributeError:
pass
@ -1257,7 +1372,7 @@ class TestDaemon(object):
if hasattr(self.minion_process, "terminate"):
self.minion_process.terminate()
else:
log.error("self.minion_process can't be terminate.")
log.error("self.minion_process can't be terminated.")
except AttributeError:
pass

View file

@ -0,0 +1,116 @@
# basic config
# Connects to master
master: localhost
interface: 127.0.0.1
master_port: 64506
tcp_pub_port: 64530
tcp_pull_port: 64531
sock_dir: virt_minion_0_sock
id: virt_minion_0
open_mode: True
log_file: virt_minion_0.log
log_level_logfile: debug
pidfile: virt_minion_0.pid
# Give the minion extra attempts to find the master
# This is especially needed for the TCP tests as we
# wait for the master to come up in 2016.3. See #35489.
master_tries: 5
# module extension
test.foo: baz
integration.test: True
# Grains addons
grains:
test_grain: cheese
script: grail
alot: many
planets:
- mercury
- venus
- earth
- mars
level1:
level2: foo
companions:
one:
- susan
- ian
- barbara
tokenv2:
keystone.endpoint: http://localhost:35357/v2.0
keystone.token: administrator
tokenv3:
keystone.endpoint: http://localhost:35357/v3
keystone.token: administrator
adminv2:
keystone.user: admin
keystone.password: adminpass
keystone.tenant: admin
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
demov2:
keystone.user: demo
keystone.password: demopass
keystone.tenant: demo
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
adminv3:
keystone.user: admin
keystone.password: adminpass
keystone.tenant: admin
keystone.auth_url: 'http://127.0.0.1:5000/v3/'
demov3:
keystone.user: demo
keystone.password: demopass
keystone.tenant: demo
keystone.auth_url: 'http://127.0.0.1:5000/v3/'
zookeeper:
prod:
hosts: 'localhost:2181'
default_acl:
- username: daniel
password: test
read: true
write: true
create: true
delete: true
admin: true
username: daniel
password: test
hosts: 'localhost:2181'
default_acl:
- username: daniel
password: test
read: true
write: true
create: true
delete: true
admin: true
username: daniel
password: test
config_test:
spam: eggs
mine_functions:
test.ping: []
test.arg:
- isn't
- allow_tgt: 'sub_minion'
# sdb env module
osenv:
driver: env
# cmd blacklist
cmd_blacklist_glob:
- 'bad_command *'
- 'second_bad_command *'
autosign_grains:
- test_grain
# disable discovery for test suite saltstack/salt-jenkins#683
discovery: false
sdbvault:
driver: vault

View file

@ -0,0 +1,116 @@
# basic config
# Connects to master
master: localhost
interface: 127.0.0.1
master_port: 64506
tcp_pub_port: 64540
tcp_pull_port: 64541
sock_dir: virt_minion_1_sock
id: virt_minion_1
open_mode: True
log_file: virt_minion_1.log
log_level_logfile: debug
pidfile: virt_minion_1.pid
# Give the minion extra attempts to find the master
# This is especially needed for the TCP tests as we
# wait for the master to come up in 2016.3. See #35489.
master_tries: 5
# module extension
test.foo: baz
integration.test: True
# Grains addons
grains:
test_grain: cheese
script: grail
alot: many
planets:
- mercury
- venus
- earth
- mars
level1:
level2: foo
companions:
one:
- susan
- ian
- barbara
tokenv2:
keystone.endpoint: http://localhost:35357/v2.0
keystone.token: administrator
tokenv3:
keystone.endpoint: http://localhost:35357/v3
keystone.token: administrator
adminv2:
keystone.user: admin
keystone.password: adminpass
keystone.tenant: admin
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
demov2:
keystone.user: demo
keystone.password: demopass
keystone.tenant: demo
keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
adminv3:
keystone.user: admin
keystone.password: adminpass
keystone.tenant: admin
keystone.auth_url: 'http://127.0.0.1:5000/v3/'
demov3:
keystone.user: demo
keystone.password: demopass
keystone.tenant: demo
keystone.auth_url: 'http://127.0.0.1:5000/v3/'
zookeeper:
prod:
hosts: 'localhost:2181'
default_acl:
- username: daniel
password: test
read: true
write: true
create: true
delete: true
admin: true
username: daniel
password: test
hosts: 'localhost:2181'
default_acl:
- username: daniel
password: test
read: true
write: true
create: true
delete: true
admin: true
username: daniel
password: test
config_test:
spam: eggs
mine_functions:
test.ping: []
test.arg:
- isn't
- allow_tgt: 'sub_minion'
# sdb env module
osenv:
driver: env
# cmd blacklist
cmd_blacklist_glob:
- 'bad_command *'
- 'second_bad_command *'
autosign_grains:
- test_grain
# disable discovery for test suite saltstack/salt-jenkins#683
discovery: false
sdbvault:
driver: vault

View file

@ -11,10 +11,11 @@ from xml.etree import ElementTree
# Import Salt Testing libs
from tests.support.case import ModuleCase
from tests.support.helpers import requires_salt_modules
from tests.support.helpers import skip_if_binaries_missing, slowTest
@requires_salt_modules("virt.get_profiles")
@skip_if_binaries_missing("docker")
@slowTest
class VirtTest(ModuleCase):
"""
Test virt routines
@ -39,7 +40,9 @@ class VirtTest(ModuleCase):
"""
Test virt.get_profiles with the KVM profile
"""
profiles = self.run_function("virt.get_profiles", ["kvm"])
profiles = self.run_function(
"virt.get_profiles", ["kvm"], minion_tgt="virt_minion_0"
)
self.assertIsInstance(profiles, dict)
nic = profiles["nic"]["default"][0]
disk = profiles["disk"]["default"][0]
@ -57,7 +60,9 @@ class VirtTest(ModuleCase):
"""
Test virt.get_profiles with the VMware profile
"""
profiles = self.run_function("virt.get_profiles", ["vmware"])
profiles = self.run_function(
"virt.get_profiles", ["vmware"], minion_tgt="virt_minion_0"
)
self.assertIsInstance(profiles, dict)
nic = profiles["nic"]["default"][0]
disk = profiles["disk"]["default"][0]
@ -76,7 +81,9 @@ class VirtTest(ModuleCase):
"""
Test virt.get_profiles with the XEN profile
"""
profiles = self.run_function("virt.get_profiles", ["xen"])
profiles = self.run_function(
"virt.get_profiles", ["xen"], minion_tgt="virt_minion_0"
)
self.assertIsInstance(profiles, dict)
nic = profiles["nic"]["default"][0]
disk = profiles["disk"]["default"][0]
@ -94,7 +101,9 @@ class VirtTest(ModuleCase):
"""
Test virt.get_profiles with the Bhyve profile
"""
profiles = self.run_function("virt.get_profiles", ["bhyve"])
profiles = self.run_function(
"virt.get_profiles", ["bhyve"], minion_tgt="virt_minion_0"
)
self.assertIsInstance(profiles, dict)
nic = profiles["nic"]["default"][0]
disk = profiles["disk"]["default"][0]
@ -114,7 +123,7 @@ class VirtTest(ModuleCase):
"""
Test virt.all_capabilities
"""
caps = self.run_function("virt.all_capabilities")
caps = self.run_function("virt.all_capabilities", minion_tgt="virt_minion_0")
self.assertIsInstance(caps, dict)
self.assertIsInstance(caps["host"]["host"]["uuid"], str)
self.assertEqual(36, len(caps["host"]["host"]["uuid"]))
@ -124,7 +133,7 @@ class VirtTest(ModuleCase):
"""
Test virt.capabilities
"""
caps = self.run_function("virt.capabilities")
caps = self.run_function("virt.capabilities", minion_tgt="virt_minion_0")
self.assertIsInstance(caps, dict)
self.assertIsInstance(caps["host"]["uuid"], str)
self.assertEqual(36, len(caps["host"]["uuid"]))
@ -136,12 +145,16 @@ class VirtTest(ModuleCase):
Test virt.cpu_baseline
"""
vendors = ["Intel", "ARM", "AMD"]
cpu_baseline = self.run_function("virt.cpu_baseline", out="libvirt")
cpu_baseline = self.run_function(
"virt.cpu_baseline", out="libvirt", minion_tgt="virt_minion_0"
)
self.assertIsInstance(cpu_baseline, str)
cpu_baseline = ElementTree.fromstring(cpu_baseline)
self.assertIn(cpu_baseline.find("vendor").text, vendors)
cpu_baseline = self.run_function("virt.cpu_baseline", out="salt")
cpu_baseline = self.run_function(
"virt.cpu_baseline", out="salt", minion_tgt="virt_minion_0"
)
self.assertIsInstance(cpu_baseline, dict)
self.assertIn(cpu_baseline["vendor"], vendors)
@ -149,7 +162,7 @@ class VirtTest(ModuleCase):
"""
Test virt.freemem
"""
available_memory = self.run_function("virt.freemem")
available_memory = self.run_function("virt.freemem", minion_tgt="virt_minion_0")
self.assertIsInstance(available_memory, Number)
self.assertGreater(available_memory, 0)
@ -157,7 +170,7 @@ class VirtTest(ModuleCase):
"""
Test virt.freecpu
"""
available_cpus = self.run_function("virt.freecpu")
available_cpus = self.run_function("virt.freecpu", minion_tgt="virt_minion_0")
self.assertIsInstance(available_cpus, Number)
self.assertGreater(available_cpus, 0)
@ -165,7 +178,7 @@ class VirtTest(ModuleCase):
"""
Test virt.full_info
"""
info = self.run_function("virt.full_info")
info = self.run_function("virt.full_info", minion_tgt="virt_minion_0")
self.assertIsInstance(info, dict)
self.assertIsInstance(info["vm_info"], dict)
@ -187,7 +200,7 @@ class VirtTest(ModuleCase):
"""
Test virt.node_info
"""
info = self.run_function("virt.node_info")
info = self.run_function("virt.node_info", minion_tgt="virt_minion_0")
self.assertIsInstance(info, dict)
self.assertIsInstance(info["cpucores"], Number)
self.assertIsInstance(info["cpumhz"], Number)

View file

@ -916,8 +916,8 @@ class SaltTestsuiteParser(SaltCoverageTestingParser):
):
status.append(self.run_integration_suite(**TEST_SUITES[suite]))
return status
except TestDaemonStartFailed:
self.exit(status=2)
except TestDaemonStartFailed as err:
self.exit(status=2, msg=str(err))
def run_multimaster_tests(self):
"""

View file

@ -210,6 +210,10 @@ class AdaptedConfigurationTestCaseMixin(object):
return os.path.join(RUNTIME_VARS.TMP_SYNDIC_MASTER_CONF_DIR, "master")
if filename == "syndic":
return os.path.join(RUNTIME_VARS.TMP_SYNDIC_MINION_CONF_DIR, "minion")
if filename == "virt_minion_0":
return os.path.join(RUNTIME_VARS.TMP_VIRT_MINION_0_CONF_DIR, "minion")
if filename == "virt_minion_1":
return os.path.join(RUNTIME_VARS.TMP_VIRT_MINION_1_CONF_DIR, "minion")
if filename == "sub_minion":
return os.path.join(RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR, "minion")
if filename == "mm_master":

View file

@ -71,6 +71,8 @@ TMP_PILLAR_TREE = os.path.join(SYS_TMP_DIR, "salt-temp-pillar-tree")
TMP_PRODENV_STATE_TREE = os.path.join(SYS_TMP_DIR, "salt-temp-prodenv-state-tree")
TMP_PRODENV_PILLAR_TREE = os.path.join(SYS_TMP_DIR, "salt-temp-prodenv-pillar-tree")
TMP_CONF_DIR = TMP_MINION_CONF_DIR = os.path.join(TMP, "config")
TMP_VIRT_MINION_0_CONF_DIR = os.path.join(TMP_CONF_DIR, "virt_minion_0")
TMP_VIRT_MINION_1_CONF_DIR = os.path.join(TMP_CONF_DIR, "virt_minion_1")
TMP_SUB_MINION_CONF_DIR = os.path.join(TMP_CONF_DIR, "sub-minion")
TMP_SYNDIC_MINION_CONF_DIR = os.path.join(TMP_CONF_DIR, "syndic-minion")
TMP_SYNDIC_MASTER_CONF_DIR = os.path.join(TMP_CONF_DIR, "syndic-master")

View file

@ -13,6 +13,9 @@
from __future__ import absolute_import
import logging
import os
import subprocess
import time
from saltfactories.utils.processes.helpers import ( # pylint: disable=unused-import
collect_child_processes,
@ -115,6 +118,99 @@ class SaltSyndic(GetSaltRunFixtureMixin, PytestSaltSyndic):
"""
class SaltVirtContainer(object):
"""
Class which represents virt-minion container
"""
def __init__(self, container_name, container_img, daemon_config_dir):
self.container_name = container_name
self.container_img = container_img
self.daemon_config_dir = daemon_config_dir
self.pid_file = os.path.join(daemon_config_dir, container_name + ".pid")
def _run_cmd(self, cmd):
log.debug("Running command:\n%s", " ".join(cmd))
return subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True,
)
def start(self):
log.info(
"Minion log file: {}/{}.log".format(
self.daemon_config_dir, self.container_name
)
)
salt_root_path = os.path.abspath(os.path.join(__file__, "..", "..", ".."))
# Start container
process = self._run_cmd(
[
"docker",
"run",
"-d",
"--rm",
"--privileged",
"--cap-add=ALL",
"--network",
"host",
"--name",
self.container_name,
"--hostname",
self.container_name,
"-v",
salt_root_path + ":/salt",
"-v",
self.daemon_config_dir + ":/etc/salt",
self.container_img,
]
)
output = process.communicate()[0]
if process.returncode != 0:
raise RuntimeError(
"Failed to start '{}':\n{}".format(self.container_name, output)
)
def wait_until_minion_is_running(self, timeout=60):
"""
Wait until a pid file exists and check the container state every 10 sec
"""
log.info("Wating for pidfile ({}s timeout): {}".format(timeout, self.pid_file))
cmd = ["docker", "inspect", "-f", "{{.State.Running}}", self.container_name]
while not os.path.isfile(self.pid_file):
time.sleep(1)
if timeout > 0:
if timeout % 10 == 0:
process = self._run_cmd(cmd)
output = process.communicate()[0].decode("utf-8")
if process.returncode != 0 or output.strip().lower() != "true":
raise RuntimeError(
"Container '{}' isn't running:\n{}".format(
self.container_name, output
)
)
timeout -= 1
else:
self.terminate()
raise RuntimeError("Timeout: minion daemon isn't running.")
def terminate(self):
"""
Send a KILL signal to container.
"""
self._run_cmd(["docker", "kill", self.container_name])
os.remove(self.pid_file)
def start_virt_daemon(container_name, container_img, daemon_config_dir):
"""
Start a salt minion daemon inside a container.
"""
container = SaltVirtContainer(container_name, container_img, daemon_config_dir)
container.start()
container.wait_until_minion_is_running()
return container
def start_daemon(
daemon_name=None,
daemon_id=None,

View file

@ -387,6 +387,84 @@ def salt_minion_config(request, salt_factories, salt_master_config):
)
@pytest.fixture(scope="session")
def salt_virt_minion_0_config(request, salt_factories, salt_master_config):
with salt.utils.files.fopen(
os.path.join(RUNTIME_VARS.CONF_DIR, "virt_minion_0")
) as rfh:
config_defaults = yaml.deserialize(rfh.read())
config_defaults["hosts.file"] = "hosts"
config_defaults["aliases.file"] = "aliases"
config_defaults["transport"] = request.config.getoption("--transport")
config_overrides = {
"file_roots": {
"base": [
RUNTIME_VARS.TMP_STATE_TREE,
os.path.join(RUNTIME_VARS.FILES, "file", "base"),
],
# Alternate root to test __env__ choices
"prod": [
RUNTIME_VARS.TMP_PRODENV_STATE_TREE,
os.path.join(RUNTIME_VARS.FILES, "file", "prod"),
],
},
"pillar_roots": {
"base": [
RUNTIME_VARS.TMP_PILLAR_TREE,
os.path.join(RUNTIME_VARS.FILES, "pillar", "base"),
],
"prod": [RUNTIME_VARS.TMP_PRODENV_PILLAR_TREE],
},
}
return salt_factories.configure_minion(
request,
"virt_minion_0",
master_id="master",
config_defaults=config_defaults,
config_overrides=config_overrides,
)
@pytest.fixture(scope="session")
def salt_virt_minion_1_config(request, salt_factories, salt_master_config):
with salt.utils.files.fopen(
os.path.join(RUNTIME_VARS.CONF_DIR, "virt_minion_1")
) as rfh:
config_defaults = yaml.deserialize(rfh.read())
config_defaults["hosts.file"] = "hosts"
config_defaults["aliases.file"] = "aliases"
config_defaults["transport"] = request.config.getoption("--transport")
config_overrides = {
"file_roots": {
"base": [
RUNTIME_VARS.TMP_STATE_TREE,
os.path.join(RUNTIME_VARS.FILES, "file", "base"),
],
# Alternate root to test __env__ choices
"prod": [
RUNTIME_VARS.TMP_PRODENV_STATE_TREE,
os.path.join(RUNTIME_VARS.FILES, "file", "prod"),
],
},
"pillar_roots": {
"base": [
RUNTIME_VARS.TMP_PILLAR_TREE,
os.path.join(RUNTIME_VARS.FILES, "pillar", "base"),
],
"prod": [RUNTIME_VARS.TMP_PRODENV_PILLAR_TREE],
},
}
return salt_factories.configure_minion(
request,
"virt_minion_1",
master_id="master",
config_defaults=config_defaults,
config_overrides=config_overrides,
)
@pytest.fixture(scope="session")
def salt_sub_minion_config(request, salt_factories, salt_master_config):
with salt.utils.files.fopen(
@ -489,11 +567,15 @@ def bridge_pytest_and_runtests(
salt_syndic_config,
salt_master_config,
salt_minion_config,
salt_virt_minion_0_config,
salt_virt_minion_1_config,
salt_sub_minion_config,
):
# Make sure unittest2 uses the pytest generated configuration
RUNTIME_VARS.RUNTIME_CONFIGS["master"] = freeze(salt_master_config)
RUNTIME_VARS.RUNTIME_CONFIGS["minion"] = freeze(salt_minion_config)
RUNTIME_VARS.RUNTIME_CONFIGS["virt_minion_0"] = freeze(salt_virt_minion_0_config)
RUNTIME_VARS.RUNTIME_CONFIGS["virt_minion_1"] = freeze(salt_virt_minion_1_config)
RUNTIME_VARS.RUNTIME_CONFIGS["sub_minion"] = freeze(salt_sub_minion_config)
RUNTIME_VARS.RUNTIME_CONFIGS["syndic_master"] = freeze(salt_syndic_master_config)
RUNTIME_VARS.RUNTIME_CONFIGS["syndic"] = freeze(salt_syndic_config)
@ -505,6 +587,12 @@ def bridge_pytest_and_runtests(
RUNTIME_VARS.TMP_ROOT_DIR = salt_factories.root_dir.realpath().strpath
RUNTIME_VARS.TMP_CONF_DIR = os.path.dirname(salt_master_config["conf_file"])
RUNTIME_VARS.TMP_MINION_CONF_DIR = os.path.dirname(salt_minion_config["conf_file"])
RUNTIME_VARS.TMP_VIRT_MINION_0_CONF_DIR = os.path.dirname(
salt_virt_minion_0_config["conf_file"]
)
RUNTIME_VARS.TMP_VIRT_MINION_1_CONF_DIR = os.path.dirname(
salt_virt_minion_1_config["conf_file"]
)
RUNTIME_VARS.TMP_SUB_MINION_CONF_DIR = os.path.dirname(
salt_sub_minion_config["conf_file"]
)

View file

@ -186,6 +186,8 @@ RUNTIME_VARS = RuntimeVars(
TMP_CONF_CLOUD_PROVIDER_INCLUDES=os.path.join(
paths.TMP_CONF_DIR, "cloud.providers.d"
),
TMP_VIRT_MINION_0_CONF_DIR=paths.TMP_VIRT_MINION_0_CONF_DIR,
TMP_VIRT_MINION_1_CONF_DIR=paths.TMP_VIRT_MINION_1_CONF_DIR,
TMP_SUB_MINION_CONF_DIR=paths.TMP_SUB_MINION_CONF_DIR,
TMP_SYNDIC_MASTER_CONF_DIR=paths.TMP_SYNDIC_MASTER_CONF_DIR,
TMP_SYNDIC_MINION_CONF_DIR=paths.TMP_SYNDIC_MINION_CONF_DIR,