-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add submodule to run tests on remote machines.
Includes a session-level fixture to turn on machines before tests run and turn them off at end of session. The scan test is parameterized on the profiles listed in config file, and runs once per profile. In each profile, all hosts have known facts compared to collected facts. If no config file is found, the fixture quits and config file dependent tests are skipped. Closes #24 Closes #25
- Loading branch information
Showing
11 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# coding=utf-8 | ||
"""Module containing tools for controlling and testing remote systems.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# coding=utf-8 | ||
"""Pytest customizations and fixtures for tests to execute on remote hosts.""" | ||
import os | ||
import pytest | ||
from camayoc import config | ||
from camayoc.exceptions import ConfigFileNotFoundError | ||
from time import sleep | ||
from camayoc.constants import ( | ||
VCENTER_DATA_CENTER, | ||
VCENTER_CLUSTER, | ||
VCENTER_HOST, | ||
) | ||
|
||
|
||
@pytest.fixture(scope='session', autouse=True) | ||
def manage_systems(request): | ||
"""Fixture that manages remote systems. | ||
At the beginning of a session, hosts will be turned on. We must wait for | ||
the machines to complete booting so there is about 30 seconds of overhead | ||
at the beginning before test begin executing. | ||
At the end of the session, they are turned off. | ||
Hosts must have a "provider" specified in the config file, otherwise they | ||
will be skipped and assumed to be accessible by rho. | ||
Hosts that are powered on at the beginning of the session are then powered | ||
down at the end of the session. | ||
Example:: | ||
#/home/user/.config/camayoc/config.yaml | ||
rho: | ||
auths: | ||
- username: root | ||
name: sonar | ||
sshkeyfile: /home/elijah/.ssh/id_sonar_jenkins_rsa | ||
hosts: | ||
- hostname: string-name-of-vm-on-vcenter | ||
ip: XX.XX.XXX.XX | ||
provider: vcenter | ||
facts: | ||
connection.port: 22 | ||
cpu.count: 1 | ||
profiles: | ||
- name: profile1 | ||
auths: | ||
- auth1 | ||
hosts: | ||
- XX.XX.XXX.XX | ||
vcenter: | ||
hostname: **Required** -- url to vcenter instance | ||
username: **optional** -- will first try environment variable $VCUSER | ||
password: **optional** -- will first try environment variable $VCPASS | ||
.. warning:: | ||
It is a bad idea to have multiple sessions of pytest running | ||
concurrently against the same hosts/config file. | ||
If one session ends before another, it will power down the | ||
machines leaving the other one to fail. | ||
""" | ||
from pyVim.connect import SmartConnect, Disconnect | ||
import ssl | ||
|
||
try: | ||
cfg = config.get_config() | ||
except ConfigFileNotFoundError: | ||
# if we don't have a config file, do nothing | ||
return | ||
|
||
s = ssl.SSLContext(ssl.PROTOCOL_TLSv1) | ||
s.verify_mode = ssl.CERT_NONE | ||
# try and make a secure connection first | ||
if 'vcenter' in cfg.keys(): | ||
if os.getenv('VCUSER', False): | ||
vcuser = os.environ['VCUSER'] | ||
else: | ||
vcuser = cfg['vcenter']['username'] | ||
if os.getenv('VCPASS', False): | ||
vcpassword = os.environ['VCPASS'] | ||
else: | ||
vcpassword = cfg['vcenter']['password'] | ||
try: | ||
c = SmartConnect( | ||
host=cfg['vcenter']['hostname'], | ||
user=vcuser, | ||
pwd=vcpassword | ||
) | ||
except ssl.SSLError: | ||
c = SmartConnect( | ||
host=cfg['vcenter']['hostname'], | ||
user=vcuser, | ||
pwd=vcpassword, | ||
sslContext=s | ||
) | ||
# these index choices are particular to our VCenter setup | ||
fldr = c.content.rootFolder.childEntity[VCENTER_DATA_CENTER].hostFolder | ||
vm_host = fldr.childEntity[VCENTER_CLUSTER].host[VCENTER_HOST] | ||
# the host has a property "vm" which is a list of VMs | ||
vms = vm_host.vm | ||
for host in cfg['rho']['hosts']: | ||
if ('provider' in host.keys()) and ( | ||
host['provider'] == 'vcenter'): | ||
for vm in vms: | ||
if vm.name == host['hostname']: | ||
if vm.runtime.powerState == 'poweredOff': | ||
vm.PowerOnVM_Task() | ||
# need to wait for machines to boot and start sshd | ||
sleep(30) | ||
|
||
def shutdown_systems(): | ||
"""Turn off hosts at the end of a session. | ||
This runs once at the end of each session. | ||
""" | ||
try: | ||
cfg = config.get_config() | ||
except ConfigFileNotFoundError: | ||
# if we don't have a config file, do nothing | ||
return | ||
|
||
for host in cfg['rho']['hosts']: | ||
if ('provider' in host.keys()) and (host['provider'] == 'vcenter'): | ||
for vm in vms: | ||
if vm.name == host['hostname']: | ||
if vm.runtime.powerState == 'poweredOn': | ||
vm.PowerOffVM_Task() | ||
Disconnect(c) | ||
|
||
# this line makes this part of the fixture only run at end of session | ||
# pytest provides us this request object | ||
request.addfinalizer(shutdown_systems) # noqa: F821 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# coding=utf-8 | ||
"""Module of tests for running rho against remote systems.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
# coding=utf-8 | ||
"""Tests for ``rho scan`` commands. | ||
These tests are parametrized on the profiles listed in the config file. If scan | ||
is successful, all facts will be validated before test fails, and then all | ||
failed facts will be reported with associated host. | ||
:caseautomation: automated | ||
:casecomponent: scan | ||
:caseimportance: high | ||
:requirement: RHO | ||
:testtype: functional | ||
:upstream: yes | ||
""" | ||
|
||
import csv | ||
import os | ||
import pexpect | ||
import pytest | ||
from io import BytesIO | ||
|
||
from camayoc import config | ||
from camayoc.tests.rho.utils import auth_add, input_vault_password | ||
from camayoc.exceptions import ConfigFileNotFoundError | ||
|
||
|
||
def profiles(): | ||
"""Gather profiles from config file.""" | ||
try: | ||
profs = config.get_config()['rho']['profiles'] | ||
except ConfigFileNotFoundError: | ||
profs = [] | ||
return profs | ||
|
||
|
||
# The test will execute once per profile. | ||
@pytest.mark.parametrize('profile', profiles()) | ||
def test_scan(isolated_filesystem, profile): | ||
"""Scan the machines listed in profile. | ||
Then test facts for each host in profile. | ||
:id: 6ee18084-86db-45ea-8fdd-59fed5639170 | ||
:description: Scan a machine and test collected facts. | ||
:steps: | ||
1) Run ``rho scan --profile <profile> --reportfile <reportfile>`` | ||
2) Validate collected facts against known facts in config file | ||
:expectedresults: | ||
A scan is performed and a report with valid facts are | ||
generated. | ||
""" | ||
cfg = config.get_config() | ||
|
||
for auth in cfg['rho']['auths']: | ||
auth_add({ | ||
'name': auth['name'], | ||
'username': auth['username'], | ||
'sshkeyfile': auth['sshkeyfile'], | ||
}) | ||
|
||
auths = ' '.join(item for item in profile['auths']) | ||
hosts = ' '.join(item for item in profile['hosts']) | ||
rho_profile_add = pexpect.spawn( | ||
'rho profile add --name {} --auth {} --hosts {}' | ||
.format(profile['name'], auths, hosts) | ||
) | ||
input_vault_password(rho_profile_add) | ||
assert rho_profile_add.expect( | ||
'Profile "{}" was added'.format(profile['name'])) == 0 | ||
assert rho_profile_add.expect(pexpect.EOF) == 0 | ||
rho_profile_add.close() | ||
assert rho_profile_add.exitstatus == 0 | ||
|
||
reportfile = '{}-report.csv'.format(profile['name']) | ||
rho_scan = pexpect.spawn( | ||
'rho scan --profile {} --reportfile {}' | ||
.format(profile['name'], reportfile), | ||
timeout=300, | ||
) | ||
input_vault_password(rho_scan) | ||
rho_scan.logfile = BytesIO() | ||
assert rho_scan.expect(pexpect.EOF) == 0 | ||
logfile = rho_scan.logfile.getvalue().decode('utf-8') | ||
rho_scan.logfile.close() | ||
rho_scan.close() | ||
assert rho_scan.exitstatus == 0, logfile | ||
assert os.path.isfile(reportfile) | ||
|
||
# we will collect errors in scanned facts and report at end of test on the | ||
# results if collected facts do not match expected values. | ||
scan_errors = [] | ||
with open(reportfile) as csvfile: | ||
scan_results = csv.DictReader(csvfile) | ||
# each row corresponds to a scanned host | ||
for row in scan_results: | ||
known_facts = {} | ||
for host in cfg['rho']['hosts']: | ||
# find the facts for the scanned host we are inspecting | ||
if host['ip'] == row['connection.host']: | ||
known_facts = host['facts'] | ||
break | ||
for fact in known_facts.keys(): | ||
try: | ||
assert (str(known_facts[fact]) in str(row[fact])) | ||
except AssertionError: | ||
msg = 'Test failed on host {} in profile {}. \ | ||
Scan found {} = {} instead of {}.'.format( | ||
host['ip'], | ||
profile['name'], | ||
fact, | ||
row[fact], | ||
known_facts[fact], | ||
) | ||
scan_errors.append(msg) | ||
if len(scan_errors) != 0: | ||
msg = '\n'.join(e for e in scan_errors) | ||
raise AssertionError(msg) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
camayoc\.tests\.remote\.conftest module | ||
======================================= | ||
|
||
.. automodule:: camayoc.tests.remote.conftest | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
camayoc\.tests\.remote\.rho package | ||
=================================== | ||
|
||
.. automodule:: camayoc.tests.remote.rho | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: | ||
|
||
Submodules | ||
---------- | ||
|
||
.. toctree:: | ||
|
||
camayoc.tests.remote.rho.test_scan | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
camayoc\.tests\.remote\.rho\.test\_scan module | ||
============================================== | ||
|
||
.. automodule:: camayoc.tests.remote.rho.test_scan | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
camayoc\.tests\.remote package | ||
============================== | ||
|
||
.. automodule:: camayoc.tests.remote | ||
:members: | ||
:undoc-members: | ||
:show-inheritance: | ||
|
||
Subpackages | ||
----------- | ||
|
||
.. toctree:: | ||
|
||
camayoc.tests.remote.rho | ||
|
||
Submodules | ||
---------- | ||
|
||
.. toctree:: | ||
|
||
camayoc.tests.remote.conftest | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ Subpackages | |
|
||
.. toctree:: | ||
|
||
camayoc.tests.remote | ||
camayoc.tests.rho | ||
|
||
Submodules | ||
|