Skip to content

Commit

Permalink
Add ability to test facts based on privilege level
Browse files Browse the repository at this point in the history
Adds constants that contain what denial messages an underprivileged user
may encounter for given facts.

Now when testing facts, if it is a fact that's data varies based on
privilege level, we only test for an exact match to the test data for
privileged users, and otherwise test against the various denial messages
acceptable for that fact.

This commit also updates the gen_config.py script to be able to take in
a new column of data in the profiles csv indicating if the profile is
one with a privileged user.

Now no profiles are re-used from the original config, only profiles
indicated in the profiles csv are used.
  • Loading branch information
kdelee committed Nov 3, 2017
1 parent 0895958 commit 52faa19
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 18 deletions.
161 changes: 161 additions & 0 deletions camayoc/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,167 @@
)
"""List of RHO's RHEL facts."""

RHO_PRIVILEGED_FACTS = {
'date.yum_history': {
'denials': [
'sudo: a password is required',
'error'
]
},
'date.anaconda_log': {
'denials': [
'error',
]
},
'dmi.bios-vendor': {
'denials': [
'sudo: a password is required',
'N/A (dmidecode not found)',
'error',
]
},
'dmi.bios-version': {
'denials': [
'sudo: a password is required',
'N/A (dmidecode not found)',
'error',
]
},
'dmi.processor-family': {
'denials': [
'sudo: a password is required',
'N/A (dmidecode not found)',
'error',
]
},
'dmi.system-manufacturer': {
'denials': [
'sudo: a password is required',
'N/A (dmidecode not found)',
'error',
]
},
'jboss.brms.kie-war-ver': {
'denials': [
'(jboss.brms.kie-war-ver not found)',
]
},
'jboss.brms.kie-api-ver': {
'denials': [
'(jboss.brms.kie-api-ver not found)',
]
},
'jboss.brms.drools-core-ver': {
'denials': [
'(jboss.brms.drools-core-ver not found)',
]
},
'jboss.fuse.cxf-ver': {
'denials': [
'(jboss.fuse.cxf-ver not found)',
]
},
'jboss.fuse.camel-ver': {
'denials': [
'(jboss.fuse.camel-ver not found)',
]
},
'jboss.fuse.activemq-ver': {
'denials': [
'(jboss.fuse.activemq-ver not found)',
]
},
'subman.consumed': {
'denials': [
'sudo: a password is required',
'N/A (subscription-manager not found)',
'error',
]
},
'subman.cpu.core(s)_per_socket': {
'denials': [
'sudo: a password is required',
'N/A (subscription-manager not found)',
'error',
]
},
'subman.cpu.cpu(s)': {
'denials': [
'sudo: a password is required',
'N/A (subscription-manager not found)',
'error',
]
},
'subman.cpu.cpu_socket(s)': {
'denials': [
'sudo: a password is required',
'N/A (subscription-manager not found)',
'error',
]
},
'subman.virt.host_type': {
'denials': [
'sudo: a password is required',
'N/A (subscription-manager not found)',
'error',
]
},
'subman.virt.is_guest': {
'denials': [
'sudo: a password is required',
'N/A (subscription-manager not found)',
'error',
]
},
'subman.virt.uuid': {
'denials': [
'sudo: a password is required',
'N/A (subscription-manager not found)',
'error',
]
},
'virt.type': {
'denials': [
'sudo: a password is required',
'error',
'N/A (dmidecode not found)',
'',
]
},
'virt.virt': {
'denials': [
'sudo: a password is required',
'error',
'N/A (dmidecode not found)',
'',
]
},
'virt-what.type': {
'denials': [
'sudo: a password is required',
'error',
'N/A (virt-what not found)',
'',
]
},
'virt.num_guests': {
'denials': [
'sudo: a password is required',
'N/A (virsh not found)',
'error',
'',
]
},
'virt.num_running_guests': {
'denials': [
'sudo: a password is required',
'N/A (virsh not found)',
'error',
'',
]
},
}

RHO_DEFAULT_FACTS = RHO_CONNECTION_FACTS + RHO_JBOSS_FACTS + RHO_RHEL_FACTS
"""List of RHO's default facts."""

Expand Down
40 changes: 32 additions & 8 deletions camayoc/tests/remote/rho/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@
from camayoc.tests.rho.utils import auth_add, input_vault_password
from camayoc.utils import _XDG_ENV_VARS


def pytest_generate_tests(metafunc):
"""Do magic things with parameters.
This function enables us to create custom marks and do special things to
the parameters of fixtures based on the value of the marks.
"""
if hasattr(metafunc.function, 'facts_needed'):
# this grabs the parameters handed to the pytest.mark.parametrize
# associated with the function that is marked 'facts_needed'
Expand All @@ -27,6 +34,20 @@ def pytest_generate_tests(metafunc):


