""" Terraform-совместимые операции: нормализация параметров, refSvc-резолв, отправка. Используется из api_test.py и scenario.py. """ import json from operations.get_instances import get_instances REDACT_KEYS = {"password", "secret", "token", "key", "privatekey", "private_key", "passwd", "pass"} def redact_params(params): """Заменить значения секретных полей на *** перед сохранением в БД.""" if not params: return params result = {} for k, v in params.items(): if any(r in str(k).lower() for r in REDACT_KEYS): result[k] = "***" else: result[k] = v return result def normalize_value(val, data_type, data_descriptor): """normalizeUniversalValueV6 — Terraform-equivalent. Пустые значения нормализуются по типу: integer→"0", boolean→"false", array→"[]", map/json→"{}", map-fixed→сборка из sub-param defaults.""" val = (val or "").strip() if val.lower() == "null" or val == '""': val = "" dt = (data_type or "").lower() if ("map" in dt) and isinstance(data_descriptor, dict) and data_descriptor: if not val or val == "{}": sub_obj = {} for sk, sv in data_descriptor.items(): sd = sv.get("defaultValue") sub_obj[sk] = str(sd) if sd is not None else "" return json.dumps(sub_obj) return val if val: return val if "array" in dt: return "[]" if "map" in dt or "json" in dt: return "{}" if "integer" in dt or "int" in dt: return "0" if "boolean" in dt or "bool" in dt: return "false" return val def resolve_ref_svc(client, cfs_params, params): """resolveRefSvcParamValues — Terraform step 4. Для параметров с refSvcId: если значение пустое — подставить UUID существующего инстанса этого сервиса.""" instances = None for p in cfs_params: pid = str(p["svcOperationCfsParamId"]) # Верхнеуровневый refSvcId top_ref = p.get("refSvcId") if top_ref: cur = (params.get(pid) or "").strip() if not cur or cur == '""': if instances is None: try: instances = get_instances(client) except Exception: return match = next((i for i in instances if i.get("serviceId") == top_ref and i.get("explainedStatus") not in ("deleted",)), None) if match: params[pid] = match["instanceUid"] # Вложенный refSvcId в dataDescriptor dd = p.get("dataDescriptor") if not isinstance(dd, dict): continue user_val = {} if pid in params: try: user_val = json.loads(params[pid]) if params[pid] else {} except (json.JSONDecodeError, ValueError): user_val = {} for sk, sv in dd.items(): ref_id = sv.get("refSvcId") if not ref_id: continue cur = user_val.get(sk, "") if cur and cur != '""': continue if instances is None: try: instances = get_instances(client) except Exception: return match = next((i for i in instances if i.get("serviceId") == ref_id and i.get("explainedStatus") not in ("deleted",)), None) if match: user_val[sk] = match["instanceUid"] if user_val: params[pid] = json.dumps(user_val) def send_params_terraform(client, op_uid, params): """Terraform steps 3-7: cfsParams → resolveRefSvc → send user → send all unsent → validate.""" op_det = client.get(f"/instanceOperations/{op_uid}?fields=cfsParams") cfs_params = op_det.get("instanceOperation", {}).get("cfsParams", []) resolve_ref_svc(client, cfs_params, params) sent = set() for pid, pval in params.items(): client.post("/instanceOperationCfsParams", { "instanceOperationUid": op_uid, "svcOperationCfsParamId": int(pid), "paramValue": str(pval) }) sent.add(int(pid)) for p in cfs_params: pid = p["svcOperationCfsParamId"] if pid in sent: continue val = p.get("paramValue") if val is None: val = p.get("defaultValue") if val is None: val = "" val = normalize_value(str(val), p.get("dataType", ""), p.get("dataDescriptor")) client.post("/instanceOperationCfsParams", { "instanceOperationUid": op_uid, "svcOperationCfsParamId": pid, "paramValue": val }) try: client.get(f"/instanceOperations/{op_uid}/validate-cfs") except Exception as e: if "Expecting value" in str(e) or "JSON" in str(type(e).__name__): pass else: raise