Skip to content

Commit 2a75418

Browse files
committed
fixed requests bug and added validation to connected host credentials before running playbook
1 parent c22d202 commit 2a75418

9 files changed

Lines changed: 190 additions & 55 deletions

File tree

2G-Service/admin-ansible-config-2g/src/driver.py

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from helper_code.validate_protocols import is_path_supported_protocol
1616
from cloudshell.core.logger.qs_logger import get_qs_logger
1717
from ansible_configuration import AnsibleConfiguration, HostConfiguration
18+
from get_resource_from_context import get_resource_from_context
1819

1920
# HOST OVERRIDE PARAMS - IF PRESENT ON RESOURCE THEY WILL OVERRIDE THE SERVICE DEFAULT
2021
# TO BE CREATED IN SYSTEM AS GLOBAL ATTRIBUTE
@@ -53,9 +54,17 @@ def execute_playbook(self, context, cancellation_context, playbook_path, script_
5354
res_id = context.reservation.reservation_id
5455
reporter = self._get_sandbox_reporter(context, api)
5556
service_name = context.resource.name
56-
ansible_config_json = self._get_ansible_config_json(context, api, reporter, playbook_path, script_params)
5757

58-
reporter.info_out("Service '{}' is Executing Ansible Playbook...".format(context.resource.name))
58+
try:
59+
ansible_config_json = self._get_ansible_config_json(context, api, reporter, playbook_path, script_params)
60+
except Exception as e:
61+
exc_msg = "Error building playbook request on '{}': {}".format(service_name, str(e))
62+
reporter.exc_out(exc_msg)
63+
api.SetServiceLiveStatus(reservationId=res_id, serviceAlias=service_name, liveStatusName="Error",
64+
additionalInfo=str(e))
65+
raise Exception(exc_msg)
66+
67+
reporter.info_out("'{}' is Executing Ansible Playbook...".format(context.resource.name))
5968
try:
6069
self.first_gen_ansible_shell.execute_playbook(context, ansible_config_json, cancellation_context)
6170
except Exception as e:
@@ -108,8 +117,17 @@ def execute_infrastructure_playbook(self, context, cancellation_context, infrast
108117
reporter = self._get_sandbox_reporter(context, api)
109118
service_name = context.resource.name
110119
resources = self._get_infrastructure_resources(infrastructure_resources, service_name, api, reporter)
111-
ansible_config_json = self._get_ansible_config_json(context, api, reporter, playbook_path, script_params,
112-
resources)
120+
121+
reporter.info_out("'{}' is Executing Ansible Playbook...".format(context.resource.name))
122+
try:
123+
ansible_config_json = self._get_ansible_config_json(context, api, reporter, playbook_path, script_params,
124+
resources)
125+
except Exception as e:
126+
exc_msg = "Error building playbook request on '{}': {}".format(service_name, str(e))
127+
reporter.exc_out(exc_msg)
128+
api.SetServiceLiveStatus(reservationId=res_id, serviceAlias=service_name, liveStatusName="Error",
129+
additionalInfo=str(e))
130+
raise Exception(exc_msg)
113131

114132
try:
115133
self.first_gen_ansible_shell.execute_playbook(context, ansible_config_json, cancellation_context)
@@ -148,12 +166,11 @@ def _build_repo_url(self, resource, playbook_path_input, reporter):
148166
2. full url on service takes precedence over base path
149167
3. base path concatenation last
150168
4. if input is not full url, then tries to concatenate with base bath on service
151-
:param AdminAnsibleConfig2G resource:
169+
:param AnsibleConfig2G resource:
152170
:param str playbook_path:
153171
:param SandboxReporter reporter:
154172
:return:
155173
"""
156-
service_name = resource.name
157174
service_full_url = resource.playbook_url_full
158175
gitlab_branch = resource.gitlab_branch if resource.gitlab_branch else "master"
159176
base_path = resource.playbook_base_path
@@ -202,7 +219,8 @@ def _build_repo_url(self, resource, playbook_path_input, reporter):
202219

203220
# validate base path includes protocol
204221
if not self._is_path_supported_protocol(base_path):
205-
err_msg = "Input Error - Base Path does not begin with valid protocol. Supported: {}".format(self.supported_protocols)
222+
err_msg = "Input Error - Base Path does not begin with valid protocol. Supported: {}".format(
223+
self.supported_protocols)
206224
reporter.err_out(err_msg)
207225
raise ValueError(err_msg)
208226

@@ -273,7 +291,7 @@ def _get_ansible_config_json(self, context, api, reporter, playbook_path, script
273291
:param CloudShellAPISession api:
274292
:return:
275293
"""
276-
resource = AdminAnsibleConfig2G.create_from_context(context)
294+
resource = get_resource_from_context(context)
277295
service_name = context.resource.name
278296
service_connection_method = resource.connection_method
279297
service_inventory_groups = resource.inventory_groups
@@ -306,11 +324,6 @@ def _get_ansible_config_json(self, context, api, reporter, playbook_path, script
306324
target_host_resource_names = list(set(all_linked_resources))
307325
target_host_resources = [api.GetResourceDetails(x) for x in target_host_resource_names]
308326

309-
# REPORT TARGET RESOURCES
310-
resource_names = [x.Name for x in target_host_resources]
311-
start_msg = "'{}' is running on : {}".format(service_name, resource_names)
312-
reporter.info_out(start_msg)
313-
314327
# INITIALIZE DATA MODEL AND START POPULATING
315328
ansi_conf = AnsibleConfiguration()
316329

@@ -333,31 +346,56 @@ def _get_ansible_config_json(self, context, api, reporter, playbook_path, script
333346
ansi_conf.repositoryDetails.password = password_val if password_val else None
334347

335348
# START POPULATING HOSTS
349+
missing_credential_hosts = []
336350
for curr_resource_obj in target_host_resources:
351+
curr_resource_name = curr_resource_obj.Name
337352
host_conf = HostConfiguration()
338353
host_conf.ip = curr_resource_obj.Address
339-
340354
attrs = curr_resource_obj.ResourceAttributes
341355

342356
user_attr = get_resource_attribute_gen_agostic("User", attrs)
343-
host_conf.username = user_attr.Value if user_attr else ""
357+
user_attr_val = user_attr.Value if user_attr else ""
358+
host_conf.username = user_attr_val
344359

345360
password_attr = get_resource_attribute_gen_agostic("Password", attrs)
346-
host_conf.password = password_attr.Value if password_attr else None
361+
encrypted_password_val = password_attr.Value
362+
host_conf.password = encrypted_password_val
347363

348364
# OVERRIDE SERVICE ATTRIBUTES IF ATTRIBUTES EXIST ON RESOURCE
365+
# ACCESS KEY
366+
access_key_attr = get_resource_attribute_gen_agostic(ACCESS_KEY_PARAM, attrs)
367+
encrypted_acces_key_val = access_key_attr.Value if access_key_attr else None
368+
host_conf.accessKey = encrypted_acces_key_val
369+
370+
# VALIDATE HOST CREDENTIALS - NEED USER AND PASSWORD/ACCESS KEY
371+
if user_attr_val:
372+
decrypted_password = api.DecryptPassword(encrypted_password_val).Value
373+
if encrypted_acces_key_val:
374+
decrypted_access_key = api.DecryptPassword(encrypted_acces_key_val).Value
375+
else:
376+
decrypted_access_key = None
377+
if not decrypted_password and not decrypted_access_key:
378+
missing_credential_hosts.append((curr_resource_name, "Empty Credentials Attribute on Resource"))
379+
else:
380+
missing_credential_hosts.append((curr_resource_name, "Empty User Attribute on Resource"))
349381

350382
# GROUPS
351383
ansible_group_attr = get_resource_attribute_gen_agostic(INVENTORY_GROUP_PARAM, attrs)
352384
host_conf.groups = ansible_group_attr.Value if ansible_group_attr else service_inventory_groups
353385

354-
# ACCESS KEY
355-
access_key_attr = get_resource_attribute_gen_agostic(ACCESS_KEY_PARAM, attrs)
356-
host_conf.accessKey = access_key_attr.Value if access_key_attr else None
357-
358386
# CONNECTION METHOD
387+
resource_connection_method = None
359388
connection_method_attr = get_resource_attribute_gen_agostic(CONNECTION_METHOD_PARAM, attrs)
360-
host_conf.connectionMethod = connection_method_attr.Value if connection_method_attr else service_connection_method
389+
if connection_method_attr:
390+
connection_val = connection_method_attr.Value
391+
if connection_val:
392+
if connection_val.lower() not in ["na", "n/a"]:
393+
resource_connection_method = connection_val
394+
395+
if resource_connection_method:
396+
host_conf.connectionMethod = resource_connection_method
397+
else:
398+
host_conf.connectionMethod = service_connection_method
361399

362400
# CONNECTION SECURED
363401
connection_secured_attr = get_resource_attribute_gen_agostic(CONNECTION_SECURED_PARAM, attrs)
@@ -378,8 +416,19 @@ def _get_ansible_config_json(self, context, api, reporter, playbook_path, script
378416

379417
ansi_conf.hostsDetails.append(host_conf)
380418

381-
ansi_conf_json = ansi_conf.get_pretty_json()
419+
if missing_credential_hosts:
420+
missing_json = json.dumps(missing_credential_hosts, indent=4)
421+
warning_msg = "=== '{}' Connected Hosts Missing Credentials ===\n{}".format(service_name, missing_json)
422+
reporter.info_out(warning_msg)
423+
err_msg = "Missing credentials on target hosts. See console / logs for info."
424+
raise Exception(err_msg)
425+
426+
# REPORT TARGET RESOURCES
427+
resource_names = [x.Name for x in target_host_resources]
428+
start_msg = "'{}' Target Hosts :\n{}".format(service_name, json.dumps(resource_names, indent=4))
429+
reporter.info_out(start_msg)
382430

431+
ansi_conf_json = ansi_conf.get_pretty_json()
383432
# hide repo password in json printout
384433
new_obj = json.loads(ansi_conf_json)
385434
curr_password = new_obj["repositoryDetails"]["password"]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# this helper is just to abstract the resource class so I can copy paste the driver over to admin version
2+
3+
from data_model import AdminAnsibleConfig2G
4+
5+
6+
def get_resource_from_context(context):
7+
"""
8+
:param ResourceCommandContext context:
9+
:return:
10+
"""
11+
return AdminAnsibleConfig2G.create_from_context(context)

2G-Service/ansible-config-2g/src/driver.py

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from helper_code.validate_protocols import is_path_supported_protocol
1616
from cloudshell.core.logger.qs_logger import get_qs_logger
1717
from ansible_configuration import AnsibleConfiguration, HostConfiguration
18+
from get_resource_from_context import get_resource_from_context
1819

1920
# HOST OVERRIDE PARAMS - IF PRESENT ON RESOURCE THEY WILL OVERRIDE THE SERVICE DEFAULT
2021
# TO BE CREATED IN SYSTEM AS GLOBAL ATTRIBUTE
@@ -53,9 +54,17 @@ def execute_playbook(self, context, cancellation_context, playbook_path, script_
5354
res_id = context.reservation.reservation_id
5455
reporter = self._get_sandbox_reporter(context, api)
5556
service_name = context.resource.name
56-
ansible_config_json = self._get_ansible_config_json(context, api, reporter, playbook_path, script_params)
5757

58-
reporter.info_out("Service '{}' is Executing Ansible Playbook...".format(context.resource.name))
58+
try:
59+
ansible_config_json = self._get_ansible_config_json(context, api, reporter, playbook_path, script_params)
60+
except Exception as e:
61+
exc_msg = "Error building playbook request on '{}': {}".format(service_name, str(e))
62+
reporter.exc_out(exc_msg)
63+
api.SetServiceLiveStatus(reservationId=res_id, serviceAlias=service_name, liveStatusName="Error",
64+
additionalInfo=str(e))
65+
raise Exception(exc_msg)
66+
67+
reporter.info_out("'{}' is Executing Ansible Playbook...".format(context.resource.name))
5968
try:
6069
self.first_gen_ansible_shell.execute_playbook(context, ansible_config_json, cancellation_context)
6170
except Exception as e:
@@ -108,8 +117,17 @@ def execute_infrastructure_playbook(self, context, cancellation_context, infrast
108117
reporter = self._get_sandbox_reporter(context, api)
109118
service_name = context.resource.name
110119
resources = self._get_infrastructure_resources(infrastructure_resources, service_name, api, reporter)
111-
ansible_config_json = self._get_ansible_config_json(context, api, reporter, playbook_path, script_params,
112-
resources)
120+
121+
reporter.info_out("'{}' is Executing Ansible Playbook...".format(context.resource.name))
122+
try:
123+
ansible_config_json = self._get_ansible_config_json(context, api, reporter, playbook_path, script_params,
124+
resources)
125+
except Exception as e:
126+
exc_msg = "Error building playbook request on '{}': {}".format(service_name, str(e))
127+
reporter.exc_out(exc_msg)
128+
api.SetServiceLiveStatus(reservationId=res_id, serviceAlias=service_name, liveStatusName="Error",
129+
additionalInfo=str(e))
130+
raise Exception(exc_msg)
113131

114132
try:
115133
self.first_gen_ansible_shell.execute_playbook(context, ansible_config_json, cancellation_context)
@@ -153,7 +171,6 @@ def _build_repo_url(self, resource, playbook_path_input, reporter):
153171
:param SandboxReporter reporter:
154172
:return:
155173
"""
156-
service_name = resource.name
157174
service_full_url = resource.playbook_url_full
158175
gitlab_branch = resource.gitlab_branch if resource.gitlab_branch else "master"
159176
base_path = resource.playbook_base_path
@@ -202,7 +219,8 @@ def _build_repo_url(self, resource, playbook_path_input, reporter):
202219

203220
# validate base path includes protocol
204221
if not self._is_path_supported_protocol(base_path):
205-
err_msg = "Input Error - Base Path does not begin with valid protocol. Supported: {}".format(self.supported_protocols)
222+
err_msg = "Input Error - Base Path does not begin with valid protocol. Supported: {}".format(
223+
self.supported_protocols)
206224
reporter.err_out(err_msg)
207225
raise ValueError(err_msg)
208226

@@ -273,7 +291,7 @@ def _get_ansible_config_json(self, context, api, reporter, playbook_path, script
273291
:param CloudShellAPISession api:
274292
:return:
275293
"""
276-
resource = AnsibleConfig2G.create_from_context(context)
294+
resource = get_resource_from_context(context)
277295
service_name = context.resource.name
278296
service_connection_method = resource.connection_method
279297
service_inventory_groups = resource.inventory_groups
@@ -306,11 +324,6 @@ def _get_ansible_config_json(self, context, api, reporter, playbook_path, script
306324
target_host_resource_names = list(set(all_linked_resources))
307325
target_host_resources = [api.GetResourceDetails(x) for x in target_host_resource_names]
308326

309-
# REPORT TARGET RESOURCES
310-
resource_names = [x.Name for x in target_host_resources]
311-
start_msg = "'{}' is running on : {}".format(service_name, resource_names)
312-
reporter.info_out(start_msg)
313-
314327
# INITIALIZE DATA MODEL AND START POPULATING
315328
ansi_conf = AnsibleConfiguration()
316329

@@ -333,31 +346,56 @@ def _get_ansible_config_json(self, context, api, reporter, playbook_path, script
333346
ansi_conf.repositoryDetails.password = password_val if password_val else None
334347

335348
# START POPULATING HOSTS
349+
missing_credential_hosts = []
336350
for curr_resource_obj in target_host_resources:
351+
curr_resource_name = curr_resource_obj.Name
337352
host_conf = HostConfiguration()
338353
host_conf.ip = curr_resource_obj.Address
339-
340354
attrs = curr_resource_obj.ResourceAttributes
341355

342356
user_attr = get_resource_attribute_gen_agostic("User", attrs)
343-
host_conf.username = user_attr.Value if user_attr else ""
357+
user_attr_val = user_attr.Value if user_attr else ""
358+
host_conf.username = user_attr_val
344359

345360
password_attr = get_resource_attribute_gen_agostic("Password", attrs)
346-
host_conf.password = password_attr.Value if password_attr else None
361+
encrypted_password_val = password_attr.Value
362+
host_conf.password = encrypted_password_val
347363

348364
# OVERRIDE SERVICE ATTRIBUTES IF ATTRIBUTES EXIST ON RESOURCE
365+
# ACCESS KEY
366+
access_key_attr = get_resource_attribute_gen_agostic(ACCESS_KEY_PARAM, attrs)
367+
encrypted_acces_key_val = access_key_attr.Value if access_key_attr else None
368+
host_conf.accessKey = encrypted_acces_key_val
369+
370+
# VALIDATE HOST CREDENTIALS - NEED USER AND PASSWORD/ACCESS KEY
371+
if user_attr_val:
372+
decrypted_password = api.DecryptPassword(encrypted_password_val).Value
373+
if encrypted_acces_key_val:
374+
decrypted_access_key = api.DecryptPassword(encrypted_acces_key_val).Value
375+
else:
376+
decrypted_access_key = None
377+
if not decrypted_password and not decrypted_access_key:
378+
missing_credential_hosts.append((curr_resource_name, "Empty Credentials Attribute on Resource"))
379+
else:
380+
missing_credential_hosts.append((curr_resource_name, "Empty User Attribute on Resource"))
349381

350382
# GROUPS
351383
ansible_group_attr = get_resource_attribute_gen_agostic(INVENTORY_GROUP_PARAM, attrs)
352384
host_conf.groups = ansible_group_attr.Value if ansible_group_attr else service_inventory_groups
353385

354-
# ACCESS KEY
355-
access_key_attr = get_resource_attribute_gen_agostic(ACCESS_KEY_PARAM, attrs)
356-
host_conf.accessKey = access_key_attr.Value if access_key_attr else None
357-
358386
# CONNECTION METHOD
387+
resource_connection_method = None
359388
connection_method_attr = get_resource_attribute_gen_agostic(CONNECTION_METHOD_PARAM, attrs)
360-
host_conf.connectionMethod = connection_method_attr.Value if connection_method_attr else service_connection_method
389+
if connection_method_attr:
390+
connection_val = connection_method_attr.Value
391+
if connection_val:
392+
if connection_val.lower() not in ["na", "n/a"]:
393+
resource_connection_method = connection_val
394+
395+
if resource_connection_method:
396+
host_conf.connectionMethod = resource_connection_method
397+
else:
398+
host_conf.connectionMethod = service_connection_method
361399

362400
# CONNECTION SECURED
363401
connection_secured_attr = get_resource_attribute_gen_agostic(CONNECTION_SECURED_PARAM, attrs)
@@ -378,8 +416,19 @@ def _get_ansible_config_json(self, context, api, reporter, playbook_path, script
378416

379417
ansi_conf.hostsDetails.append(host_conf)
380418

381-
ansi_conf_json = ansi_conf.get_pretty_json()
419+
if missing_credential_hosts:
420+
missing_json = json.dumps(missing_credential_hosts, indent=4)
421+
warning_msg = "=== '{}' Connected Hosts Missing Credentials ===\n{}".format(service_name, missing_json)
422+
reporter.info_out(warning_msg)
423+
err_msg = "Missing credentials on target hosts. See console / logs for info."
424+
raise Exception(err_msg)
425+
426+
# REPORT TARGET RESOURCES
427+
resource_names = [x.Name for x in target_host_resources]
428+
start_msg = "'{}' Target Hosts :\n{}".format(service_name, json.dumps(resource_names, indent=4))
429+
reporter.info_out(start_msg)
382430

431+
ansi_conf_json = ansi_conf.get_pretty_json()
383432
# hide repo password in json printout
384433
new_obj = json.loads(ansi_conf_json)
385434
curr_password = new_obj["repositoryDetails"]["password"]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# this helper is just to abstract the resource class so I can copy paste the driver over to admin version
2+
3+
from data_model import AnsibleConfig2G
4+
5+
6+
def get_resource_from_context(context):
7+
"""
8+
:param ResourceCommandContext context:
9+
:return:
10+
"""
11+
return AnsibleConfig2G.create_from_context(context)

0 commit comments

Comments
 (0)