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 __init__(self, module: AnsibleModule): SolaceUtils.module_fail_on_import_error( module, SOLACE_API_HAS_IMPORT_ERROR, SOLACE_API_IMPORT_ERR_TRACEBACK) self.module = module self.safe_for_path_array = None return
def _update_func_sempv1(self, ldap_profile_name, settings, delta_settings, op): if not settings: return None combined_resps = {} # iterate over settings.keys and send 1 update after the other for key, val in settings.items(): if val: if key == 'search' and isinstance(val, dict): # iterate through sarch for skey, sval in val.items(): _resp = self._send_sempv1_update( ldap_profile_name, key, {skey: sval}, op) _call_log = { self.sempv1_api.getNextCallKey(): { key: { skey: sval, 'response': _resp } } } combined_resps = SolaceUtils.merge_dicts_recursive( combined_resps, _call_log) else: _resp = self._send_sempv1_update(ldap_profile_name, key, val, op) _call_log = { self.sempv1_api.getNextCallKey(): { key: val, 'response': _resp } } combined_resps = SolaceUtils.merge_dicts_recursive( combined_resps, _call_log) return combined_resps
def __init__(self, module: AnsibleModule): SolaceUtils.module_fail_on_import_error( module, SOLACE_TASK_HAS_IMPORT_ERROR, SOLACE_TASK_ERR_TRACEBACK) self.module = module self.changed = False self.result = SolaceUtils.create_result() return
def normalize_new_settings(self, new_settings) -> dict: # solace cloud and self-hosted broker return the same types if new_settings: SolaceUtils.type_conversion(new_settings, False) if self.get_config().is_solace_cloud(): new_settings = self._normalize_new_settings_solace_cloud( new_settings) return new_settings
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 get_remoteFormattedHostInventory( self, solace_cloud_service_facts: SolaceCloudBrokerFacts, search_dict: dict, remote_host_inventory: dict, remote_host_inventory_hostname: str, host_entry: str, api_token: str = None, meta: dict = None): remote_host_inventory_params = solace_cloud_service_facts.get_field( remote_host_inventory, remote_host_inventory_hostname) if not remote_host_inventory_params: raise SolaceParamsValidationError( "remote_host_inventory_hostname", remote_host_inventory_hostname, f"not found in remote_host_inventory: {remote_host_inventory}") _field, broker_inventory = self.get_formattedHostInventory( solace_cloud_service_facts, search_dict, host_entry, api_token, meta) del broker_inventory['all']['hosts'][host_entry]['ansible_connection'] remote_inventory = { 'all': { 'hosts': { host_entry: remote_host_inventory_params } } } inventory = SolaceUtils.merge_dicts_recursive(remote_inventory, broker_inventory) return 'remoteFormattedHostInventory', inventory
def update_func(self, vpn_name, replay_log_name, settings=None, delta_settings=None): # PATCH /msgVpns/{msgVpnName}/replayLogs/{replayLogName} # solace cloud complains about using the exact types converted_settings = SolaceUtils.deep_dict_convert_strs_to_types( settings) path_array = [SolaceSempV2Api.API_BASE_SEMPV2_CONFIG, 'msgVpns', vpn_name, 'replayLogs', replay_log_name] return self.sempv2_api.make_patch_request(self.get_config(), path_array, converted_settings)
def check_min_sempv1_supported_version(self): min_sempv1_version = SolaceUtils.create_version( self.MIN_SEMP_V1_VERSION) raw_api_version, version = self.sempv1_api.get_sempv1_version( self.get_config()) if version < min_sempv1_version: raise SolaceSempv1VersionNotSupportedError( self.get_module()._name, f"{version}({raw_api_version})", min_sempv1_version)
def assert_min_sempv2_version_supported(self): if self.MIN_SEMP_V2_VERSION_STR is not None: sempv2_api = SolaceSempV2Api(self.get_module()) _version_str, version = sempv2_api.get_sempv2_version( self.get_config()) min_sempv2_version = SolaceUtils.create_version( self.MIN_SEMP_V2_VERSION_STR) if version < min_sempv2_version: raise SolaceMinSempv2VersionSupportedError( self.get_module()._name, f"{version}", min_sempv2_version)
def validate_params(self): params = self.get_module().params name = params.get('name', None) if self.get_config().is_solace_cloud() and name != 'default': raise SolaceParamsValidationError( 'name', name, "ldap profile name for Solace Cloud must be 'default'.") invalid_chars = '-' if SolaceUtils.stringContainsAnyChars(name, invalid_chars): raise SolaceParamsValidationError( 'name', name, f"contains 1 or more invalid chars from set: '{invalid_chars}'" )
def _send_sempv1_update(self, ldap_profile_name, key, val, op): _rpc_update_dict = {'authentication': {'ldap-profile': {key: val}}} _rpc_dict = { 'authentication': { 'ldap-profile': { 'profile-name': ldap_profile_name } } } rpc_dict = SolaceUtils.merge_dicts_recursive(_rpc_dict, _rpc_update_dict) return self.sempv1_api.make_post_request( self.get_config(), self.sempv1_api.convertDict2Sempv1RpcXmlString(rpc_dict), op)
def do_task(self): self.validate_params() args = self.get_args() new_settings = self.get_new_settings() _current_settings = self.get_func(*args) current_settings = self.normalize_current_settings(_current_settings, new_settings) new_state = self.get_module().params['state'] # delete if exists if new_state == 'absent': if current_settings is None: return None, self.create_result(rc=0, changed=False) result = self.create_result(rc=0, changed=True) if not self.get_module().check_mode: result['response'] = self.delete_func(*args) return None, result # create if not exist if new_state == 'present' and current_settings is None: result = self.create_result(rc=0, changed=True) if not self.get_module().check_mode: args.append(new_settings) result['response'] = self.create_func(*args) return None, result # update if any changes if new_state == 'present' and current_settings is not None: update_settings = None if new_settings is not None: update_settings = SolaceUtils.deep_dict_diff( new_settings, current_settings) if not update_settings: result = self.create_result(rc=0, changed=False) result['response'] = current_settings return None, result if update_settings: result = self.create_result(rc=0, changed=True) if not self.get_module().check_mode: # sending all settings to update ==> no missing together or required check necessary args.append(new_settings) args.append(update_settings) result['response'] = self.update_func(*args) return None, result return self.do_task_extension(args, new_state, new_settings, current_settings)
def log_http_roundtrip(resp): if not solace_sys.ENABLE_LOGGING: return request_body = SolaceApi.get_http_request_body(resp) resp_body = SolaceUtils.parse_response_text(resp.text) log = { 'request': { 'method': resp.request.method, 'url': resp.request.url, 'headers': SolaceApi._get_http_masked_headers(resp.request.headers), 'body': request_body }, 'response': { 'status_code': resp.status_code, 'reason': resp.reason, 'url': resp.url, 'headers': dict(resp.headers), 'body': resp_body } } logging.debug("\n%s", json.dumps(log, indent=2)) return
def __init__(self, module): SolaceUtils.module_fail_on_import_error( module, SOLACE_GET_AVAILABLE_HAS_IMPORT_ERROR, SOLACE_GET_AVAILABLE_ERR_TRACEBACK) super().__init__(module)
def __init__(self, module: AnsibleModule): super().__init__(module) is_secure = module.params['secure_connection'] host = module.params['host'] port = module.params['port'] self.broker_url = ('https' if is_secure else 'http') + \ '://' + host + ':' + str(port) self.timeout = float(module.params['timeout']) self.validate_certs = bool(module.params['validate_certs']) self.x_broker = module.params.get('x_broker', None) solace_cloud_api_token = module.params.get('solace_cloud_api_token', None) solace_cloud_service_id = module.params.get('solace_cloud_service_id', None) # either both are provided or none ok = ((solace_cloud_api_token and solace_cloud_service_id) or (not solace_cloud_api_token and not solace_cloud_service_id)) if not ok: result = SolaceUtils.create_result(rc=1) msg = f"must provide either both or none for Solace Cloud: solace_cloud_api_token={solace_cloud_api_token}, solace_cloud_service_id={solace_cloud_service_id}." module.fail_json(msg=msg, **result) if ok and solace_cloud_api_token and solace_cloud_service_id: self.solace_cloud_config = dict(api_token=solace_cloud_api_token, service_id=solace_cloud_service_id) else: self.solace_cloud_config = None self.solace_cloud_auth = None if self.solace_cloud_config is not None: self.solace_cloud_auth = BearerAuth( self.solace_cloud_config['api_token']) self.semp_auth = (module.params['username'], module.params['password']) # reverse proxy self.reverse_proxy = module.params.get('reverse_proxy', None) if self.reverse_proxy: if self.solace_cloud_config: result = SolaceUtils.create_result(rc=1) msg = "No support for reverse proxy for Solace Cloud, remove 'reverse_proxy' from module arguments." module.fail_json(msg=msg, **result) semp_base_path = self.reverse_proxy.get('semp_base_path', None) if semp_base_path: self.broker_url += '/' + semp_base_path use_basic_auth = self.reverse_proxy.get('use_basic_auth', False) if not use_basic_auth: self.semp_auth = None _reverse_proxy_headers = self.reverse_proxy.get('headers', None) if _reverse_proxy_headers: _reverse_proxy_headers_include_x_asc_module = _reverse_proxy_headers.get( 'x-asc-module', False) if not isinstance(_reverse_proxy_headers_include_x_asc_module, bool): result = SolaceUtils.create_result(rc=1) msg = f"argument: 'reverse_proxy.headers.x-asc-module={_reverse_proxy_headers_include_x_asc_module}, is not of type 'bool'; use True/False or yes/no" module.fail_json(msg=msg, **result) _reverse_proxy_headers_include_x_asc_module_op = _reverse_proxy_headers.get( 'x-asc-module-op', False) if not isinstance( _reverse_proxy_headers_include_x_asc_module_op, bool): result = SolaceUtils.create_result(rc=1) msg = f"argument: 'reverse_proxy.headers.x-asc-module-op={_reverse_proxy_headers_include_x_asc_module_op}, is not of type 'bool'; use True/False or yes/no" module.fail_json(msg=msg, **result) self.reverse_proxy['headers'][ 'x-asc-module'] = _reverse_proxy_headers_include_x_asc_module self.reverse_proxy['headers'][ 'x-asc-module-op'] = _reverse_proxy_headers_include_x_asc_module_op return
def convertDict2Sempv1RpcXmlString(self, d) -> str: rpc_elem = SolaceUtils.convertDict2XmlElem('rpc', d) return ET.tostring(rpc_elem, encoding='utf-8').decode('utf-8')
def handle_bad_response(self, resp, module_op): _resp = dict(status_code=resp.status_code, reason=resp.reason) _resp.update({'body': SolaceUtils.parse_response_text(resp.text)}) raise SolaceApiError(resp, _resp, self.get_module()._name, module_op)
def validate_params(self): topic = self.get_module().params['name'] if SolaceUtils.doesStringContainAnyWhitespaces(topic): raise SolaceParamsValidationError( 'topic', topic, "must not contain any whitespace") return super().validate_params()
def normalize_new_settings(self, new_settings) -> dict: if new_settings: SolaceUtils.type_conversion(new_settings, False) return new_settings
def handle_bad_response(self, resp, module_op): _resp = dict(status_code=resp.status_code, reason=resp.reason if resp.reason else None) if resp.text: _resp.update(dict(body=SolaceUtils.parse_response_text(resp.text))) raise SolaceApiError(resp, _resp, self.get_module()._name, module_op)
def normalize_new_settings(self, new_settings) -> dict: if new_settings: SolaceUtils.type_conversion(new_settings, self.get_config().is_solace_cloud()) return new_settings
def _remove_solace_cloud_keys(self, settings): _settings = SolaceUtils.deep_copy(settings) for key in self.SOLACE_CLOUD_SETTINGS.keys(): _settings.pop(key, None) return _settings
def create_result(self, rc=0, changed=False) -> dict: return SolaceUtils.create_result(rc, changed)
def validate_key(self, key): if SolaceUtils.doesStringContainAnyWhitespaces(key): raise SolaceParamsValidationError( 'name', key, "must not contain any whitespace")