def __get_roles(self, app_type: str): api_url = self.__api_roles_list.format(app_type) url = "https://{}:{}{}".format(self.__ms_hostname, self.__ms_port, api_url) rq = exec_request(self.__ms_session, url, method="GET", timeout=self.settings.connection_timeout) response = rq.json() self.__roles[app_type] = {} for i in response: role_id = i.get("id") role_name = i.get("name") role_priv = i.get("privileges") if self.__roles[app_type].get(role_name) is None: self.__roles[app_type][role_name] = {} self.__roles[app_type][role_name] = { "id": role_id, "privileges": role_priv } self.log.debug( 'status=success, action=get_roles, msg="Got roles from {}", ' 'hostname="{}" roles="{}"'.format(app_type, self.__ms_hostname, self.__roles))
def get_agents_list(self) -> dict: """ Получить список всех агентов. Есть еще одно API в HealthMonitor :return: """ if len(self.__agents) != 0: return self.__agents url = "https://{}{}".format(self.__core_hostname, self.__api_agents_list) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() for i in response: self.__agents[i.get("id")] = { "name": i.get("name"), "hostname": i.get("address"), "version": i.get("version"), "status": i.get("status"), "modules": i.get("modules") } self.log.info( 'status=success, action=get_agents_list, msg="Got agents list", ' 'hostname="{}", count={}'.format(self.__core_hostname, len(self.__agents))) return self.__agents
def get_credentials_list(self) -> dict: """ Получить список всех учетных записей для подключения к источникам. Информация урезана. :return: """ if len(self.__credentials) != 0: return self.__credentials url = "https://{}{}".format(self.__core_hostname, self.__api_credentials_list) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() for i in response: self.__credentials[i.get("id")] = { "name": i.get("name"), "type": i.get("id"), "description": i.get("description"), "transports": i.get("metatransports"), } self.log.info( 'status=success, action=get_credentials_list, msg="Got credentials list", ' 'hostname="{}", count={}'.format(self.__core_hostname, len(self.__credentials))) return self.__credentials
def get_health_kb_status(self) -> dict: """ Получить статус обновления VM контента в Core. :return: dict. """ url = "https://{}{}".format(self.__core_hostname, self.__api_kb_status) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() local = response.get("localKnowledgeBase") remote = response.get("remoteKnowledgeBase") status = { "status": response.get("status"), "local_updated": local.get("lastUpdate"), "local_current_revision": local.get("localRevision"), "local_global_revision": local.get("globalRevision"), "kb_db_name": remote.get("name") } self.log.info( 'status=success, action=get_license_status, msg="Got license status", ' 'hostname="{}"'.format(self.__core_hostname)) return status
def get_transports_list(self) -> dict: """ Получить список всех транспортов. Информация урезана. :return: """ if len(self.__transports) != 0: return self.__transports url = "https://{}{}".format(self.__core_hostname, self.__api_transports_list) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() for i in response: self.__transports[i.get("id")] = {"name": i.get("name")} self.log.info( 'status=success, action=get_transports_list, msg="Got transports list", ' 'hostname="{}", count={}'.format(self.__core_hostname, len(self.__transports))) return self.__transports
def get_deploy_status(self, db_name: str, deploy_id: str) -> dict: """ Получить общий статус установки контента :param db_name: Имя БД :param deploy_id: Идентификатор процесса установки/удаления :return: {"start_date": "date_string", "deployment_status": "succeeded|running"} """ headers = {'Content-Database': db_name, 'Content-Locale': 'RUS'} params = {"skip": 0, "take": 100, "deployStatusIds": []} url = "https://{}:{}{}".format(self.__kb_hostname, self.__kb_port, self.__api_deploy_log) r = exec_request(self.__kb_session, url, method='POST', timeout=self.settings.connection_timeout, headers=headers, json=params) state = r.json() ret = {} for i in state: if i.get("Id") != deploy_id: continue ret = { "start_date": i.get("StartDate"), "deployment_status": i.get("DeployStatusId") } self.log.info( 'status=success, action=get_deploy_status, msg="Got deploy status", ' 'hostname="{}", db="{}", status="{}"'.format( self.__kb_hostname, db_name, ret)) return ret
def get_modules_list(self) -> dict: """ Получить список всех доступных модулей. Информация урезана. :return: """ if len(self.__modules) != 0: return self.__modules url = "https://{}{}".format(self.__core_hostname, self.__api_modules_list) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() for i in response: self.__modules[i.get("id")] = { "name": i.get("name"), "type": i.get("outputType").lower(), } self.log.info( 'status=success, action=get_modules_list, msg="Got credentials list", ' 'hostname="{}", count={}'.format(self.__core_hostname, len(self.__modules))) return self.__modules
def get_groups_list(self, db_name: str) -> dict: """ Получить список групп :param db_name: Имя БД :return: {'group_id': {'parent_id': 'value', 'name': 'value'}} """ if len(self.__groups) != 0: return self.__groups headers = {'Content-Database': db_name, 'Content-Locale': 'RUS'} url = "https://{}:{}{}".format(self.__kb_hostname, self.__kb_port, self.__api_groups_list) r = exec_request(self.__kb_session, url, method='GET', timeout=self.settings.connection_timeout, headers=headers) groups = r.json() self.__groups.clear() for i in groups: self.__groups[i.get("Id")] = { "parent_id": i.get("ParentGroupId"), "name": i.get("SystemName") } self.log.info( 'status=success, action=get_groups_list, msg="Got {} groups", ' 'hostname="{}", db="{}"'.format(len(self.__groups), self.__kb_hostname, db_name)) return self.__groups
def __get_privileges(self, app_type: str) -> None: """ Парсинг ответа от сервера и заполнение привилегий :param app_type: MPComponent :return: None """ api_url = self.__api_privileges_list.format(app_type) url = "https://{}:{}{}".format(self.__ms_hostname, self.__ms_port, api_url) rq = exec_request(self.__ms_session, url, method="GET", timeout=self.settings.connection_timeout) response = rq.json() self.__privileges[app_type] = {} for i in response: for p in i.get("privileges", {}): priv_id = p.get("code") priv_name = p.get("name") self.__privileges[app_type][priv_id] = priv_name self.log.debug( 'status=success, action=get_roles, msg="Got privileges from {}", ' 'hostname="{}" privileges="{}"'.format(app_type, self.__ms_hostname, self.__privileges))
def __iterate_folders_tree(self, db_name: str): params = {"expandNodes": []} headers = {'Content-Database': db_name, 'Content-Locale': 'RUS'} url = "https://{}:{}{}".format(self.__kb_hostname, self.__kb_port, self.__api_folders_packs_list) r = exec_request(self.__kb_session, url, method='POST', timeout=self.settings.connection_timeout, headers=headers, json=params) folders_packs = r.json() self.__folders.clear() self.__packs.clear() for i in folders_packs: node_type = i.get("NodeKind") current = None if node_type == "Folder": current = self.__folders elif node_type == "KnowledgePack": current = self.__packs else: continue current[i.get("Id")] = { "parent_id": i.get("ParentId"), "name": i.get("Name") }
def get_task_info(self, task_id: str) -> dict: """ Получить информацию по задаче :return: """ if len(self.__tasks) == 0: self.get_tasks_list() api_url = self.__api_task_info.format(task_id) url = "https://{}{}".format(self.__core_hostname, api_url) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) r = r.json() task = self.__tasks.get(task_id) if task is None: raise Exception("Task {} not found".format(task_id)) task["parameters"] = r.get("parameters") self.log.info( 'status=success, action=get_task_info, msg="Got info for task {}", ' 'hostname="{}"'.format(task_id, self.__core_hostname)) return task
def get_databases_list(self) -> dict: """ Получить список БД :return: {'db_name': {'param1': 'value1'}} """ # TODO Не учитывается что БД с разными родительскими БД могут иметь одинаковое имя url = "https://{}:{}{}".format(self.__kb_hostname, self.__kb_port, self.__api_kb_db_list) r = exec_request(self.__kb_session, url, method='GET', timeout=self.settings.connection_timeout) db_names = r.json() ret = {} for i in db_names: name = i.get("Name") ret[name] = { "id": i.get("Uid"), "status": i.get("Status").lower(), "updatable": i.get("IsUpdatable"), "deployable": i.get("IsDeployable"), "parent": i.get("ParentName"), "revisions": i.get("RevisionsCount") } self.log.info( 'status=success, action=get_databases_list, msg="Got {} databases", ' 'hostname="{}"'.format(len(ret), self.__kb_hostname)) return ret
def get_health_agents_status(self) -> List[dict]: """ Получить статус агентов. :return: Список агентов и их параметры. """ url = "https://{}{}".format(self.__core_hostname, self.__api_agents_status) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() agents = [] for i in response: agents.append({ "id": i.get("id"), "name": i.get("name"), "hostname": i.get("address"), "version": i.get("version"), "updates": i.get("availableUpdates"), "status": i.get("status"), "roles": i.get("roleNames"), "ip": i.get("ipAddresses"), "platform": i.get("platform"), "modules": i.get("modules") }) self.log.info( 'status=success, action=get_license_status, msg="Got agents status", ' 'hostname="{}" count="{}"'.format(self.__core_hostname, len(agents))) return agents
def get_health_license_status(self) -> dict: """ Получить статус лицензии. :return: dict """ url = "https://{}{}".format(self.__core_hostname, self.__api_license_status) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() lic = response.get("license") status = { "valid": response.get("validity") == "valid", "key": lic.get("keyNumber"), "type": lic.get("licenseType"), "granted": lic.get("keyDate"), "expiration": lic.get("expirationDate"), "assets": lic.get("assetsCount") } self.log.info( 'status=success, action=get_license_status, msg="Got license status", ' 'hostname="{}"'.format(self.__core_hostname)) return status
def get_table_info(self, table_name) -> dict: """ Получить метаданные по табличке :param table_name: Имя таблицы :return: {'property': 'value'} """ self.log.debug('status=prepare, action=get_groups, msg="Try to get table info for {}", ' 'hostname="{}"'.format(table_name, self.__core_hostname)) table_id = self.get_table_id_by_name(table_name) api_url = self.__api_table_info.format(table_id) url = "https://{}{}".format(self.__core_hostname, api_url) rq = exec_request(self.__core_session, url, method="GET", timeout=self.settings.connection_timeout) response = dict(rq.json()) table_info = self.__tables_cache.get(table_name) table_info["size_max"] = response.get("maxSize") table_info["size_typical"] = response.get("typicalSize") table_info["ttl"] = response.get("ttl") table_info["description"] = response.get("description") table_info["created"] = response.get("created") table_info["updated"] = response.get("lastUpdated") table_info["size_current"] = response.get("currentSize") table_info["fields"] = response.get("fields") self.log.info('status=success, action=get_table_info, msg="Get {} properties for table {}", ' 'hostname="{}"'.format(len(table_info), table_name, self.__core_hostname)) return table_info
def __manipulate_rule(self, db_name: str, content_type: str, guids_list: list, control="stop"): # нет гарантий, что объекты в PT KB и SIEM будут называться одинаково. # сейчас в классе прописаны названия из PT KB. Название табличек уже разное. object_type = None if content_type == MPContentTypes.CORRELATION: object_type = "correlation" elif content_type == MPContentTypes.ENRICHMENT: object_type = "enrichment" else: raise Exception( "Unsupported content type to stop {}".format(content_type)) if len(self.__rules_mapping) == 0: self.__update_rules_mapping(db_name, content_type) rules_names = [] for i in guids_list: name = self.__rules_mapping.get(db_name, {}).get(content_type, {}).get(i, {}).get("name") if name is None: self.log.error( 'status=failed, action=manipulate_rule, msg="Rule id not found", ' 'hostname="{}", db="{}", rule_id="{}"'.format( self.__kb_hostname, db_name, i)) rules_names.append(name) data = {"names": rules_names} api_url = self.__api_rule_start.format(object_type) if control == "start" \ else self.__api_rule_stop.format(object_type) url = "https://{}{}".format(self.__kb_hostname, api_url) r = exec_request(self.__kb_session, url, method='POST', timeout=self.settings.connection_timeout, json=data) response = r.json() if len(response.get("error")) != 0: self.log.error( 'status=failed, action=manipulate_rule, ' 'msg="Got error while manipulate rule", hostname="{}", rules_ids="{}", ' 'rules_names="{}", db="{}", error="{}"'.format( self.__kb_hostname, guids_list, rules_names, db_name, response.get("error"))) raise Exception("Got error while manipulate rule") self.log.info( 'status=success, action=manipulate_rule, msg="{} {} rules", ' 'hostname="{}", rules_names="{}", db="{}"'.format( control, object_type, self.__kb_hostname, rules_names, db_name))
def get_tasks_list(self, do_refresh=False) -> dict: """ Получить список всех задач. Информация урезана. :return: """ if len(self.__tasks) != 0 and not do_refresh: return self.__tasks url = "https://{}{}".format(self.__core_hostname, self.__api_tasks_list) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() for i in response: profile = { "id": i.get("profile", {}).get("id").replace("{", "").replace("}", ""), "name": i.get("name") } self.__tasks[i.get("id")] = { "name": i.get("name"), "agent": i.get("agent"), "scope": i.get("scope"), "profile": profile, "module": i.get("module"), "transports": i.get("metatransports"), "status": i.get("status"), "created": i.get("created"), "run_last": i.get("lastRun"), "run_last_error_level": i.get("lastRunErrorLevel"), "run_last_error": i.get("lastRunError"), "target_include": i.get("include"), "target_exclude": i.get("exclude"), "status_validation": i.get("validationState"), "host_discovery": i.get("hostDiscovery"), "bookmarks": i.get("hasBookmarks"), "credentials": i.get("credentials"), "trigger_parameters": i.get("triggerParameters") } self.log.info( 'status=success, action=get_tasks_list, msg="Got task list", ' 'hostname="{}", count={}'.format(self.__core_hostname, len(self.__tasks))) return self.__tasks
def set_table_data(self, table_name: str, data: IO) -> None: """ Импортировать бинарные данные в табличный список. Данные должны быть в формате CSV, понятном MP SIEM. Usage: with open("import.csv", "rb") as data: Tables.set_data("table_name", data) :param table_name: Имя таблицы :param data: Поток бинарных данных для вставки :return: None """ self.log.debug('status=prepare, action=get_groups, msg="Try to import data to {}", ' 'hostname="{}"'.format(table_name, self.__core_hostname)) api_url = self.__api_table_import.format(table_name) url = "https://{}{}".format(self.__core_hostname, api_url) headers = {'Content-Type': 'application/x-www-form-urlencoded'} rq = exec_request(self.__core_session, url, method="POST", timeout=self.settings.connection_timeout, data=data, headers=headers) response = rq.json() total_records = response.get('recordsNum') imported_records = response.get('importedNum') bad_records = response.get('badRecordsNum') skipped_records = response.get('skippedRecordsNum') if (imported_records == 0 or imported_records <= bad_records + skipped_records) and total_records != 0: self.log.error('status=failed, action=set_table_data, msg="Importing data to table {} ends with error", ' 'hostname="{}", total={}, imported={}, bad={}, skipped={}'.format(table_name, self.__core_hostname, total_records, imported_records, bad_records, skipped_records)) raise Exception("Importing data to table {} ends with error".format(table_name)) if bad_records != 0 or skipped_records != 0: self.log.error('status=warning, action=set_table_data, msg="Some data not imported to table {}", ' 'hostname="{}", total={}, imported={}, bad={}, skipped={}'.format(table_name, self.__core_hostname, total_records, imported_records, bad_records, skipped_records)) self.log.info('status=success, action=set_table_data, msg="Data imported to table {}", ' 'hostname="{}", lines={}'.format(table_name, self.__core_hostname, imported_records))
def __iterate_table(self, url, params, offset, limit): params["offset"] = offset params["limit"] = limit rq = exec_request(self.__core_session, url, method="POST", timeout=self.settings.connection_timeout, json=params) response = rq.json() if response is None or "items" not in response: self.log.error('status=failed, action=table_iterate, msg="Table data request return None or ' 'has wrong response structure", ' 'hostname="{}"'.format(self.__core_hostname)) raise Exception("Table data request return None or has wrong response structure") return response.get("items")
def get_rule_running_state(self, db_name: str, content_type: str, guid: str): """ Получить статус правила, работающего в SIEM Server. Используются ID правил из KB. :param db_name: Имя БД в KB :param content_type: MPContentType :param guid: ID правила :return: """ object_type = None if content_type == MPContentTypes.CORRELATION: object_type = "correlation" elif content_type == MPContentTypes.ENRICHMENT: object_type = "enrichment" else: raise Exception( "Unsupported content type to stop {}".format(content_type)) if len(self.__rules_mapping) == 0: self.__update_rules_mapping(db_name, content_type) name = self.__rules_mapping.get(db_name, {}).get(content_type, {}).get(guid, {}).get("name") api_url = self.__api_rule_running_info.format(object_type, name) url = "https://{}{}".format(self.__kb_hostname, api_url) r = exec_request(self.__kb_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() state = response.get("state", {}) return { "state": state.get("name"), "reason": state.get("reason"), "context": state.get("context") }
def get_health_status(self) -> str: """ Получить общее состояние системы :return: "ok" - если нет ошибок """ url = "https://{}{}".format(self.__core_hostname, self.__api_global_status) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() status = response.get("status") self.log.info( 'status=success, action=get_global_status, msg="Got global status", ' 'hostname="{}" status="{}"'.format(self.__core_hostname, status)) return status
def __manipulate_task(self, task_id, control="stop"): api_url = (self.__api_task_start if control == "start" else self.__api_task_stop).format(task_id) url = "https://{}{}".format(self.__core_hostname, api_url) r = exec_request(self.__core_session, url, method='POST', timeout=self.settings.connection_timeout) run_id = None if control == "start": response = r.json() run_id = response.get("id") if run_id is None: raise Exception("Task manipulation error") self.log.info( 'status=success, action=manipulate_task, msg="{} task {}", ' 'hostname="{}"'.format(control, task_id, self.__core_hostname)) return run_id
def __iterate_table_rows(self, url: str, params: dict, headers: dict, offset: int, limit: int): params["skip"] = offset params["take"] = limit rq = exec_request(self.__kb_session, url, method="POST", timeout=self.settings.connection_timeout, headers=headers, json=params) response = rq.json() if response is None or "Rows" not in response: self.log.error( 'status=failed, action=kb_objects_iterate, msg="KB data request return None or ' 'has wrong response structure", ' 'hostname="{}"'.format(self.__kb_hostname)) raise Exception( "KB data request return None or has wrong response structure") return response.get("Rows")
def get_profiles_list(self) -> dict: """ Получить список всех профилей. Информация урезана. :return: """ if len(self.__profiles) != 0: return self.__profiles url = "https://{}{}".format(self.__core_hostname, self.__api_profiles_list) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() for i in response: # почему-то ID выглядит как "{2341234-1234-234-2388}" base_profile = i.get("baseProfileName") profile_id = i.get("id", "").replace("{", "").replace("}", "") self.__profiles[profile_id] = { "name": i.get("name"), "system": i.get("isSystem"), "base_profile": base_profile.replace("\"", "") if base_profile else None, "module_id": i.get("moduleId"), "output": i.get("output") } self.log.info( 'status=success, action=get_profiles_list, msg="Got profiles list", ' 'hostname="{}", count={}'.format(self.__core_hostname, len(self.__profiles))) return self.__profiles
def __iterate_items(self, url: str, params: dict, offset: int, limit: int): params["offset"] = offset params["limit"] = limit # url = "https://mp-core-dev.rt-solar.local/api/events_monitoring/v2/sources?controlState=all&limit=50&offset=0&timeFrom=2020-12-03T21:00:00.000Z&timeTo=2020-12-05T20:59:59.999Z" rq = exec_request(self.__core_session, url, method="GET", timeout=self.settings.connection_timeout, params=params) response = rq.json() if response is None: self.log.error( 'status=failed, action=monitor_items_iterate, msg="Core data request return None or ' 'has wrong response structure", ' 'hostname="{}"'.format(self.__core_hostname)) raise Exception( "Core data request return None or has wrong response structure" ) return response
def get_health_errors(self) -> List[dict]: """ Получить список ошибок из семафора. :return: Список ошибок или пустой массив, если ошибок нет """ limit = 1000 offset = 0 api_url = self.__api_checks.format(limit, offset) url = "https://{}{}".format(self.__core_hostname, api_url) r = exec_request(self.__core_session, url, method='GET', timeout=self.settings.connection_timeout) response = r.json() errors = response.get("items") self.log.info('status=success, action=get_errors, msg="Got errors", ' 'hostname="{}" count="{}"'.format( self.__core_hostname, len(errors))) return errors
def install_objects(self, db_name: str, guids_list: list, do_remove=False) -> str: """ Установить объекты из KB в SIEM :param db_name: Имя БД :param guids_list: Список обЪектов для установки :param do_remove: :return: deploy ID """ self.log.info( 'status=prepare, action=install_objects, msg="Try to {} objects {}", ' 'hostname="{}", db="{}"'.format( "install" if not do_remove else "uninstall", guids_list, self.__kb_hostname, db_name)) headers = {'Content-Database': db_name, 'Content-Locale': 'RUS'} params = { "mode": "selection" if not do_remove else "uninstall", "include": guids_list } url = "https://{}:{}{}".format(self.__kb_hostname, self.__kb_port, self.__api_deploy_object) r = exec_request(self.__kb_session, url, method='POST', timeout=self.settings.connection_timeout, headers=headers, json=params) response = r.json() self.log.info( 'status=success, action=install_objects, msg="{} objects {}", ' 'hostname="{}", db="{}"'.format( "Install" if not do_remove else "Uninstall", guids_list, self.__kb_hostname, db_name)) return response.get("Id")
def truncate_table(self, table_name: str) -> None: """ Очистить табличный список :param table_name: Имя таблицы :return: None """ self.log.debug('status=prepare, action=truncate_table, msg="Try to truncate table {}", ' 'hostname="{}"'.format(table_name, self.__core_hostname)) api_url = self.__api_table_truncate.format(self.get_table_id_by_name(table_name)) url = "https://{}{}".format(self.__core_hostname, api_url) rq = exec_request(self.__core_session, url, method="DELETE", timeout=self.settings.connection_timeout) response = rq.json() if "result" not in response or response.get("result") != "success": self.log.error('status=failed, action=table_truncate, msg="Table {} have not been truncated", ' 'hostname="{}"'.format(table_name, self.__core_hostname)) raise Exception("Table {} have not been truncated".format(table_name)) self.log.info('status=success, action=truncate_table, msg="Table {} have been truncated", ' 'hostname="{}"'.format(table_name, self.__core_hostname))
def get_applications_list(self) -> dict: """ Получить информацию по приложениям, включая тенанты :return: {'app_id': {'name': 'value', 'tenants': ['', '']}} """ self.__applications.clear() self.log.debug( 'status=prepare, action=get_applications, msg="Try to get applications as {}", ' 'hostname="{}"'.format(self.auth.creds.core_login, self.__ms_hostname)) url = "https://{}:{}{}".format(self.__ms_hostname, self.__ms_port, self.__api_applications_list) rq = exec_request(self.__ms_session, url, method="GET", timeout=self.settings.connection_timeout) response = rq.json() for i in response: app_id = i.get("id") app_name = i.get("name") app_tenants = i.get("tenantIds") if self.__applications.get(app_id) is None: self.__applications[app_id] = {} self.__applications[app_id] = { 'name': app_name, 'tenants_ids': app_tenants } self.log.info( 'status=success, action=get_applications, msg="Got {} apps", ' 'hostname="{}"'.format(len(self.__applications), self.__ms_hostname)) return self.__applications
def get_tables_list(self) -> dict: """ Получить список всех установленных табличных списков :return: {'id': 'name'} """ self.log.debug('status=prepare, action=get_groups, msg="Try to get table list", ' 'hostname="{}"'.format(self.__core_hostname)) url = "https://{}{}".format(self.__core_hostname, self.__api_table_list) rq = exec_request(self.__core_session, url, method="GET", timeout=self.settings.connection_timeout) self.__tables_cache.clear() response = rq.json() for i in response: self.__tables_cache[i["name"]] = {"id": i.get("token"), "type": i.get("fillType").lower(), "editable": i.get("editable"), "ttl_enabled": i.get("ttlEnabled"), "notifications": i.get("notifications")} self.log.info('status=success, action=get_table_list, msg="Found {} tables", ' 'hostname="{}"'.format(len(self.__tables_cache), self.__core_hostname)) return self.__tables_cache