def testable_facts(profiles_to_scan):
"""Perform scans on each profile and return facts.
This function will attempt to scan all profiles listed in config file. For
those that are successful, facts will be returned so the test may be
parametrized on them.
Additional data about the facts like what host, profile, auth, actual and
expected value are collected into a single dictionary with the scanned fact
and passed to the test that is marked as follows:
@pytest.mark.facts_needed
@pytest.mark.parametrize('fact', profiles())
"""
profiles_to_scan = profiles_to_scan.args[1]
cfg = config.get_config()
facts_to_test = []
Expand Down Expand Up @@ -103,10 +124,13 @@ def testable_facts(profiles_to_scan):
'expected': known_facts.get(fact),
'actual': row.get(fact),
'host': host['hostname'],
'host-ip': host['ip'],
'profile': row['profile'],
'auth': row['auth'],
'scandir': path,
})
'privileged': profile.get('privileged')
}
)
break
for envvar in _XDG_ENV_VARS:
os.environ[envvar] = ''
Expand All @@ -116,10 +140,10 @@ def testable_facts(profiles_to_scan):
except (OSError, IOError):
pass
return pytest.mark.parametrize(
'fact',
facts_to_test,
ids=['{}-{}-{}-{}'.format(
fact['host'],
fact['profile'],
fact['auth'],
fact['fact']) for fact in facts_to_test])
'fact',
facts_to_test,
ids=['{}-{}-{}-{}'.format(
fact['host-ip'],
fact['profile'],
fact['auth'],
fact['fact']) for fact in facts_to_test])
25 changes: 21 additions & 4 deletions camayoc/tests/remote/rho/test_scan.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@
:upstream: yes
"""

import csv
import os
import pexpect
import pytest
import unittest
from io import BytesIO

from camayoc import config
from camayoc.exceptions import ConfigFileNotFoundError
from camayoc.constants import RHO_PRIVILEGED_FACTS
from camayoc.tests.rho.utils import auth_add, input_vault_password


Expand All @@ -34,11 +33,12 @@ def profiles():
profs = []
return profs


# The test will execute once per profile.
@pytest.mark.parametrize(
'profile', profiles(),
ids=[p['name'] for p in profiles()]
)
)
def test_scan(isolated_filesystem, profile):
"""Scan the machines listed in profile.
Expand Down Expand Up @@ -86,7 +86,24 @@ def test_scan(isolated_filesystem, profile):
assert rho_scan.exitstatus == 0, logfile
assert os.path.isfile(reportfile)


@pytest.mark.facts_needed
@pytest.mark.parametrize('fact', profiles())
def test_facts(fact):
assert fact['expected'] == fact['actual']
"""Test each fact from the scan reports.
:id: d9eb29bd-1b61-421a-b680-f494e868b11e
:description: Test the facts from a scan report
:steps:
1) Test facts collected from scans against expected data
:expectedresults:
The fact will contain expected data
"""
if fact['fact'] not in RHO_PRIVILEGED_FACTS:
assert fact['expected'] == fact['actual']

if fact['privileged'] and fact['fact'] in RHO_PRIVILEGED_FACTS:
assert fact['expected'] == fact['actual']

if fact['fact'] in RHO_PRIVILEGED_FACTS and not fact['privileged']:
assert fact['actual'] in RHO_PRIVILEGED_FACTS[fact['fact']]['denials']
7 changes: 7 additions & 0 deletions docs/api/camayoc.tests.remote.rho.conftest.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
camayoc\.tests\.remote\.rho\.conftest module
============================================

.. automodule:: camayoc.tests.remote.rho.conftest
:members:
:undoc-members:
:show-inheritance:
1 change: 1 addition & 0 deletions docs/api/camayoc.tests.remote.rho.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ Submodules

.. toctree::

camayoc.tests.remote.rho.conftest
camayoc.tests.remote.rho.test_scan

30 changes: 24 additions & 6 deletions scripts/gen_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
.. note::
This tool reads in your camayoc config file in $XDG_CONFIG_HOME and outputs
it to the $CWD with new data. It discards old hosts data but uses auth,
profile, and any other data provided in your $XDG_CONFIG_HOME config file
it to the $CWD with new data. It discards old hosts and profile data but
uses auth data and other data provided in your $XDG_CONFIG_HOME config file
(if it exists). This does not modify your $XDG_CONFIG_HOME config file
directly, that is left to you. Always backup your old config file before
you replace it with the generated one!
Expand Down Expand Up @@ -89,18 +89,34 @@ def gen_hosts(profilepath, reportpath):
'facts': {},
'profile': row.get('profile', uuid.uuid4()),
'auth': row.get('auth', ''),
'privileged': row.get('privileged')
})
with open(reportpath) as csvfile:
scan_results = csv.DictReader(csvfile)
for row in scan_results:
for host in hosts:
if host['ip'] == row.get('connection.host'):
host['facts'] = {
k: v for k, v in row.items() if 'date' not in k
}
for k, v in row.items():
if 'date' not in k:
if k in [
'cpu.bogomips',
'dmi.bios-version'] and is_float(v):
host['facts'][k] = '%.2f' % float(v)
else:
host['facts'][k] = v

return hosts


def is_float(s):
"""Test to see if the string can be converted to float."""
try:
float(s)
return True
except ValueError:
return False


def gen_profiles(cfg, hosts):
"""Generate profiles for a given config and hosts.
Expand All @@ -126,8 +142,8 @@ def gen_profiles(cfg, hosts):
"""
profiles = cfg.get('profiles', [])
for host in hosts:
profile_found = False
# first append hosts to any existing profiles
profile_found = False
for profile in profiles:
if host.get('profile', '') == \
profile.get('name', uuid.uuid4()):
Expand All @@ -143,7 +159,9 @@ def gen_profiles(cfg, hosts):
'name': host['profile'],
'hosts': [host['ip']],
'auths': [host['auth']],
'privileged': host.get('privileged')
})

return profiles


Expand Down

0 comments on commit 52faa19

Please sign in to comment.