Skip to content

Commit ee78440

Browse files
committed
Implement granular Ansible verbosity control via configuration file
Introduce a new `verbosity` field under the `ansible` section in the global YAML configuration to allow fine-grained control over Ansible's log level, replacing the previous reliance on the global `--verbose` CLI flag for subprocess execution. Add `verbosity` validation logic to ensure values are integers between 1 and 6. Update Ansible command generation to dynamically set the `-v` flag count based on the new configuration field. Update `sap_hana_install` documentation to clarify the relationship between `sap_hana_install_update_firewall` and the global `firewall_cfg`.
1 parent 797be14 commit ee78440

13 files changed

Lines changed: 148 additions & 46 deletions

File tree

ansible/playbooks/roles/sap_hana_install/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,9 @@ or if it will add further hosts to an existing SAP HANA system as specified by v
147147
`sap_hana_install_addhosts`. Default is `yes` for a fresh SAP HANA installation.
148148
149149
The role can be configured to also set the required firewall ports for SAP HANA. If this is desired, set
150-
the variable `sap_hana_install_update_firewall` to `true` (default is `false` that means the role will not touch
151-
any firewall related system settings, leaving whatever the system has unchanged). The firewall ports are defined
150+
the variable `sap_hana_install_update_firewall` to `true` (default is `false`). Note that in this project,
151+
this variable is automatically influenced by the global `firewall_cfg` variable defined in `hana_vars.yaml`.
152+
If `firewall_cfg` is set to `enable`, the firewall ports will be managed. The firewall ports are defined
152153
in a variable which is compatible with the variable structure used by Linux System Role `firewall`.
153154
The firewall ports for SAP HANA are defined in member `port` of the first field of variable
154155
`sap_hana_install_firewall` (`sap_hana_install_firewall[0].port`), see file `defaults/main.yml`. If the

scripts/qesap/lib/cmds.py

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,14 @@ def cmd_configure(configure_data, base_project, dryrun):
140140
return Status("ok")
141141

142142

143-
def cmd_deploy(configure_data, base_project, dryrun=False, verbose=False):
143+
def cmd_deploy(configure_data, base_project, dryrun=False):
144144
"""Main executor for the deploy sub-command
145145
146146
Args:
147147
configure_data (obj): configuration structure
148148
base_project (str): base project path where to
149149
look for the Terraform and Ansible files
150150
dryrun (bool): enable dryrun execution mode
151-
verbose (bool): enable more verbosity
152151
153152
Returns:
154153
int: execution result, 0 means OK. It is mind to be used as script exit code
@@ -161,26 +160,25 @@ def cmd_deploy(configure_data, base_project, dryrun=False, verbose=False):
161160
)
162161
if res != 0:
163162
return res
164-
return cmd_ansible(configure_data, base_project, dryrun, verbose, destroy=False)
163+
return cmd_ansible(configure_data, base_project, dryrun, destroy=False)
165164

166165

167-
def cmd_destroy(configure_data, base_project, dryrun=False, verbose=False):
166+
def cmd_destroy(configure_data, base_project, dryrun=False):
168167
"""Main executor for the deploy sub-command
169168
170169
Args:
171170
configure_data (obj): configuration structure
172171
base_project (str): base project path where to
173172
look for the Terraform and Ansible files
174173
dryrun (bool): enable dryrun execution mode
175-
verbose (bool): enable more verbosity
176174
177175
Returns:
178176
int: execution result, 0 means OK. It is mind to be used as script exit code
179177
"""
180178
config = CONF(configure_data)
181179
if not config.validate():
182180
return Status(f"Invalid configuration file content in {configure_data}")
183-
res = cmd_ansible(configure_data, base_project, dryrun, verbose, destroy=True)
181+
res = cmd_ansible(configure_data, base_project, dryrun, destroy=True)
184182
if res != 0:
185183
return res
186184
return cmd_terraform(
@@ -294,7 +292,6 @@ def ansible_command_sequence(
294292
admin_user,
295293
base_project,
296294
sequence,
297-
verbose,
298295
inventory,
299296
profile,
300297
junit,
@@ -308,7 +305,6 @@ def ansible_command_sequence(
308305
base_project (str): base project path where to
309306
look for the Ansible files
310307
sequence (str): 'create' or 'destroy'
311-
verbose (bool): enable more verbosity
312308
inventory (str): inventory.yaml file path
313309
profile (bool): enable task profile
314310
junit (str): enable junit report and provide folder where to store report
@@ -319,8 +315,7 @@ def ansible_command_sequence(
319315
list of strings, each of them is an anslble or ansible-playbook command
320316
"""
321317

