Skip to content

Commit 54a1b67

Browse files
committed
Set template for become test cases
1 parent f41f8fd commit 54a1b67

File tree

3 files changed

+169
-26
lines changed

3 files changed

+169
-26
lines changed

tests/conftest.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def pytest_addoption(parser):
3939
Add CLI options and modify options for pytest-ansible where needed.
4040
Note: Set the default to to None, otherwise when evaluating with `request.config.getoption("--zinventory"):`
4141
will always return true because a default will be returned.
42+
New option have been added to the execution of the command to allow the become method.
4243
"""
4344
parser.addoption(
4445
"--zinventory",
@@ -73,7 +74,13 @@ def pytest_addoption(parser):
7374
help="Str "
7475
)
7576
parser.addoption(
76-
"--ansible_user",
77+
"--password",
78+
action="store",
79+
default=None,
80+
help="Str "
81+
)
82+
parser.addoption(
83+
"--ssh_key",
7784
action="store",
7885
default=None,
7986
help="Str "
@@ -229,10 +236,20 @@ def get_config(request):
229236
path = request.config.getoption("--zinventory")
230237
yield path
231238

239+
@pytest.fixture(scope="function")
240+
def get_config_raw(request):
241+
""" Call the pytest-ansible plugin to check volumes on the system and work properly a list by session."""
242+
path = request.config.getoption("--zinventory-raw")
243+
yield path
244+
232245
@pytest.fixture(scope='session')
233246
def get_config_for_become(request):
234-
user_value = request.config.option.user_adm
235-
user_method = request.config.option.user_method
236-
ansible_promp = request.config.option.ansible_promp
237-
ansible_user = request.config.option.ansible_user
238-
return user_value, user_method, ansible_promp, ansible_user
247+
""" Return as a dict the values to be used on the test cases for become method"""
248+
become_config = {
249+
"user" : request.config.option.user_adm,
250+
"method" : request.config.option.user_method,
251+
"promp" : request.config.option.ansible_promp,
252+
"key" : request.config.option.password,
253+
"ssh_key" : request.config.option.ssh_key
254+
}
255+
return become_config

tests/functional/modules/test_zos_fetch_func.py

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import string
2222
import random
2323
import yaml
24+
import json
2425
import subprocess
2526
import tempfile
2627

@@ -36,6 +37,7 @@
3637
# pylint: disable-next=import-error
3738
from ibm_zos_core.tests.helpers.dataset import get_tmp_ds_name
3839
from ibm_zos_core.tests.helpers.utils import get_random_file_name
40+
from ibm_zos_core.tests.helpers.users import ManagedUserType, ManagedUser
3941

4042
__metaclass__ = type
4143

@@ -107,8 +109,9 @@
107109
ZOAU: "{0}"
108110
PYZ: "{1}"
109111
ansible_become_user: {2}
112+
110113
ansible_become_method: {3}
111-
ansible_su_prompt_l10n: {4}
114+
ansible_su_prompt_l10n: {4} {{ansible_become_user}}
112115
environment:
113116
_BPXK_AUTOCVT: "ALL"
114117
ZOAU_HOME: "{0}"
@@ -123,13 +126,27 @@
123126
PYTHONSTDINENCODING: "cp1047"
124127
tasks:
125128
- name: Fetch PDSE member while escalating privileges.
126-
ibm.ibm_zos_core.zos_fetch:
129+
zos_fetch:
127130
src: {6}
128131
dest: /tmp/
129132
flat: true
130133
become: true
131134
"""
132135

136+
ANSIBLE_CFD = """[defaults]
137+
forks = 25
138+
become_password_file = ./key.txt
139+
140+
[connection]
141+
pipelining = True
142+
143+
[ssh_connection]
144+
pipelining = True
145+
146+
[colors]
147+
verbose = green"""
148+
149+
KEY = "{0}"
133150

