def do_task(self): self.validate_params() account_name = self.get_module().params['account_name'] return_format = self.module.params['return_format'] facts = {} if return_format == 'dict': facts = dict(data_centers={}, services={}) elif return_format == 'list': facts = dict(data_centers=[], services=[]) else: raise SolaceInternalError( f"arg 'return_format={return_format}' invalid") services = self.get_solace_cloud_api().get_services_with_details( self.get_config()) data_centers = self.get_solace_cloud_api().get_data_centers( self.get_config()) if return_format == 'dict': for service in services: name = service['name'] facts['services'][name] = service for data_center in data_centers: id = data_center['id'] facts['data_centers'][id] = data_center else: facts['services'] = services facts['data_centers'] = data_centers result = self.create_result() result.update(dict(solace_cloud_account={account_name: facts})) return None, result
def get_objects(self, config: SolaceTaskBrokerConfig, xml_cmd: str, reponse_list_path_array: list) -> list: result_list = [] hasNextPage = True while hasNextPage: semp_resp = self.make_post_request( config, xml_cmd, SolaceTaskOps.OP_READ_OBJECT_LIST) # extract the list _d = semp_resp for path in reponse_list_path_array: if _d and path in _d: _d = _d[path] else: # empty list / not found return [] if isinstance(_d, dict): resp = [_d] elif isinstance(_d, list): resp = _d else: raise SolaceInternalError( f"unknown SEMP v1 return type: {type(_d)}") result_list.extend(resp) # see if there is more more_cookie = None if 'more-cookie' in semp_resp['rpc-reply']: more_cookie = semp_resp['rpc-reply']['more-cookie'] if more_cookie: xml_cmd = xmltodict.unparse(more_cookie) hasNextPage = True else: hasNextPage = False return result_list
def get_sempv2_version_map_key(self, semp_version): if semp_version <= SolaceUtils.create_version("2.13"): return '<=2.13' elif semp_version >= SolaceUtils.create_version("2.14"): return '>=2.14' raise SolaceInternalError( f"sempv2_version: {semp_version} not supported")
def get_settings_type_conversion(self, d): # everything is a string or null # import logging # import json # logging.debug(f"d={json.dumps(d, indent=2)}") for k, i in d.items(): t = type(i) if t == dict: d[k] = self.get_settings_type_conversion(i) else: if i and t != str: raise SolaceInternalError( f"unhandled type, field={k}, value={i}, type={t}") if not i: # placeholder, leave it for now d[k] = None elif i == "": # placeholder, leave it for now d[k] = i elif i.lower() == 'false': d[k] = False elif i.lower() == 'true': d[k] = True elif re.search(r'^[0-9]+$', i): d[k] = int(i) elif re.search(r'^[0-9]+\.[0-9]$', i): d[k] = float(i) else: # leave any other strings d[k] = i return d
def _extract_msg_vpn_attributes(self) -> dict: vpn_attributes = SolaceBrokerFacts.get_field(self.input_dict, "msgVpnAttributes") if not vpn_attributes: raise SolaceInternalError( "Could not find 'msgVpnAttributes' in 'ansible_facts.solace'. API may have changed." ) return vpn_attributes
def _extract_messaging_protocols(self) -> dict: if self.messaging_protocols: return self.messaging_protocols mps = self.get_field(self.input_dict, "messagingProtocols") if not mps: raise SolaceInternalError( "Could not find 'messagingProtocols' in 'ansible_facts.solace'. API may have changed." ) self.messaging_protocols = mps return self.messaging_protocols
def find_service_by_name_in_services(self, services, name): if isinstance(services, dict): if name == services.get('name'): return services elif isinstance(services, list): for service in services: if name == service.get('name'): return service else: raise SolaceInternalError( f"solace cloud response not 'dict' nor 'list' but {type(services)}" ) return None
def get_sempv2_version(self, config: SolaceTaskBrokerConfig): resp = self.make_get_request(config, [SolaceSempV2Api.API_BASE_SEMPV2_CONFIG] + ["about", "api"], query_params=None) raw_api_version = SolaceUtils.get_key(resp, "sempVersion") # format: 2.21 try: v = SolaceUtils.create_version(raw_api_version) except SolaceInternalError as e: raise SolaceInternalError( f"sempv2 version parsing failed: {raw_api_version}") from e return raw_api_version, v
def get_sempv1_version(self, config: SolaceTaskBrokerConfig): rpc_xml = "<rpc><show><service></service></show></rpc>" resp = self.make_post_request(config, rpc_xml, SolaceTaskOps.OP_READ_SEMP_VERSION) rpc_reply = resp['rpc-reply'] raw_api_version = SolaceUtils.get_key(rpc_reply, "@semp-version") # format: soltr/9_9VMR s = raw_api_version[6:9].replace('_', '.') try: v = SolaceUtils.create_version(s) except SolaceInternalError as e: raise SolaceInternalError( f"sempv1 version parsing failed: {raw_api_version}") from e return raw_api_version, v
def filter(self, settings: dict, query_params: dict) -> dict: if not query_params: return settings where_list = [] if ("where" in query_params and query_params['where'] and len(query_params['where']) > 0): where_list = query_params['where'] is_match = True for where in where_list: # OP: == where_elements = where.split('==') if len(where_elements) != 2: raise SolaceParamsValidationError( 'query_params.where', where, "cannot parse where clause - must be in format '{key}=={pattern}' (other ops are not supported)" ) sempv2_key = where_elements[0] pattern = where_elements[1] solace_cloud_key = self.MAPPINGS.get(sempv2_key, None) if not solace_cloud_key: raise SolaceParamsValidationError( 'query_params.where', where, f"unknown key for solace cloud '{sempv2_key}' - check with Solace Cloud API settings" ) # pattern match solace_cloud_value = settings.get(solace_cloud_key, None) if not solace_cloud_value: raise SolaceInternalError( f"solace-cloud-key={solace_cloud_key} not found in solace cloud settings - likely a key map issue" ) # create regex regex = pattern.replace("*", ".+") this_match = re.search(regex, solace_cloud_value) is_match = (is_match and this_match) if not is_match: break if is_match: return settings return None
def make_service_post_request(self, config: SolaceTaskBrokerConfig, path_array: list, service_id: str, json_body, module_op): resp = self.make_request(config, requests.post, path_array, json_body) # import logging, json # logging.debug(f"resp (make_request) = \n{json.dumps(resp, indent=2)}") request_id = resp['id'] timeout_minutes = 2 is_completed = False is_failed = False try_count = -1 delay = 5 # seconds max_retries = (timeout_minutes * 60) // delay # wait 1 cycle before start polling time.sleep(delay) while not is_completed and not is_failed and try_count < max_retries: resp = self.get_service_request_status(config, service_id, request_id) # import logging, json # logging.debug(f"resp (get_service_request_status)= \n{json.dumps(resp, indent=2)}") is_completed = (resp['adminProgress'] == 'completed') is_failed = (resp['adminProgress'] == 'failed') try_count += 1 if timeout_minutes > 0: time.sleep(delay) if is_failed: raise SolaceApiError(resp, resp, self.get_module()._name, module_op) if not is_completed: msg = [ f"timeout service post request - not completed, state={resp['adminProgress']}", str(resp) ] raise SolaceInternalError(msg) return resp
def do_task(self): self.validate_params() params = self.get_config().get_params() is_check_mode = self.get_module().check_mode self.existing_key_list = self.get_object_key_list( *self.get_objects_result_data_object_keys()) # logging.debug( # f"\n\n>>>>>>>>>>existing_key_list={json.dumps(self.existing_key_list, indent=2)}\n\n") target_key_list = self.deduplicate_keys(self.get_target_key_list()) # logging.debug( # f"\n\n>>>>>>>>>>target_key_list={json.dumps(target_key_list, indent=2)}\n\n") self.set_result(self.create_result(rc=0, changed=False)) state_object_combination_error = False new_state = params['state'] new_settings = self.get_new_settings() for target_key in target_key_list: crud_args = self.get_crud_args(target_key) target_key_exists = target_key in self.existing_key_list if (new_state == 'present' or new_state == 'exactly') and not target_key_exists: self.changed = True if not is_check_mode: try: _response = self.create_func(*crud_args, new_settings) self.created_key_list.append(target_key) except Exception as ex: self.do_rollback_on_error(target_key, ex) elif new_state == 'present' and target_key_exists: pass elif new_state == 'absent' and target_key_exists: if not is_check_mode: try: _response = self.delete_func(*crud_args) self.deleted_key_list.append(target_key) except Exception as ex: self.do_rollback_on_error(target_key, ex) elif new_state == 'absent' and not target_key_exists: pass elif new_state == 'exactly' and target_key_exists: pass elif new_state == 'exactly': pass else: state_object_combination_error = True if new_state == 'exactly': for existing_key in self.existing_key_list: crud_args = self.get_crud_args(existing_key) if new_state == 'exactly' and existing_key not in target_key_list: if not is_check_mode: try: _response = self.delete_func(*crud_args) self.deleted_key_list.append(existing_key) except Exception as ex: self.do_rollback_on_error(existing_key, ex) elif new_state == 'exactly' and existing_key in target_key_list: pass else: state_object_combination_error = True if state_object_combination_error: raise SolaceInternalError([ "unsupported state / object combination", f"state={new_state}", f"target_key_list={target_key_list}", f"existing_key_list={self.existing_key_list}" ]) response_list = [] for k in self.created_key_list: response_list.append({'added': k}) for k in self.deleted_key_list: response_list.append({'deleted': k}) for k in self.duplicate_key_list: response_list.append({'duplicate': k}) if len(response_list) > 0: self.changed = True self.update_result( { 'changed': self.changed, 'response': response_list }) return None, self.get_result()
def call_dynamic_func(self, func_name, *args): try: return getattr(self, func_name)(*args) except AttributeError as e: raise SolaceInternalError( f"function '{func_name}' not found") from e
def get_key(d: dict, k: str): try: return d[k] except KeyError as e: raise SolaceInternalError( f"KeyError: dict has no key '{k}'") from e
def create_version(s: str): try: v = packaging.version.Version(s) except packaging.version.InvalidVersion as e: raise SolaceInternalError(f"version parsing failed: {s}") from e return v
def do_task_extension(self, args, new_state, new_settings, current_settings): raise SolaceInternalError( f"unhandled task-state combination, state={new_state}")
def get_solace_cloud_auth(self) -> str: if not self.is_solace_cloud: raise SolaceInternalError( "config does not contain solace cloud parameters") return self.solace_cloud_auth