322-
# 1. Create the environment variable set
323-
# that will be used by any command
318+
# Create the environment variable set that will be used by any command
324319
original_env = dict(os.environ)
325320
original_env["ANSIBLE_PIPELINING"] = "True"
326321
original_env["ANSIBLE_TIMEOUT"] = "20"
@@ -335,7 +330,7 @@ def ansible_command_sequence(
335330
if "roles_path" in configure_data_ansible:
336331
original_env["ANSIBLE_ROLES_PATH"] = configure_data_ansible["roles_path"]
337332

338-
# 2. Verify that the two needed binaries are usable
333+
# Verify that the two needed binaries are usable
339334
ansible_bin_paths = {}
340335
for ansible_bin in ["ansible", "ansible-playbook"]:
341336
binpath = shutil.which(ansible_bin)
@@ -344,14 +339,13 @@ def ansible_command_sequence(
344339
return False, f"Missing binary {ansible_bin}"
345340
ansible_bin_paths[ansible_bin] = binpath
346341

347-
# 3. Compose common arguments applicable to both 'ansible' and 'ansible-playbook'.
342+
# Compose common arguments applicable to both 'ansible' and 'ansible-playbook'.
348343
ansible_common = "-vv"
349-
if verbose:
350-
# add two more 'v' without any space
351-
ansible_common += "vv"
344+
if "verbosity" in configure_data_ansible:
345+
ansible_common = "-" + "v" * int(configure_data_ansible["verbosity"])
352346
ansible_common += f" -i {inventory}"
353347

354-
# 4. Start composing and accumulating all needed commands in a list
348+
# Start composing and accumulating all needed commands in a list
355349
ansible_cmd_seq = []
356350

357351
if junit and not os.path.isdir(junit):
@@ -494,7 +488,6 @@ def cmd_ansible(
494488
configure_data,
495489
base_project,
496490
dryrun,
497-
verbose,
498491
destroy=False,
499492
profile=False,
500493
junit=False,
@@ -507,7 +500,6 @@ def cmd_ansible(
507500
base_project (str): base project path where to
508501
look for the Ansible files
509502
dryrun (bool): enable dryrun execution mode
510-
verbose (bool): enable more verbosity
511503
destroy (bool): select the playbook list
512504
profile (bool): enable task profile
513505
junit (str): enable junit report and provide folder where to store it
@@ -560,7 +552,6 @@ def cmd_ansible(
560552
admin_user,
561553
base_project,
562554
selected_sequence,
563-
verbose,
564555
inventory,
565556
profile,
566557
junit,

scripts/qesap/lib/config.py

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,45 @@ def get_playbooks(self, sequence):
319319
return self.conf["ansible"][sequence]
320320
return self.conf["ansible"]["sequences"][sequence]
321321

322+
def _validate_ansible_sequence(self, sequence):
323+
"""
324+
Validate the sequence part of the ansible configure.yaml
325+
"""
326+
if not sequence:
327+
return True
328+
selected_seq = (
329+
self.conf["ansible"]
330+
if self.conf["apiver"] < 4
331+
else self.conf["ansible"].get("sequences", {})
332+
)
333+
if sequence not in selected_seq or selected_seq[sequence] is None:
334+
log.error(
335+
"No Ansible playbooks to play in %s for sequence:%s",
336+
self.conf["ansible"],
337+
sequence,
338+
)
339+
return False
340+
return True
341+
342+
@staticmethod
343+
def validate_ansible_verbosity(ansible_conf):
344+
"""
345+
Validate the verbosity part of the ansible configure.yaml
346+
"""
347+
if "verbosity" in ansible_conf:
348+
verbosity = ansible_conf["verbosity"]
349+
try:
350+
verbosity = int(verbosity)
351+
except (ValueError, TypeError):
352+
log.error("ansible verbosity must be an integer, got: %r", verbosity)
353+
return False
354+
if not 1 <= verbosity <= 6:
355+
log.error(
356+
"ansible verbosity must be between 1 and 6, got: %d", verbosity
357+
)
358+
return False
359+
return True
360+
322361
def validate_ansible_config(self, sequence):
323362
"""
324363
Validate the ansible part of the internal structure of the config.yaml
@@ -339,25 +378,15 @@ def validate_ansible_config(self, sequence):
339378
log.error("Ansible media configuration")
340379
return False
341380

342-
if sequence:
343-
selected_seq = None
344-
if self.conf["apiver"] < 4:
345-
selected_seq = self.conf["ansible"]
346-
else:
347-
selected_seq = self.conf["ansible"]["sequences"]
348-
if sequence not in selected_seq or selected_seq[sequence] is None:
349-
log.error(
350-
"No Ansible playbooks to play in %s for sequence:%s",
351-
self.conf["ansible"],
352-
sequence,
353-
)
354-
return False
381+
if not self._validate_ansible_sequence(sequence):
382+
return False
355383

356384
if "hana_vars" in self.conf["ansible"] and not validate_ansible_hana_var(
357385
self.conf["ansible"]["hana_vars"]
358386
):
359387
return False
360-
return True
388+
389+
return self.validate_ansible_verbosity(self.conf["ansible"])
361390

362391
def validate_basedir(self, basedir):
363392
"""

scripts/qesap/qesap.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,10 @@ def run_subcommand(args):
180180
return cmd_configure(args.configdata, args.basedir, args.dryrun)
181181
if args.command == "deploy":
182182
log.info("Deploying...")
183-
return cmd_deploy(args.configdata, args.basedir, args.dryrun, args.verbose)
183+
return cmd_deploy(args.configdata, args.basedir, args.dryrun)
184184
if args.command == "destroy":
185185
log.info("Destroying...")
186-
return cmd_destroy(args.configdata, args.basedir, args.dryrun, args.verbose)
186+
return cmd_destroy(args.configdata, args.basedir, args.dryrun)
187187
if args.command == "terraform":
188188
log.info("Running Terraform...")
189189
return cmd_terraform(
@@ -200,7 +200,6 @@ def run_subcommand(args):
200200
args.configdata,
201201
args.basedir,
202202
args.dryrun,
203-
args.verbose,
204203
destroy=args.destroy,
205204
profile=args.profile,
206205
junit=args.junit,

scripts/qesap/test/conftest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def _callback(playbook_list):
176176

177177
@pytest.fixture
178178
def ansible_config():
179-
def _callback(provider, playbooks, apiver=3):
179+
def _callback(provider, playbooks, apiver=3, verbosity=None):
180180
config_content = f"""---
181181
apiver: {apiver}
182182
provider: {provider}
@@ -187,6 +187,9 @@ def _callback(provider, playbooks, apiver=3):
187187
hana_media:
188188
- pippo"""
189189

190+
if verbosity is not None:
191+
config_content += f"\n verbosity: {verbosity}"
192+
190193
if apiver < 4:
191194
for seq in playbooks:
192195
config_content += f"\n {seq}:"

scripts/qesap/test/e2e/ansible_tests.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,28 @@ test_split
365365
echo "Run the script again without dryrun"
366366
PATH=$TROOT:$PATH qesap.py -b ${QESAPROOT} -c ${QESAP_CFG} ansible -d |& tee "${THIS_LOG}"
367367
[[ ! -f "${FILE_TOUCH_BY_ANSIBLE}" ]] || test_die "File ${FILE_TOUCH_BY_ANSIBLE} has to be deleted by ribes_nero.yaml"
368+
369+
#######################################################################
370+
QESAP_CFG=test_11.yaml
371+
test_step "[${QESAP_CFG}] Run Ansible with verbosity 0"
372+
THIS_LOG="${QESAPROOT}/ansible_v0.log"
373+
reset_root
374+
cp sambuconero.yaml "${QESAPROOT}/ansible/playbooks/"
375+
cp inventory.yaml "${TEST_PROVIDER}/"
376+
qesap.py -b ${QESAPROOT} -c ${QESAP_CFG} --dryrun ansible |& tee "${THIS_LOG}"
377+
set +e
378+
grep -E "ansible.*-v" "${THIS_LOG}"
379+
rc=$?; [[ $rc -ne 0 ]] || test_die "${QESAP_CFG} should not have -v in ansible commands"
380+
set -e
381+
rm "${THIS_LOG}"
382+
383+
#######################################################################
384+
QESAP_CFG=test_12.yaml
385+
test_step "[${QESAP_CFG}] Run Ansible with verbosity 4"
386+
THIS_LOG="${QESAPROOT}/ansible_v4.log"
387+
reset_root
388+
cp sambuconero.yaml "${QESAPROOT}/ansible/playbooks/"
389+
cp inventory.yaml "${TEST_PROVIDER}/"
390+
qesap.py -b ${QESAPROOT} -c ${QESAP_CFG} --dryrun ansible |& tee "${THIS_LOG}"
391+
grep -E "ansible.*-vvvv" "${THIS_LOG}" || test_die "${QESAP_CFG} should have -vvvv in ansible commands"
392+
rm "${THIS_LOG}"

scripts/qesap/test/e2e/test_10.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ terraform:
55
variables:
66
fruit: lampone
77
ansible:
8+
verbosity: 4
89
hana_media:
910
- mora
1011
- corniolo
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
apiver: 3
3+
provider: fragola
4+
terraform:
5+
variables:
6+
fruit: lampone
7+
ansible:
8+
verbosity: 0
9+
hana_media:
10+
- mora
11+
- corniolo
12+
hana_urls: mirtillo
13+
az_storage_account_name: ribes
14+
az_container_name: uvaspina
15+
az_sas_token: '***'
16+
create:
17+
- sambuconero.yaml
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
apiver: 3
3+
provider: fragola
4+
terraform:
5+
variables:
6+
fruit: lampone
7+
ansible:
8+
verbosity: 4
9+
hana_media:
10+
- mora
11+
- corniolo
12+
hana_urls: mirtillo
13+
az_storage_account_name: ribes
14+
az_container_name: uvaspina
15+
az_sas_token: '***'
16+
create:
17+
- sambuconero.yaml

scripts/qesap/test/unit/test_qesap_ansible.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,18 +83,17 @@ def test_ansible_verbose(
8383
mock_call_ansibleplaybook,
8484
):
8585
"""
86-
run with -vvvv if qesap ansible --verbose
87-
(probably not supported in qesap deploy/destroy)
86+
run with -vvvv if config.yaml has verbosity: 4
8887
"""
8988
provider = "grilloparlante"
9089
playbooks = {"create": ["get_cherry_wood"]}
9190

92-
config_content = ansible_config(provider, playbooks)
91+
config_content = ansible_config(provider, playbooks, verbosity=4)
9392
config_file_name = str(tmpdir / "config.yaml")
9493
with open(config_file_name, "w", encoding="utf-8") as file:
9594
file.write(config_content)
9695

97-
args = base_args(None, config_file_name, True)
96+
args = base_args(None, config_file_name, False)
9897
args.append("ansible")
9998
run.return_value = (0, [])
10099

0 commit comments

Comments
 (0)