134151
def extract_member_name(data_set):
135152
start = data_set.find("(")
@@ -1030,29 +1047,55 @@ def test_fetch_uss_file_relative_path_not_present_on_local_machine(ansible_zos_m
10301047
os.remove(dest)
10311048

10321049

1033-
def test_extra_parameters(get_config_for_become, get_config, capsys):
1050+
def test_become_option_with_restricted_user(ansible_zos_module, z_python_interpreter, get_config_for_become, capsys):
1051+
"""
1052+
This tests check the become method it will pass the escalation but fail to execute the module because of lack of
1053+
permissions on on the system.
1054+
"""
10341055
with capsys.disabled():
1035-
adm_user, method, promp, ansible_us = get_config_for_become
1036-
promp = promp + f" {adm_user}"
1056+
managed_user = None
1057+
managed_user_test_case_name = "managed_user_limited_become_method"
1058+
become = get_config_for_become
1059+
try:
1060+
# Initialize the Managed user API from the pytest fixture.
1061+
managed_user = ManagedUser.from_fixture(ansible_zos_module, z_python_interpreter)
1062+
1063+
# Important: Execute the test case with the managed users execution utility.
1064+
# For become method is better to have verbose and debug as False only available for debug options.
1065+
managed_user.execute_managed_user_become_test(
1066+
managed_user_test_case = managed_user_test_case_name, become_method = become, debug = False,
1067+
verbose = False, managed_user_type=ManagedUserType.ZOAU_LIMITED_ACCESS_OPERCMD)
1068+
1069+
finally:
1070+
# Delete the managed user on the remote host to avoid proliferation of users.
1071+
managed_user.delete_managed_user()
10371072

1073+
def managed_user_limited_become_method(get_config_for_become, get_config_raw, capsys):
1074+
with capsys.disabled():
10381075
ds_name = get_tmp_ds_name()
10391076
ds_name = ds_name + "(MEMBER)"
10401077

1041-
path = get_config
1042-
with open(path, 'r') as file:
1043-
enviroment = yaml.safe_load(file)
1044-
1045-
ssh_key = enviroment["ssh_key"]
1046-
hosts = enviroment["host"].upper()
1047-
user = enviroment["user"].upper()
1048-
python_path = enviroment["python_path"]
1078+
# Get values from the command
1079+
adm_user = get_config_for_become["user"]
1080+
method = get_config_for_become["method"]
1081+
promp = get_config_for_become["promp"]
1082+
password = get_config_for_become["key"]
1083+
ssh_key = get_config_for_become["ssh_key"]
1084+
1085+
# Get values from the new configuration file
1086+
configuration = json.loads(get_config_raw)
1087+
hosts = configuration["host"]
1088+
user = configuration["user"]
1089+
python_path = configuration["python_interpreter"]
10491090
cut_python_path = python_path[:python_path.find('/bin')].strip()
1050-
zoau = enviroment["environment"]["ZOAU_ROOT"]
1091+
zoau = configuration["zoau"]
10511092
python_version = cut_python_path.split('/')[2]
10521093

10531094
try:
10541095
playbook = "playbook.yml"
10551096
inventory = "inventory.yml"
1097+
ansible_cfd = "ansible.cfd"
1098+
key_file = "key.txt"
10561099

10571100
os.system("echo {0} > {1}".format(quote(BECOME_USER.format(
10581101
zoau,
@@ -1071,7 +1114,12 @@ def test_extra_parameters(get_config_for_become, get_config, capsys):
10711114
python_path
10721115
)), inventory))
10731116

1074-
command = "ansible-playbook -i -vvv {0} {1}".format(
1117+
os.system("echo {0} > {1}".format(quote(ANSIBLE_CFD), ansible_cfd))
1118+
os.system("echo {0} > {1}".format(quote(KEY.format(
1119+
password
1120+
)), key_file))
1121+
1122+
command = "ansible-playbook -vvv -i {0} {1}".format(
10751123
inventory,
10761124
playbook
10771125
)
@@ -1086,5 +1134,7 @@ def test_extra_parameters(get_config_for_become, get_config, capsys):
10861134

10871135
assert result.returncode == 0
10881136
finally:
1089-
os.remove("inventory.yml")
1090-
os.remove("playbook.yml")
1137+
os.remove(playbook)
1138+
os.remove(inventory)
1139+
os.remove(key_file)
1140+
os.remove(ansible_cfd)

tests/helpers/users.py

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ def managed_user_test_demo_how_to_use_managed_user(ansible_zos_module):
149149
Who am I AFTER asking for a managed user = LJBXMONV
150150
"""
151151

152-
def __init__(self, model_user: str = None, remote_host: str = None, zoau_path: str = None, pyz_path: str = None, pythonpath: str = None, volumes: str = None, hostpattern: str = None) -> None:
152+
def __init__(self, model_user: str = None, remote_host: str = None, zoau_path: str = None, pyz_path: str = None, pythonpath: str = None, volumes: str = None, python_interpreter: str=None, hostpattern: str = None) -> None:
153153
"""
154154
Initialize class with necessary parameters.
155155
@@ -168,6 +168,7 @@ def __init__(self, model_user: str = None, remote_host: str = None, zoau_path: s
168168
self._pyz_path = pyz_path
169169
self._pythonpath = pythonpath
170170
self._volumes = volumes
171+
self._python_interpreter = python_interpreter
171172
self._hostpattern = "all" # can also get it from options host_pattern
172173
self._managed_racf_user = None
173174
self._managed_user_group = None
@@ -183,6 +184,7 @@ def from_fixture(cls, pytest_module_fixture, pytest_interpreter_fixture):
183184
inventory_hosts = pytest_module_fixture["options"]["inventory_manager"]._inventory.hosts
184185
inventory_list = list(inventory_hosts.values())[0].vars.get('ansible_python_interpreter').split(";")
185186
environment_vars = pytest_interpreter_fixture[0]
187+
python_interpreter = pytest_interpreter_fixture[1]
186188

187189
zoau_path = environment_vars.get("ZOAU_HOME")
188190
pythonpath = environment_vars.get("PYTHONPATH")
@@ -193,7 +195,7 @@ def from_fixture(cls, pytest_module_fixture, pytest_interpreter_fixture):
193195
# volumes to extra_args.
194196
volumes = "000000,222222"
195197
hostpattern = pytest_module_fixture["options"]["host_pattern"]
196-
return cls(model_user, remote_host, zoau_path, pyz_path, pythonpath, volumes, hostpattern)
198+
return cls(model_user, remote_host, zoau_path, pyz_path, pythonpath, volumes, python_interpreter, hostpattern)
197199

198200

199201
def _connect(self, remote_host:str , model_user: str, command: str) -> List[str]:
@@ -423,7 +425,7 @@ def execute_managed_user_test(self, managed_user_test_case: str, debug: bool = F
423425
inventory.update({'host': self._remote_host})
424426
inventory.update({'user': self._managed_racf_user})
425427
inventory.update({'zoau': self._zoau_path}) # get this from fixture
426-
inventory.update({'pyz': self._pyz_path}) # get this from fixture
428+
inventory.update({'pyz': self._python_path}) # get this from fixture
427429
inventory.update({'pythonpath': self._pythonpath}) # get this from fixture
428430
extra_args = {}
429431
extra_args.update({'extra_args':{'volumes':self._volumes.split(",")}}) # get this from fixture
@@ -440,6 +442,80 @@ def execute_managed_user_test(self, managed_user_test_case: str, debug: bool = F
440442
print(result.stdout)
441443

442444

445+
def execute_managed_user_become_test(self, managed_user_test_case: str, become_method: dict[str, str], debug: bool = False, verbose: bool = False, managed_user_type: ManagedUserType = None) -> None:
446+
"""
447+
Executes the test case using articulated pytest options when the test case needs a managed user. This is required
448+
to execute any test that needs a manage user, a wrapper test case should call this method, the 'managed_user_test_case'
449+
must begin with 'managed_user_' as opposed to 'test_', this because the pytest command built will override the ini
450+
with this value. For running playbooks with become method.
451+
452+
Parameters
453+
----------
454+
managed_user_test_case (str)
455+
The managed user test case that begins with 'managed_user_'
456+
become_method (Dict[str, str])
457+
Key value pair of user command and options to set playbook for become method.
458+
debug (str)
459+
Enable verbose output for pytest, the equivalent command line option of '-s'.
460+
verbose (str)
461+
Enables pytest verbosity level 4 (-vvvv)
462+
463+
Raises
464+
------
465+
Exception - if the test case fails (non-zero RC from pytest/subprocess), the stdout and stderr are returned for evaluation.
466+
ValueError - if the managed user is not created, you must call `self._managed_racf_user()`.
467+
468+
See Also
469+
--------
470+
:py:member:`_create_managed_user` required before this function can be used as a managed user needs to exist.
471+
"""
472+
473+
if managed_user_test_case is None or not managed_user_test_case.startswith("managed_user_"):
474+
raise ValueError("Test cases using a managed user must begin with 'managed_user_' to be collected for execution.")
475+
476+
# if not self._managed_racf_user:
477+
# raise ValueError("No managed user has been created, please ensure that the method `self._managed_racf_user()` has been called prior.")
478+
479+
self._create_managed_user(managed_user_type)
480+
481+
# Get the file path of the caller function
482+
calling_test_path = inspect.getfile(inspect.currentframe().f_back)
483+
484+
# Get the test case name that this code is being run from, this is not an function arg.
485+
# managed_user_test_case = inspect.stack()[1][3]
486+
487+
testcase = f"{calling_test_path}::{managed_user_test_case}"
488+
# hostpattern = "all" # can also get it from options host_pattern
489+
capture = " -s"
490+
verbosity = " -vvvv"
491+
492+
inventory: dict [str, str] = {}
493+
inventory.update({'host': self._remote_host})
494+
inventory.update({'user': self._managed_racf_user})
495+
inventory.update({'zoau': self._zoau_path}) # get this from fixture
496+
inventory.update({'pyz': self._pyz_path}) # get this from fixture
497+
inventory.update({'python_interpreter': self._python_interpreter}) # get this from fixture
498+
extra_args = {}
499+
extra_args.update({'extra_args':{'volumes':self._volumes.split(",")}}) # get this from fixture
500+
inventory.update(extra_args)
501+
502+
node_inventory = json.dumps(inventory)
503+
504+
# Get options for become method
505+
user = become_method["user"]
506+
method = become_method["method"]
507+
promp = become_method["promp"]
508+
key = become_method["key"]
509+
ssh_key = become_method["ssh_key"]
510+
# Carefully crafted 'pytest' command to be allow for it to be called from anther test driven by pytest and uses the zinventory-raw fixture.
511+
pytest_cmd = f"""pytest {testcase} --override-ini "python_functions=managed_user_" --host-pattern={self._hostpattern}{capture if debug else ""}{verbosity if verbose else ""} --zinventory-raw='{node_inventory}' --user_adm {user} --user_method {method} --ansible_promp '{promp}' --password {key} --ssh_key '{ssh_key}'"""
512+
result = subprocess.run(pytest_cmd, capture_output=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True)
513+
if result.returncode != 0:
514+
raise Exception(result.stdout + result.stderr)
515+
else:
516+
print(result.stdout)
517+
518+
443519
def delete_managed_user(self) -> None:
444520
"""
445521
Delete managed user from z/OS managed node. Performs clean up of the remote

0 commit comments

Comments
 (0)