diff --git a/CalmVersion b/CalmVersion index f28071967..fcdb2e109 100644 --- a/CalmVersion +++ b/CalmVersion @@ -1 +1 @@ -3.8.1 +4.0.0 diff --git a/Makefile b/Makefile index 9db8863ae..3c759a26d 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,15 @@ dev: venv/bin/pip3 install --no-cache -r requirements.txt -r dev-requirements.txt venv/bin/python3 setup.py develop +windev: + # Setup our python3 based virtualenv in windows machine + # This step assumes python3 is installed on your dev machine + [ -f venv/Scripts/python3 ] || python -m venv venv + venv/Scripts/python -m pip install --upgrade pip + venv/Scripts/pip install setuptools --upgrade --ignore-installed + venv/Scripts/pip install --no-cache -r requirements.txt -r dev-requirements.txt + venv/Scripts/python setup.py develop + test-bed: dev venv/bin/python3 tests/testprep.py diff --git a/README.md b/README.md index 51c1fc9dd..b25effb73 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ ![Build](https://github.com/nutanix/calm-dsl/workflows/Setup%20&%20build%20calm-dsl/badge.svg) -`Latest release version: 3.8.1, Latest-release-tag: v3.8.1` +`Latest release version: 4.0.0, Latest-release-tag: v4.0.0` -`Latest Release Notes:` [read here](release-notes/3.8.1) +`Latest Release Notes:` [read here](release-notes/4.0.0) # Nutanix Cloud Manager (NCM) Self Service (formerly Calm) DSL diff --git a/calm/dsl/api/provider.py b/calm/dsl/api/provider.py index 4d9c96d30..7cdb22798 100644 --- a/calm/dsl/api/provider.py +++ b/calm/dsl/api/provider.py @@ -1,5 +1,13 @@ +import json +import os + +from calm.dsl.log import get_logging_handle + from .resource import ResourceAPI from .connection import REQUEST +from .util import strip_provider_secrets, patch_secrets, strip_uuids + +LOG = get_logging_handle(__name__) class ProviderAPI(ResourceAPI): @@ -7,6 +15,33 @@ def __init__(self, connection): super().__init__(connection, resource_type="providers", calm_api=True) self.CREATE = self.PREFIX + self.BULK_CREATE = self.CREATE + "/bulk" + self.BULK_UPDATE = self.ITEM + "/bulk" + self.BULK_READ = self.ITEM + "/bulk" + self.COMPILE = self.ITEM + "/compile" + self.TEST_PROVIDER_VERIFY = self.PREFIX + "/{}/actions/{}/test_run" + self.ABORT_RUN = self.ITEM + "/runlogs/{}/abort" + self.POLL_RUN = self.ITEM + "/runlogs/{}" + self.CHILD_RUNLOG_LIST = self.ITEM + "/runlogs/{}/children/list" + self.RUNLOG_OUTPUT = self.ITEM + "/runlogs/{}/children/{}/output" + self.RUNLOG_LIST = self.CREATE + "/runlogs/list" + self.CLONE = self.ITEM + "/clone" + self.IMPORT_JSON = self.PREFIX + "/import_json" + self.IMPORT_FILE = self.PREFIX + "/import_file" + self.EXPORT_JSON = self.ITEM + "/export_json" + self.EXPORT_FILE = self.ITEM + "/export_file" + + def check_if_provider_already_exists(self, provider_name): + params = {"filter": "name=={};state!=DELETED".format(provider_name)} + res, err = self.list(params=params) + if err: + return None, err + + response = res.json() + entities = response.get("entities", None) + if entities and len(entities) > 0: + return entities[0], None + return None, None def create(self, provider_payload): return self.connection._call( @@ -16,3 +51,225 @@ def create(self, provider_payload): method=REQUEST.METHOD.POST, timeout=(5, 300), ) + + # This is equivalent to "compile" of whole Provider tree as present in Calm UI + def preview_validate(self, uuid): + return self.connection._call( + self.COMPILE.format(uuid), verify=False, method=REQUEST.METHOD.GET + ) + + def bulk_create(self, provider_payload): + res, err = self.connection._call( + self.BULK_CREATE, + verify=False, + request_json=provider_payload, + method=REQUEST.METHOD.POST, + ) + if err: + return res, err + + return self.preview_validate(res.json()["metadata"]["uuid"]) + + def bulk_read(self, id): + return self.connection._call( + self.BULK_READ.format(id), verify=False, method=REQUEST.METHOD.GET + ) + + def bulk_update(self, uuid, provider_payload): + res, err = self.connection._call( + self.BULK_UPDATE.format(uuid), + verify=False, + request_json=provider_payload, + method=REQUEST.METHOD.PUT, + ) + if err: + return res, err + + return self.preview_validate(uuid) + + def run(self, uuid, action_uuid, payload): + return self.connection._call( + self.TEST_PROVIDER_VERIFY.format(uuid, action_uuid), + verify=False, + request_json=payload, + method=REQUEST.METHOD.POST, + ) + + def list_child_runlogs(self, provider_uuid, runlog_uuid): + return self.connection._call( + self.CHILD_RUNLOG_LIST.format(provider_uuid, runlog_uuid), + verify=False, + request_json={}, + method=REQUEST.METHOD.POST, + ) + + def list_runlogs(self, payload=None): + return self.connection._call( + self.RUNLOG_LIST, + verify=False, + request_json=payload, + method=REQUEST.METHOD.POST, + ) + + def runlog_output(self, provider_uuid, runlog_uuid, child_runlog_uuid): + return self.connection._call( + self.RUNLOG_OUTPUT.format(provider_uuid, runlog_uuid, child_runlog_uuid), + verify=False, + method=REQUEST.METHOD.GET, + ) + + def poll_action_run(self, uuid, runlog_uuid, payload=None): + if payload: + return self.connection._call( + self.POLL_RUN.format(uuid, runlog_uuid), + request_json=payload, + verify=False, + method=REQUEST.METHOD.POST, + ) + else: + return self.connection._call( + self.POLL_RUN.format(uuid, runlog_uuid), + verify=False, + method=REQUEST.METHOD.GET, + ) + + def abort(self, uuid, runlog_uuid): + return self.connection._call( + self.ABORT_RUN.format(uuid, runlog_uuid), + verify=False, + request_json={}, + method=REQUEST.METHOD.POST, + ) + + def clone(self, uuid, clone_payload): + return self.connection._call( + self.CLONE.format(uuid), + verify=False, + request_json=clone_payload, + method=REQUEST.METHOD.POST, + ) + + def import_json(self, provider_json): + return self.connection._call( + self.IMPORT_JSON, + verify=False, + request_json=provider_json, + method=REQUEST.METHOD.POST, + ) + + def export_json(self, uuid): + return self.connection._call( + self.EXPORT_JSON.format(uuid), verify=False, method=REQUEST.METHOD.GET + ) + + def export_file(self, uuid, passphrase=None): + if passphrase: + return self.connection._call( + self.EXPORT_FILE.format(uuid), + verify=False, + method=REQUEST.METHOD.POST, + request_json={"passphrase": passphrase}, + files=[], + ) + return self.connection._call( + self.EXPORT_FILE.format(uuid), verify=False, method=REQUEST.METHOD.GET + ) + + def export_provider(self, uuid, passphrase=None): + current_path = os.path.dirname(os.path.realpath(__file__)) + if passphrase: + res, err = self.connection._call( + self.EXPORT_FILE.format(uuid), + verify=False, + method=REQUEST.METHOD.POST, + request_json={"passphrase": passphrase}, + files=[], + ) + else: + res, err = self.connection._call( + self.EXPORT_FILE.format(uuid), verify=False, method=REQUEST.METHOD.GET + ) + + if err: + raise Exception("[{}] - {}".format(err["code"], err["error"])) + + with open(current_path + "/" + uuid + ".json", "wb") as downloaded_file: + for chunk in res.iter_content(chunk_size=2048): + downloaded_file.write(chunk) + + return current_path + "/" + uuid + ".json" + + def upload_using_import_file(self, payload, files): + return self.connection._call( + self.IMPORT_FILE, + verify=False, + files=files, + request_json=payload, + method=REQUEST.METHOD.POST, + ) + + def upload_with_decompiled_secrets( + self, + provider_payload, + passphrase, + decompiled_secrets=[], + ): + """ + Used to create a provider if it contains encrypted secrets from decompilation + + Args: + provider_payload (dict): payload of the provider + passphrase (string): passphrase for creating provider with secrets (it should be same as the one provided while decompilation) + decompiled_secrets (list): contains all the secrets that were present in the decompiled provider + """ + secret_map = {} + secret_variables = [] + not_stripped_secrets = [] + provider_name = provider_payload["spec"]["name"] + provider_resources = provider_payload["spec"]["resources"] + LOG.debug("provider_resources pre-stripping secrets") + LOG.debug(provider_resources) + strip_provider_secrets( + provider_name, + provider_resources, + secret_map, + secret_variables, + decompiled_secrets=decompiled_secrets, + not_stripped_secrets=not_stripped_secrets, + ) + LOG.debug("provider_resources post-stripping secrets") + LOG.debug(provider_resources) + + strip_uuids(provider_resources) + LOG.debug("provider_resources post-stripping UUIDs") + LOG.debug(provider_resources) + provider_payload["spec"]["resources"] = provider_resources + files = {"file": ("file", json.dumps(provider_payload))} + res, err = self.upload_using_import_file( + {"name": provider_name, "passphrase": passphrase}, files + ) + if err: + return res, err + + # Add secrets and update provider + provider = res.json() + del provider["status"] + LOG.info("Patching newly created/updated secrets") + for k in secret_map: + LOG.debug("[CREATED/MODIFIED] credential -> '{}'".format(k)) + for s in secret_variables: + LOG.debug("[CREATED/MODIFIED] variable -> '{}' path: {}".format(s[2], s[0])) + + patch_secrets( + provider["spec"]["resources"], + secret_map, + secret_variables, + not_stripped_secrets, + ) + LOG.debug("Update provider payload:") + LOG.debug(provider) + + # Update provider + update_payload = provider + uuid = provider["metadata"]["uuid"] + return self.bulk_update(uuid, update_payload) diff --git a/calm/dsl/api/resource_type.py b/calm/dsl/api/resource_type.py index 8e7090d54..ebea7def8 100644 --- a/calm/dsl/api/resource_type.py +++ b/calm/dsl/api/resource_type.py @@ -6,9 +6,11 @@ class ResourceTypeAPI(ResourceAPI): def __init__(self, connection): super().__init__(connection, resource_type="resource_types", calm_api=True) self.CREATE = self.PREFIX + self.LIST = self.PREFIX + "/list" self.TEST_RUNBOOK = self.PREFIX + "/{}/test_runbook/{}/run" self.PLATFORM_LIST = self.PREFIX + "/platform_list" self.UPDATE = self.PREFIX + "/{}" + self.TEST_EXECUTE = self.PREFIX + "/{}/actions/{}/test_run" def create(self, resource_type_payload): return self.connection._call( @@ -19,6 +21,16 @@ def create(self, resource_type_payload): timeout=(5, 300), ) + def list(self, payload={}): + if not payload.get("length"): + payload["length"] = 20 + return self.connection._call( + self.LIST, + verify=False, + request_json=payload, + method=REQUEST.METHOD.POST, + ) + def update(self, uuid, resource_type_payload): return self.connection._call( self.UPDATE.format(uuid), @@ -27,6 +39,14 @@ def update(self, uuid, resource_type_payload): method=REQUEST.METHOD.PUT, ) + def run(self, resource_type_uuid, action_uuid, payload): + return self.connection._call( + self.TEST_EXECUTE.format(resource_type_uuid, action_uuid), + request_json=payload, + verify=False, + method=REQUEST.METHOD.POST, + ) + def run_test_runbook(self, resource_type_id, action_id, payload): return self.connection._call( self.TEST_RUNBOOK.format(resource_type_id, action_id), diff --git a/calm/dsl/api/runbook.py b/calm/dsl/api/runbook.py index 29f914efd..945b03546 100644 --- a/calm/dsl/api/runbook.py +++ b/calm/dsl/api/runbook.py @@ -1,4 +1,5 @@ import os +import sys from distutils.version import LooseVersion as LV @@ -6,8 +7,11 @@ from .connection import REQUEST from .util import strip_secrets, patch_secrets from calm.dsl.config import get_context +from calm.dsl.log import get_logging_handle from .project import ProjectAPI +LOG = get_logging_handle(__name__) + class RunbookAPI(ResourceAPI): def __init__(self, connection): @@ -34,6 +38,7 @@ def __init__(self, connection): self.MARKETPLACE_EXECUTE = self.PREFIX + "/marketplace_execute" self.MARKETPLACE_CLONE = self.PREFIX + "/marketplace_clone" self.VARIABLE_VALUES = self.ITEM + "/variables/{}/values" + self.CLONE = self.PREFIX + "/{}/clone" def upload(self, payload): return self.connection._call( @@ -435,3 +440,21 @@ def variable_values(self, uuid, var_uuid, payload={}): return self.connection._call( url, verify=False, method=REQUEST.METHOD.POST, request_json=payload ) + + def clone(self, uuid, payload): + from calm.dsl.store.version import Version + + calm_version = Version.get_version("Calm") + + if LV(calm_version) < LV("4.0.0"): + LOG.error( + "Runbook clone is supported from Calm version 4.0.0. Please upgrade your Calm version to use this feature." + ) + sys.exit("Runbook clone is supported from calm version 4.0.0 onwards") + + return self.connection._call( + self.CLONE.format(uuid), + verify=False, + request_json=payload, + method=REQUEST.METHOD.POST, + ) diff --git a/calm/dsl/api/setting.py b/calm/dsl/api/setting.py index 0d68b4c0d..81de8d7c7 100644 --- a/calm/dsl/api/setting.py +++ b/calm/dsl/api/setting.py @@ -69,7 +69,7 @@ def create(self, account_name, account_payload, force_create): return None, err response = res.json() - entities = response.get("entitites", None) + entities = response.get("entities", None) if entities: if len(entities) > 0: if not force_create: diff --git a/calm/dsl/api/util.py b/calm/dsl/api/util.py index 0ef20d713..83823df98 100644 --- a/calm/dsl/api/util.py +++ b/calm/dsl/api/util.py @@ -59,29 +59,12 @@ def strip_secrets( """ # Remove creds before upload - creds = resources.get("credential_definition_list", []) or [] - filtered_decompiled_secret_credentials = get_secrets_from_context( - decompiled_secrets, "credential_definition_list" - ) - - default_creds = [] - for cred in creds: - name = cred["name"] - value = cred["secret"]["value"] - - if is_secret_modified(filtered_decompiled_secret_credentials, name, value): - secret_map[name] = cred.pop("secret", {}) - # Explicitly set defaults so that secret is not created at server - # TODO - Fix bug in server: {} != None - cred["secret"] = { - "attrs": {"is_secret_modified": False, "secret_reference": None} - } + strip_credentials(resources, decompiled_secrets, secret_map) + # Remove creds from HTTP endpoints resources filtered_decompiled_secret_auth_creds = get_secrets_from_context( decompiled_secrets, "authentication" ) - - # Remove creds from HTTP endpoints resources auth = resources.get("authentication", {}) or {} if auth.get("type", None) == "basic": if is_secret_modified( @@ -91,286 +74,473 @@ def strip_secrets( secret_map[name] = auth.pop("password", {}) auth["password"] = {"attrs": {"is_secret_modified": False, "value": None}} - # Strip secret variable values - # TODO: Refactor and/or clean this up later + for object_list in object_lists: + for obj_idx, obj in enumerate(resources.get(object_list, []) or []): + strip_all_secret_variables( + [object_list, obj_idx], + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + ) - def strip_entity_secret_variables( - path_list, obj, field_name="variable_list", context="" - ): + # Currently, deployment actions and variables are unsupported. + # Uncomment the following lines if and when the API does support them. + # if object_list == "app_profile_list": + # for dep_idx, dep in enumerate(obj["deployment_create_list"]): + # strip_all_secret_variables( + # [object_list, obj_idx, "deployment_create_list", dep_idx], + # dep, decompiled_secrets, secret_variables, not_stripped_secrets, + # ) - if field_name != "headers" and obj.get("name", None): - context = context + "." + obj["name"] + "." + field_name + for obj in objects: + strip_all_secret_variables( + [obj], + resources.get(obj, {}), + decompiled_secrets, + secret_variables, + not_stripped_secrets, + ) - filtered_decompiled_secrets = get_secrets_from_context( - decompiled_secrets, context + +def strip_provider_secrets( + provider_name, + provider_resources, + secret_map, + secret_variables, + decompiled_secrets=[], + not_stripped_secrets=[], +): + """ + Strips secrets from provider resources + Args: + provider_resources (dict): request payload + secret_map (dict): credential secret values + secret_variables (list): list of secret variables + Returns: None + """ + # Remove creds before upload + strip_credentials(provider_resources, decompiled_secrets, secret_map) + + for obj_idx, obj in enumerate( + provider_resources.get("resource_type_list", []) or [] + ): + path_list = ["resource_type_list", obj_idx] + strip_all_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + ) + strip_entity_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context=path_list[0], + field_name="schema_list", ) - for var_idx, variable in enumerate(obj.get(field_name, []) or []): - if variable["type"] == "SECRET": - if is_secret_modified( - filtered_decompiled_secrets, - variable.get("name", None), - variable.get("value", None), - ): - secret_variables.append( - ( - path_list + [field_name, var_idx], - variable.pop("value"), - variable.get("name", None), - ) - ) - variable["attrs"] = { - "is_secret_modified": False, - "secret_reference": None, - } - elif variable.get("value", None): - not_stripped_secrets.append( - (path_list + [field_name, var_idx], variable["value"]) - ) - # For dynamic variables having http task with auth - opts = variable.get("options", None) - auth = None - if opts: - attrs = opts.get("attrs", None) - if attrs: - auth = attrs.get("authentication", None) - if auth and auth.get("auth_type") == "basic": - basic_auth = auth.get("basic_auth") - username = basic_auth.get("username") - password = basic_auth.pop("password") + # NOTE: Not calling strip_all_secret_variables for provider spec because the context + # building logic assumes heavily that actions, variables, runbooks etc..,. always + # belong to an entity thats part of a list. Eg: profile_list, substrate_list etc..,. + + # Strip secrets in endpoint schema, auth schema, attribute & test account variables + strip_entity_secret_variables( + [], + provider_resources, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context="variable_list", + ) + strip_entity_secret_variables( + [], + provider_resources, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context="auth_schema_list", + field_name="auth_schema_list", + ) + strip_entity_secret_variables( + ["endpoint_schema"], + provider_resources.get("endpoint_schema", {}), + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context="endpoint_schema_list", + ) + strip_entity_secret_variables( + ["test_account", "data"], + provider_resources.get("test_account", {}).get("data", {}), + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context="test_account_variable_list", + ) + + # Strip secrets in provider actions & their runbooks + strip_action_secret_variables( + [], + provider_resources, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context="action_list", + ) + + +def strip_entity_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + field_name="variable_list", + context="", +): + + if field_name != "headers" and obj.get("name", None): + context = context + "." + obj["name"] + "." + field_name + + filtered_decompiled_secrets = get_secrets_from_context(decompiled_secrets, context) + + for var_idx, variable in enumerate(obj.get(field_name, []) or []): + if variable["type"] == "SECRET": + if is_secret_modified( + filtered_decompiled_secrets, + variable.get("name", None), + variable.get("value", None), + ): secret_variables.append( ( - path_list - + [ - field_name, - var_idx, - "options", - "attrs", - "authentication", - "basic_auth", - "password", - ], - password.get("value", None), - username, + path_list + [field_name, var_idx], + variable.pop("value"), + variable.get("name", None), ) ) - basic_auth["password"] = { - "value": None, - "attrs": {"is_secret_modified": False}, + variable["attrs"] = { + "is_secret_modified": False, + "secret_reference": None, } + elif variable.get("value", None): + not_stripped_secrets.append( + (path_list + [field_name, var_idx], variable["value"]) + ) + # For dynamic variables having http task with auth + opts = variable.get("options", None) + auth = None + if opts: + attrs = opts.get("attrs", None) + if attrs: + auth = attrs.get("authentication", None) + if auth and auth.get("auth_type") == "basic": + basic_auth = auth.get("basic_auth") + username = basic_auth.get("username") + password = basic_auth.pop("password") + secret_variables.append( + ( + path_list + + [ + field_name, + var_idx, + "options", + "attrs", + "authentication", + "basic_auth", + "password", + ], + password.get("value", None), + username, + ) + ) + basic_auth["password"] = { + "value": None, + "attrs": {"is_secret_modified": False}, + } + - def strip_action_secret_variables(path_list, obj): +def strip_action_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context="", +): + if not context: context = path_list[0] + "." + obj["name"] + ".action_list" - for action_idx, action in enumerate(obj.get("action_list", []) or []): - var_context = context + "." + action["name"] - runbook = action.get("runbook", {}) or {} - var_runbook_context = var_context + ".runbook" + for action_idx, action in enumerate(obj.get("action_list", []) or []): + var_context = context + "." + action["name"] + runbook = action.get("runbook", {}) or {} + var_runbook_context = var_context + ".runbook" - if not runbook: - return - strip_entity_secret_variables( - path_list + ["action_list", action_idx, "runbook"], - runbook, - context=var_runbook_context, - ) + if not runbook: + return + strip_entity_secret_variables( + path_list + ["action_list", action_idx, "runbook"], + runbook, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context=var_runbook_context, + ) - tasks = runbook.get("task_definition_list", []) + tasks = runbook.get("task_definition_list", []) - var_runbook_task_context = ( - var_runbook_context + "." + runbook["name"] + ".task_definition_list" - ) + var_runbook_task_context = ( + var_runbook_context + "." + runbook["name"] + ".task_definition_list" + ) - for task_idx, task in enumerate(tasks): - if task.get("type", None) != "HTTP": - continue - auth = (task.get("attrs", {}) or {}).get("authentication", {}) or {} + for task_idx, task in enumerate(tasks): + if task.get("type", None) != "HTTP": + continue + auth = (task.get("attrs", {}) or {}).get("authentication", {}) or {} - var_runbook_task_name_context = ( - var_runbook_task_context + "." + task["name"] - ) + var_runbook_task_name_context = ( + var_runbook_task_context + "." + task["name"] + ) - var_runbook_task_name_basic_auth_context = ( - var_runbook_task_context + "." + task["name"] + ".basic_auth" - ) + var_runbook_task_name_basic_auth_context = ( + var_runbook_task_context + "." + task["name"] + ".basic_auth" + ) - if auth.get("auth_type", None) == "basic": + if auth.get("auth_type", None) == "basic": - filtered_decompiled_secrets = get_secrets_from_context( - decompiled_secrets, var_runbook_task_name_basic_auth_context - ) + filtered_decompiled_secrets = get_secrets_from_context( + decompiled_secrets, var_runbook_task_name_basic_auth_context + ) - if is_secret_modified( - filtered_decompiled_secrets, - auth.get("basic_auth", {}).get("username", None), - auth.get("basic_auth", {}) - .get("password", {}) - .get("value", None), - ): - secret_variables.append( - ( - path_list - + [ - "action_list", - action_idx, - "runbook", - "task_definition_list", - task_idx, - "attrs", - "authentication", - "basic_auth", - "password", - ], - auth["basic_auth"]["password"].pop("value"), - auth.get("basic_auth", {}).get("username", None), - ) + if is_secret_modified( + filtered_decompiled_secrets, + auth.get("basic_auth", {}).get("username", None), + auth.get("basic_auth", {}).get("password", {}).get("value", None), + ): + secret_variables.append( + ( + path_list + + [ + "action_list", + action_idx, + "runbook", + "task_definition_list", + task_idx, + "attrs", + "authentication", + "basic_auth", + "password", + ], + auth["basic_auth"]["password"].pop("value"), + auth.get("basic_auth", {}).get("username", None), ) - auth["basic_auth"]["password"] = { - "attrs": { - "is_secret_modified": False, - "secret_reference": None, - } + ) + auth["basic_auth"]["password"] = { + "attrs": { + "is_secret_modified": False, + "secret_reference": None, } - elif ( - auth.get("basic_auth", None) - .get("password", None) - .get("value", None) - ): - not_stripped_secrets.append( - ( - path_list - + [ - "action_list", - action_idx, - "runbook", - "task_definition_list", - task_idx, - "attrs", - "authentication", - "basic_auth", - "password", - ], - auth["basic_auth"]["password"]["value"], - ) + } + elif ( + auth.get("basic_auth", None) + .get("password", None) + .get("value", None) + ): + not_stripped_secrets.append( + ( + path_list + + [ + "action_list", + action_idx, + "runbook", + "task_definition_list", + task_idx, + "attrs", + "authentication", + "basic_auth", + "password", + ], + auth["basic_auth"]["password"]["value"], ) - - http_task_headers = (task.get("attrs", {}) or {}).get( - "headers", [] - ) or [] - if http_task_headers: - strip_entity_secret_variables( - path_list - + [ - "action_list", - action_idx, - "runbook", - "task_definition_list", - task_idx, - "attrs", - ], - task["attrs"], - field_name="headers", - context=var_runbook_task_name_context + ".headers", ) - def strip_runbook_secret_variables(path_list, obj): - - context = path_list[0] + "." + obj["name"] + ".task_definition_list" - - tasks = obj.get("task_definition_list", []) - original_path_list = copy.deepcopy(path_list) - for task_idx, task in enumerate(tasks): - path_list = original_path_list - if task.get("type", None) == "RT_OPERATION": - path_list = path_list + [ - "task_definition_list", - task_idx, - "attrs", - ] + http_task_headers = (task.get("attrs", {}) or {}).get("headers", []) or [] + if http_task_headers: strip_entity_secret_variables( - path_list, task["attrs"], field_name="inarg_list" + path_list + + [ + "action_list", + action_idx, + "runbook", + "task_definition_list", + task_idx, + "attrs", + ], + task["attrs"], + decompiled_secrets, + secret_variables, + not_stripped_secrets, + field_name="headers", + context=var_runbook_task_name_context + ".headers", ) - continue - elif task.get("type", None) != "HTTP": - continue - auth = (task.get("attrs", {}) or {}).get("authentication", {}) or {} - if auth.get("auth_type", None) == "basic": - path_list = path_list + ["runbook"] + +def strip_runbook_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, +): + + context = path_list[0] + "." + obj["name"] + ".task_definition_list" + + tasks = obj.get("task_definition_list", []) + original_path_list = copy.deepcopy(path_list) + for task_idx, task in enumerate(tasks): + path_list = original_path_list + if task.get("type", None) == "RT_OPERATION": path_list = path_list + [ "task_definition_list", task_idx, "attrs", ] - var_task_context = context + "." + task["name"] - - strip_authentication_secret_variables( - path_list, task.get("attrs", {}) or {}, context=var_task_context - ) - - if not (task.get("attrs", {}) or {}).get("headers", []) or []: - continue strip_entity_secret_variables( path_list, task["attrs"], - field_name="headers", - context=var_task_context + ".headers", + decompiled_secrets, + secret_variables, + not_stripped_secrets, + field_name="inarg_list", ) + continue + elif task.get("type", None) != "HTTP": + continue + auth = (task.get("attrs", {}) or {}).get("authentication", {}) or {} + if auth.get("auth_type", None) == "basic": + path_list = path_list + ["runbook"] + + path_list = path_list + [ + "task_definition_list", + task_idx, + "attrs", + ] + var_task_context = context + "." + task["name"] + + strip_authentication_secret_variables( + path_list, + task.get("attrs", {}) or {}, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context=var_task_context, + ) - def strip_authentication_secret_variables(path_list, obj, context=""): + if not (task.get("attrs", {}) or {}).get("headers", []) or []: + continue + strip_entity_secret_variables( + path_list, + task["attrs"], + decompiled_secrets, + secret_variables, + not_stripped_secrets, + field_name="headers", + context=var_task_context + ".headers", + ) - basic_auth_context = context + "." + obj.get("name", "") + ".basic_auth" - filtered_decompiled_secrets = get_secrets_from_context( - decompiled_secrets, basic_auth_context - ) +def strip_authentication_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context="", +): - auth = obj.get("authentication", {}) - if auth.get("auth_type", None) == "basic": + basic_auth_context = context + "." + obj.get("name", "") + ".basic_auth" - if is_secret_modified( - filtered_decompiled_secrets, - auth.get("password", {}).get("name", None), - auth.get("password", {}).get("value", None), - ): - secret_variables.append( - ( - path_list + ["authentication", "basic_auth", "password"], - auth["password"].pop("value"), - auth.get("password", {}).get("name", None), - ) + filtered_decompiled_secrets = get_secrets_from_context( + decompiled_secrets, basic_auth_context + ) + + auth = obj.get("authentication", {}) + if auth.get("auth_type", None) == "basic": + + if is_secret_modified( + filtered_decompiled_secrets, + auth.get("password", {}).get("name", None), + auth.get("password", {}).get("value", None), + ): + secret_variables.append( + ( + path_list + ["authentication", "basic_auth", "password"], + auth["password"].pop("value"), + auth.get("password", {}).get("name", None), ) - auth["password"] = {"attrs": {"is_secret_modified": False}} - elif auth.get("password", None).get("value", None): - not_stripped_secrets.append( - ( - path_list + ["authentication", "basic_auth", "password"], - auth["password"]["value"], - ) + ) + auth["password"] = {"attrs": {"is_secret_modified": False}} + elif auth.get("password", None).get("value", None): + not_stripped_secrets.append( + ( + path_list + ["authentication", "basic_auth", "password"], + auth["password"]["value"], ) + ) - def strip_all_secret_variables(path_list, obj): - strip_entity_secret_variables(path_list, obj, context=path_list[0]) - strip_action_secret_variables(path_list, obj) - strip_runbook_secret_variables(path_list, obj) - strip_authentication_secret_variables(path_list, obj, context=path_list[0]) - for object_list in object_lists: - for obj_idx, obj in enumerate(resources.get(object_list, []) or []): - strip_all_secret_variables([object_list, obj_idx], obj) +def strip_all_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, +): + strip_entity_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context=path_list[0], + ) + strip_action_secret_variables( + path_list, obj, decompiled_secrets, secret_variables, not_stripped_secrets + ) + strip_runbook_secret_variables( + path_list, obj, decompiled_secrets, secret_variables, not_stripped_secrets + ) + strip_authentication_secret_variables( + path_list, + obj, + decompiled_secrets, + secret_variables, + not_stripped_secrets, + context=path_list[0], + ) - # Currently, deployment actions and variables are unsupported. - # Uncomment the following lines if and when the API does support them. - # if object_list == "app_profile_list": - # for dep_idx, dep in enumerate(obj["deployment_create_list"]): - # strip_all_secret_variables( - # [object_list, obj_idx, "deployment_create_list", dep_idx], - # dep, - # ) - for obj in objects: - strip_all_secret_variables([obj], resources.get(obj, {})) +def strip_credentials(resources, decompiled_secrets, secret_map): + creds = resources.get("credential_definition_list", []) or [] + filtered_decompiled_secret_credentials = get_secrets_from_context( + decompiled_secrets, "credential_definition_list" + ) + + for cred in creds: + name = cred["name"] + value = cred["secret"]["value"] + + if is_secret_modified(filtered_decompiled_secret_credentials, name, value): + secret_map[name] = cred.pop("secret", {}) + # Explicitly set defaults so that secret is not created at server + # TODO - Fix bug in server: {} != None + cred["secret"] = { + "attrs": {"is_secret_modified": False, "secret_reference": None} + } # Handling vmware secrets @@ -920,3 +1090,22 @@ def add_patch_config_tasks( idx += 1 profile_idx += 1 + + +def strip_uuids(upload_payload): + """Strip UUIDs from upload payload""" + + if isinstance(upload_payload, dict): + key_list = list(upload_payload.keys()) + for key in key_list: + if key == "uuid": + del upload_payload[key] + + # elif key == "icon_reference": + # continue + + else: + strip_uuids(upload_payload[key]) + elif isinstance(upload_payload, list): + for item in upload_payload: + strip_uuids(item) diff --git a/calm/dsl/builtins/__init__.py b/calm/dsl/builtins/__init__.py index 946613355..601c517ab 100644 --- a/calm/dsl/builtins/__init__.py +++ b/calm/dsl/builtins/__init__.py @@ -11,7 +11,7 @@ from .models.action import action, parallel, ActionType, get_runbook_action from .models.credential import basic_cred, secret_cred, dynamic_cred, CredentialType -from .models.task import Task, CalmTask, TaskType +from .models.task import Task, CalmTask, TaskType, HTTPResponseHandle, StatusHandle from .models.port import Port, port, PortType from .models.service import ( @@ -147,6 +147,19 @@ from .models.gcp_account import GcpAccountData, gcp_account from .models.account_resources import AccountResources from .models.account import Account +from .models.provider_endpoint_schema import ( + ProviderEndpointSchema, + NutanixEndpointSchema, + VmwareEndpointSchema, + GCPEndpointSchema, + AWSEndpointSchema, + AzureEndpointSchema, + NoneEndpointSchema, +) +from .models.provider_test_account import ProviderTestAccount +from .models.cloud_provider_payload import CloudProviderPayload +from .models.cloud_provider import CloudProvider +from .models.resource_type import ResourceType __all__ = [ "Ref", @@ -164,6 +177,8 @@ "Task", "CalmTask", "TaskType", + "HTTPResponseHandle", + "StatusHandle", "action", "ActionType", "get_runbook_action", @@ -297,6 +312,17 @@ "TimeMachine", "Tag", "PostgresDatabaseOutputVariables", + "ProviderEndpointSchema", + "NutanixEndpointSchema", + "VmwareEndpointSchema", + "GCPEndpointSchema", + "AWSEndpointSchema", + "AzureEndpointSchema", + "NoneEndpointSchema", + "CloudProvider", + "CloudProviderPayload", + "ProviderTestAccount", + "ResourceType", "AhvUpdateConfigAttrs", "PatchField", "AppEdit", diff --git a/calm/dsl/builtins/models/account.py b/calm/dsl/builtins/models/account.py index f9d635da7..60ab3e894 100644 --- a/calm/dsl/builtins/models/account.py +++ b/calm/dsl/builtins/models/account.py @@ -1,7 +1,7 @@ from .entity import Entity, EntityType, EntityDict from .validator import PropertyValidator, get_property_validators from .vmware_account import VmwareAccountType -from .custom_provider_account import CustomProviderType +from .custom_provider_account import CustomProviderAccountType from calm.dsl.constants import ACCOUNT, ENTITY from types import MappingProxyType @@ -42,8 +42,13 @@ def pre_validate(cls, vdict, name, value, parent=None): if "resources" in vdict: new_dict = dict(vdict) - if account_type: + if account_type in account_type_map: new_dict["resources"] = (account_type_map[account_type], False) + elif account_type: + new_dict["resources"] = ( + property_validators[ENTITY.OPENAPI_TYPE.CUSTOM_PROVIDER], + False, + ) vdict = MappingProxyType(new_dict) return vdict, value @@ -72,7 +77,7 @@ def compile(cls): if cdict["type"] == "NDB": if "parent_reference" in dir(cdict["data"]): - parent_reference = CustomProviderType.compile(cdict["data"]).pop( + parent_reference = CustomProviderAccountType.compile(cdict["data"]).pop( "parent_reference", {} ) cdict["parent_reference"] = parent_reference diff --git a/calm/dsl/builtins/models/account_resources.py b/calm/dsl/builtins/models/account_resources.py index ba68cd2ec..f8597cd0e 100644 --- a/calm/dsl/builtins/models/account_resources.py +++ b/calm/dsl/builtins/models/account_resources.py @@ -14,11 +14,12 @@ custom_provider_account, credential_provider_account, CalmVariable, + Ref, ) -from calm.dsl.constants import CACHE +from calm.dsl.constants import CACHE, VARIABLE from calm.dsl.store import Cache -from .helper.common import get_provider +from calm.dsl.builtins.models.helper.common import is_not_right_ref from .utils import is_compile_secrets from calm.dsl.log import get_logging_handle @@ -160,12 +161,23 @@ def __new__(cls, vault_uri, vault_token, resource_config, **kwargs): ) class CustomProvider: - def __new__(cls, provider, parent, variable_dict): + def __new__(cls, provider, variable_dict, parent=None): - variable_list_values = variable_dict + if is_not_right_ref(provider, Ref.Provider): + LOG.error("Provider should be instance of Ref.Provider") + sys.exit("Provider should be instance of Ref.Provider") - provider_auth_schema_list = deepcopy( - provider["spec"]["resources"]["auth_schema_list"] + provider_cache = provider.compile() + + # provider_auth_schema_list = auth_schema_variables + endpoint_schema_variables + variables + provider_auth_schema_list = deepcopy(provider_cache["auth_schema_list"]) + provider_auth_schema_list.extend( + deepcopy( + provider_cache.get("endpoint_schema", {}).get("variable_list", []) + ) + ) + provider_auth_schema_list.extend( + deepcopy(provider_cache.get("variable_list", [])) ) # check if all the variables provided are present in provider's auth_schema_list @@ -174,40 +186,56 @@ def __new__(cls, provider, parent, variable_dict): for auth_var in provider_auth_schema_list ] + provider_auth_schema_variables.extend( + [auth_var["name"] for auth_var in provider_auth_schema_list] + ) + for auth_var in variable_dict: if auth_var not in provider_auth_schema_variables: - LOG.error("{} is not a valid variable") + LOG.error("{} is not a valid variable".format(auth_var)) sys.exit("Invalid variable provided") - for auth_schema in provider_auth_schema_list: - auth_schema["uuid"] = str(uuid.uuid4()) - auth_schema.pop("state") - auth_schema["message_list"] = [] + for auth_schema_var in provider_auth_schema_list: + auth_schema_var.pop("state", None) + auth_schema_var.pop("message_list", None) + auth_schema_var["uuid"] = str(uuid.uuid4()) + + if ( + auth_schema_var.get("options", {}).get("type") + == VARIABLE.OPTIONS.TYPE.HTTP + ): + header_vars = auth_schema_var["options"]["attrs"].get("headers", []) + for header_var in header_vars: + header_var["uuid"] = str(uuid.uuid4()) - label_dict_key = auth_schema["label"].lower().replace(" ", "_") + var_name = auth_schema_var["name"] + var_type = auth_schema_var["type"] + var_label = auth_schema_var["label"] + label_dict_key = var_label.lower().replace(" ", "_") - if not label_dict_key: + if not label_dict_key and not var_name: continue - if (label_dict_key not in variable_list_values) and ( - auth_schema["is_mandatory"] - ): - LOG.error("{} is a mandatory variable".format(label_dict_key)) + if not is_compile_secrets() and var_type in VARIABLE.SECRET_TYPES: + auth_schema_var["value"] = "" + elif var_name in variable_dict: + auth_schema_var["value"] = variable_dict[var_name] + elif label_dict_key in variable_dict: + auth_schema_var["value"] = variable_dict[label_dict_key] + elif auth_schema_var["is_mandatory"]: + LOG.error( + "{} is a mandatory variable".format(var_label or var_name) + ) sys.exit("Mandatory variable not provided") - auth_schema["value"] = ( - "" - if auth_schema["type"] == "SECRET" and not is_compile_secrets() - else variable_list_values[label_dict_key] - ) - - if auth_schema["type"] == "SECRET": - auth_schema.pop("attrs") - auth_schema["attrs"] = {"is_secret_modified": True} + if var_type in VARIABLE.SECRET_TYPES: + auth_schema_var.pop("attrs") + auth_schema_var["attrs"] = {"is_secret_modified": True} provider_reference = { "kind": "provider", - "uuid": provider["metadata"]["uuid"], + "uuid": provider_cache["uuid"], + "name": provider_cache["name"], } return custom_provider_account( @@ -227,7 +255,7 @@ def __new__(cls, parent, variable_dict): LOG.error("Parent should be of type 'AHV'") sys.exit("Invalid parent value") - ndb_provider = get_provider(name="NDB") + ndb_provider = Ref.Provider(name="NDB") return AccountResources.CustomProvider( provider=ndb_provider, parent=parent, variable_dict=variable_dict diff --git a/calm/dsl/builtins/models/action.py b/calm/dsl/builtins/models/action.py index 4573e1eb5..ac9132782 100644 --- a/calm/dsl/builtins/models/action.py +++ b/calm/dsl/builtins/models/action.py @@ -5,7 +5,12 @@ from .validator import PropertyValidator from .task import create_call_rb, _get_target_ref from .runbook import runbook, runbook_create -from calm.dsl.constants import SUBSTRATE +from calm.dsl.constants import ( + SUBSTRATE, + ACTION, + RESOURCE_TYPE, + CLOUD_PROVIDER as PROVIDER, +) from calm.dsl.log import get_logging_handle LOG = get_logging_handle(__name__) @@ -154,7 +159,18 @@ def __get__(self, instance, cls): # System action names action_name = self.action_name - ACTION_TYPE = "user" + ACTION_TYPE = ACTION.TYPE.USER + subclasses = EntityType.get_entity_types() + if isinstance(cls, subclasses[PROVIDER.ENTITY_NAME]): + ACTION_TYPE = ACTION.TYPE.PROVIDER # Default for provider actions + elif isinstance(cls, subclasses[RESOURCE_TYPE.ENTITY_NAME]): + ACTION_TYPE = ( + RESOURCE_TYPE.ACTION_TYPE.GENERIC + ) # Default for resource_type actions + + sig = inspect.signature(self.user_func) + if sig.parameters.get("type", None): + ACTION_TYPE = sig.parameters["type"].default func_name = self.user_func.__name__.lower() if func_name.startswith("__") and func_name.endswith("__"): SYSTEM = getattr(cls, "ALLOWED_SYSTEM_ACTIONS", {}) @@ -172,7 +188,6 @@ def __get__(self, instance, cls): else: # `name` argument is only supported in non-system actions - sig = inspect.signature(self.user_func) gui_display_name = sig.parameters.get("name", None) if gui_display_name and gui_display_name.default != action_name: action_name = gui_display_name.default diff --git a/calm/dsl/builtins/models/calm_ref.py b/calm/dsl/builtins/models/calm_ref.py index d78b16a23..6e016f04d 100644 --- a/calm/dsl/builtins/models/calm_ref.py +++ b/calm/dsl/builtins/models/calm_ref.py @@ -761,6 +761,30 @@ def compile(cls, name=None, **kwargs): "uuid": cdict["uuid"], } + class Provider: + def __new__(cls, name, **kwargs): + kwargs["__ref_cls__"] = cls + kwargs["name"] = name + return _calm_ref(**kwargs) + + def compile(cls, name, **kwargs): + + if name: + res = Cache.get_entity_data( + entity_type=CACHE.ENTITY.PROVIDER, name=name + ) + if not res: + LOG.error( + "Provider with name {} not found. Run 'calm update cache' if data is stale or double check the provider name".format( + name + ) + ) + sys.exit(-1) + return res + + LOG.error("Provider name not passed, please pass provider name") + sys.exit(-1) + class Resource_Type: __ref_kind__ = CACHE.ENTITY.RESOURCE_TYPE diff --git a/calm/dsl/builtins/models/cloud_provider.py b/calm/dsl/builtins/models/cloud_provider.py new file mode 100644 index 000000000..7642ccc3a --- /dev/null +++ b/calm/dsl/builtins/models/cloud_provider.py @@ -0,0 +1,83 @@ +import json +import sys + +from calm.dsl.log import get_logging_handle +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER, ACTION, CREDENTIAL + +from .resource_type import ResourceType +from .entity import EntityType, Entity +from .validator import PropertyValidator + +LOG = get_logging_handle(__name__) + + +class CloudProviderType(EntityType): + __schema_name__ = "CloudProvider" + __openapi_type__ = "cloud_provider" + + def compile(cls): + cdict = super().compile() + if len(cdict.get("auth_schema_list", [])) == 0: + error = "Auth_schema is required for provider" + LOG.error(error) + sys.exit(error) + + if cdict.get("type", PROVIDER.TYPE.CUSTOM) != PROVIDER.TYPE.CUSTOM: + error = "provider type is not '%s'" % (PROVIDER.TYPE.CUSTOM) + LOG.error(error) + sys.exit(error) + + action_list = cdict.get("action_list", []) + if len(action_list) > 1: + error = "Atmost one verify action can be added for provider" + LOG.error(error) + sys.exit(error) + elif action_list: + action = action_list[0] + if str(action) != PROVIDER.VERIFY_ACTION_NAME: + error = "Action should be named '%s'" % (PROVIDER.VERIFY_ACTION_NAME) + LOG.error(error) + sys.exit(error) + + if action.type != ACTION.TYPE.PROVIDER: + error = "Provider Action should be of type '%s'" % ( + ACTION.TYPE.PROVIDER + ) + LOG.error(error) + sys.exit(error) + + infra_type = cdict.get("infra_type", None) + if infra_type is not None and infra_type not in PROVIDER.INFRA_TYPES: + error = "Infra type should be one of %s" % ( + json.dumps(PROVIDER.INFRA_TYPES) + ) + LOG.error(error) + sys.exit(error) + + credentials = cdict.get("credential_definition_list", []) + for cred in credentials: + if cred.cred_class == CREDENTIAL.CRED_CLASS.DYNAMIC: + error = "Provider cannot have dynamic credentials" + LOG.error(error) + sys.exit(error) + + # NOTE: Validation of test_account_variables >= (auth_schema_list + endpoint_schema.variables) + # is not being done here because in some cases, endpoint variables are generated on backend & + # are not accessible here. Hence, the validation will be done in backend & errors if any, will + # be displayed here + + return cdict + + +class CloudProviderTypeValidator(PropertyValidator, openapi_type="cloud_provider"): + __default__ = None + __kind__ = CloudProviderType + + +def cloud_provider(**kwargs): + name = kwargs.pop("name", None) + bases = (Entity,) + return CloudProviderType(name, bases, kwargs) + + +CloudProvider = cloud_provider() diff --git a/calm/dsl/builtins/models/cloud_provider_payload.py b/calm/dsl/builtins/models/cloud_provider_payload.py new file mode 100644 index 000000000..007358c59 --- /dev/null +++ b/calm/dsl/builtins/models/cloud_provider_payload.py @@ -0,0 +1,62 @@ +import uuid + +from .entity import Entity, EntityType +from .validator import PropertyValidator +from calm.dsl.builtins.models.cloud_provider import CloudProvider +from calm.dsl.log import get_logging_handle + + +LOG = get_logging_handle(__name__) + + +class CloudProviderPayloadType(EntityType): + """Metaclass for user provider""" + + __schema_name__ = "CloudProviderPayload" + __openapi_type__ = "cloud_provider_payload" + + +class CloudProviderPayloadValidator( + PropertyValidator, openapi_type="cloud_provider_payload" +): + __default__ = None + __kind__ = CloudProviderPayloadType + + +def _cloud_provider_payload(**kwargs): + name = kwargs.pop("name", None) + bases = (Entity,) + return CloudProviderPayloadType(name, bases, kwargs) + + +CloudProviderPayload = _cloud_provider_payload() + + +def create_cloud_provider_payload(CloudProviderClass): + err = {"error": "", "code": -1} + + if CloudProviderClass is None: + err["error"] = "Given provider is empty." + return None, err + + if not isinstance(CloudProviderClass, type(CloudProvider)): + err["error"] = "Given provider is not of type User Provider" + LOG.info("Given provider is not of type User Provider") + return None, err + + spec = { + "name": CloudProviderClass.__name__, + "resources": CloudProviderClass, + "description": CloudProviderClass.__doc__ or "", + } + metadata = { + "kind": "provider", + "name": CloudProviderClass.__name__, + "uuid": str(uuid.uuid4()), + } + + CloudProviderPayloadClass = _cloud_provider_payload() + CloudProviderPayloadClass.metadata = metadata + CloudProviderPayloadClass.spec = spec + + return CloudProviderPayloadClass diff --git a/calm/dsl/builtins/models/custom_provider_account.py b/calm/dsl/builtins/models/custom_provider_account.py index 7ad619d7a..d3d1de9a5 100644 --- a/calm/dsl/builtins/models/custom_provider_account.py +++ b/calm/dsl/builtins/models/custom_provider_account.py @@ -9,10 +9,8 @@ LOG = get_logging_handle(__name__) -# CustomProvider - -class CustomProviderType(EntityType): +class CustomProviderAccountType(EntityType): __schema_name__ = "CustomProviderAccountResources" __openapi_type__ = ENTITY.OPENAPI_TYPE.CUSTOM_PROVIDER @@ -30,13 +28,13 @@ class CustomProviderAccountResourcesTypeValidator( PropertyValidator, openapi_type=ENTITY.OPENAPI_TYPE.CUSTOM_PROVIDER ): __default__ = None - __kind__ = CustomProviderType + __kind__ = CustomProviderAccountType def custom_provider_account(**kwargs): name = kwargs.pop("name", None) bases = (Entity,) - return CustomProviderType(name, bases, kwargs) + return CustomProviderAccountType(name, bases, kwargs) CustomProviderAccountResources = custom_provider_account() diff --git a/calm/dsl/builtins/models/helper/common.py b/calm/dsl/builtins/models/helper/common.py index c70f24271..2286ecb79 100644 --- a/calm/dsl/builtins/models/helper/common.py +++ b/calm/dsl/builtins/models/helper/common.py @@ -480,20 +480,7 @@ def get_provider_uuid(name): LOG.info("{} found ".format(name)) provider = entities[0] else: - LOG.error("No provider found with name {} found".format(name)) - sys.exit("No provider found with name {} found".format(name)) + LOG.error("No provider found with name {}".format(name)) + sys.exit("No provider found with name {}".format(name)) return provider["metadata"]["uuid"] - - -def get_provider(name): - """returns provider get call data""" - - client = get_api_client() - provider_uuid = get_provider_uuid(name=name) - res, err = client.provider.read(provider_uuid) - if err: - LOG.exception("[{}] - {}".format(err["code"], err["error"])) - sys.exit("[{}] - {}".format(err["code"], err["error"])) - - return res.json() diff --git a/calm/dsl/builtins/models/job.py b/calm/dsl/builtins/models/job.py index 9b96118b6..5a17a86be 100644 --- a/calm/dsl/builtins/models/job.py +++ b/calm/dsl/builtins/models/job.py @@ -282,7 +282,7 @@ def exec_app_action( # Get app uuid from name client = get_api_client() - app = apps._get_app(client, app_name, all=True) + app = apps._get_app(client, app_name, all=False) res, err = client.application.read(app["metadata"]["uuid"]) if err: LOG.error("[{}] - {}".format(err["code"], err["error"])) diff --git a/calm/dsl/builtins/models/node_visitor.py b/calm/dsl/builtins/models/node_visitor.py index 717ce9a80..04d5ecea8 100644 --- a/calm/dsl/builtins/models/node_visitor.py +++ b/calm/dsl/builtins/models/node_visitor.py @@ -20,7 +20,7 @@ def handle_meta_create(node, func_globals, prefix=None): node_visitor.visit(node) except Exception as ex: raise ex - tasks, variables, task_list = node_visitor.get_objects() + tasks, variables, task_list, _ = node_visitor.get_objects() child_tasks = [] for child_task in task_list: @@ -50,19 +50,21 @@ def __init__( self.task_list = [] self.all_tasks = [] self.variables = {} + self.outputs = [] self.target = target or None self._globals = func_globals or {}.copy() self.is_branch_present = is_branch_present - # flag to check if this runbook is in context of RaaS, as decision, while, parallel tasks are supported only in RaaS + # flag to check if this runbook is in context of RaaS or custom providers, + # as decision, while, parallel tasks are supported only in RaaS & custom provider actions self.is_runbook = is_runbook # flag to check if tasks are in context of metatask self.is_metatask = is_metatask_context def get_objects(self): - return self.all_tasks, self.variables, self.task_list + return self.all_tasks, self.variables, self.task_list, self.outputs def visit_Call(self, node, return_task=False): sub_node = node.func @@ -81,18 +83,41 @@ def visit_Call(self, node, return_task=False): return return self.generic_visit(node) + def is_calm_or_runbook_variable(self, sub_node): + while not isinstance(sub_node, ast.Name): + sub_node = sub_node.value + py_object = eval(compile(ast.Expression(sub_node), "", "eval"), self._globals) + return py_object == CalmVariable or RunbookVariable + def visit_Assign(self, node): + if isinstance(node.value, ast.List): # Parse & set outputs + lhs = node.targets[0].id if node.targets else None + if lhs == "outputs": + self.outputs = [] + for element in node.value.elts: + sub_node = element.func + + if self.is_calm_or_runbook_variable(sub_node): + variable = eval( + compile(ast.Expression(element), "", "eval"), self._globals + ) + if ( + variable.type != "LOCAL" + or variable.value_type != "STRING" + or variable.options + ): + raise ValueError( + "output variable {} can only be of type Simple.string".format( + variable.name + ) + ) + if isinstance(variable, VariableType): + self.outputs.append(variable) + if not isinstance(node.value, ast.Call): return self.generic_visit(node) sub_node = node.value.func - while not isinstance(sub_node, ast.Name): - sub_node = sub_node.value - if ( - eval(compile(ast.Expression(sub_node), "", "eval"), self._globals) - == CalmVariable - or eval(compile(ast.Expression(sub_node), "", "eval"), self._globals) - == RunbookVariable - ): + if self.is_calm_or_runbook_variable(sub_node): if len(node.targets) > 1: raise ValueError( "not enough values to unpack (expected {}, got 1)".format( @@ -216,7 +241,7 @@ def visit_With(self, node): _node_visitor.visit(statementBody) except Exception as ex: raise ex - tasks, variables, task_list = _node_visitor.get_objects() + tasks, variables, task_list, outputs = _node_visitor.get_objects() if len(task_list) == 0: raise ValueError( "Atleast one task is required under parallel branch" @@ -224,6 +249,7 @@ def visit_With(self, node): parallel_tasks.append(task_list) self.all_tasks.extend(tasks) self.variables.update(variables) + self.outputs.extend(outputs) else: raise ValueError( "Only with branch() contexts are supported under parallel context." diff --git a/calm/dsl/builtins/models/provider_endpoint_schema.py b/calm/dsl/builtins/models/provider_endpoint_schema.py new file mode 100644 index 000000000..8672294df --- /dev/null +++ b/calm/dsl/builtins/models/provider_endpoint_schema.py @@ -0,0 +1,74 @@ +import json +import sys + +from calm.dsl.log import get_logging_handle +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER + +from .entity import EntityType, Entity +from .validator import PropertyValidator + +LOG = get_logging_handle(__name__) + + +# Provider Endpoint Schema +class ProviderEndpointSchemaType(EntityType): + __schema_name__ = "ProviderEndpointSchema" + __openapi_type__ = "provider_endpoint_schema" + + def compile(cls): + cdict = super().compile() + + schema_type = cdict.get("type", PROVIDER.ENDPOINT_KIND.NONE) + if schema_type not in PROVIDER.ENDPOINT_KINDS: + error = "Endpoint Schema type should be one of %s" % ( + json.dumps(PROVIDER.ENDPOINT_KINDS) + ) + LOG.error(error) + sys.exit(error) + + if schema_type != PROVIDER.ENDPOINT_KIND.CUSTOM: + variable_list = cdict.pop("variable_list", None) + if variable_list: + error = "Cannot specify Variables for Endpoint Schema of type '%s'" % ( + schema_type + ) + LOG.error(error) + sys.exit(error) + else: + variable_list = cdict.get("variable_list") + if not variable_list: + error = "No variables specified for Endpoint Schema of type '%s'. Note: " % ( + schema_type + ) + "If Endpoint Schema is not required, either remove endpoint_schema or use type '%s'" % ( + PROVIDER.ENDPOINT_KIND.NONE + ) + LOG.error(error) + sys.exit(error) + + return cdict + + +class ProviderEndpointSchemaValidator( + PropertyValidator, openapi_type="provider_endpoint_schema" +): + __default__ = None + __kind__ = ProviderEndpointSchemaType + + +def provider_endpoint_schema(**kwargs): + name = kwargs.pop("name", None) + bases = (Entity,) + return ProviderEndpointSchemaType(name, bases, kwargs) + + +ProviderEndpointSchema = provider_endpoint_schema +NoneEndpointSchema = lambda: ProviderEndpointSchema(type=PROVIDER.ENDPOINT_KIND.NONE) +NutanixEndpointSchema = lambda: ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.NUTANIX_PC +) +VmwareEndpointSchema = lambda: ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.VMWARE +) +GCPEndpointSchema = lambda: ProviderEndpointSchema(type=PROVIDER.ENDPOINT_KIND.GCP) +AWSEndpointSchema = lambda: ProviderEndpointSchema(type=PROVIDER.ENDPOINT_KIND.AWS) +AzureEndpointSchema = lambda: ProviderEndpointSchema(type=PROVIDER.ENDPOINT_KIND.AZURE) diff --git a/calm/dsl/builtins/models/provider_test_account.py b/calm/dsl/builtins/models/provider_test_account.py new file mode 100644 index 000000000..3c3531a7e --- /dev/null +++ b/calm/dsl/builtins/models/provider_test_account.py @@ -0,0 +1,37 @@ +from .entity import EntityType, Entity +from .validator import PropertyValidator + + +class ProviderTestAccountType(EntityType): + __schema_name__ = "ProviderTestAccount" + __openapi_type__ = "provider_test_account" + + def compile(cls): + cdict = super().compile() + cdict["data"] = {"variable_list": cdict.pop("variable_list", [])} + return cdict + + @classmethod + def pre_decompile(mcls, cdict, context, prefix=""): + """Hook to modify cdict before decompile""" + + cdict["variable_list"] = cdict.get("data", {}).get("variable_list", []) + return super(ProviderTestAccountType, mcls).pre_decompile( + cdict, context, prefix=prefix + ) + + +class ProviderTestAccountValidator( + PropertyValidator, openapi_type="provider_test_account" +): + __default__ = None + __kind__ = ProviderTestAccountType + + +def provider_test_account(**kwargs): + name = kwargs.pop("name", None) + bases = (Entity,) + return ProviderTestAccountType(name, bases, kwargs) + + +ProviderTestAccount = provider_test_account diff --git a/calm/dsl/builtins/models/resource_type.py b/calm/dsl/builtins/models/resource_type.py new file mode 100644 index 000000000..6700e2f9b --- /dev/null +++ b/calm/dsl/builtins/models/resource_type.py @@ -0,0 +1,67 @@ +import json +import sys + +from calm.dsl.log import get_logging_handle +from calm.dsl.constants import RESOURCE_TYPE, ACTION + +from .entity import EntityType, Entity +from .validator import PropertyValidator + +LOG = get_logging_handle(__name__) + + +class ResourceTypeEntity(EntityType): + __schema_name__ = "ResourceType" + __openapi_type__ = "resource_type" + + def compile(cls): + cdict = super().compile() + cdict["type"] = RESOURCE_TYPE.TYPE.USER + + action_list = cdict["action_list"] + for action in action_list: + if action.type not in RESOURCE_TYPE.ACTION_TYPES: + error = "ResourceType Action's type should be one of %s" % ( + json.dumps(RESOURCE_TYPE.ACTION_TYPES) + ) + LOG.error(error) + sys.exit(error) + + icon_name = cdict.pop("icon_name", None) + if icon_name: + cdict["icon_name"] = icon_name + icon_file = cdict.pop("icon_file", None) + if icon_file: + cdict["icon_file"] = icon_file + + resource_kind = cdict.pop("resource_kind", None) + if resource_kind: + cdict["tags"] = [resource_kind] + return cdict + + @classmethod + def pre_decompile(mcls, cdict, context, prefix=""): + """Hook to modify cdict before decompile""" + + tags = cdict.pop("tags", []) + if tags: + cdict["resource_kind"] = tags[0] + + cdict["icon_name"] = cdict.pop("icon_reference", {}).get("name", "") + return super(ResourceTypeEntity, mcls).pre_decompile( + cdict, context, prefix=prefix + ) + + +class ResourceTypeValidator(PropertyValidator, openapi_type="resource_type"): + __default__ = None + __kind__ = ResourceTypeEntity + + +def _resource_type(**kwargs): + name = kwargs.pop("name", None) + bases = (Entity,) + return ResourceTypeEntity(name, bases, kwargs) + + +ResourceType = _resource_type() diff --git a/calm/dsl/builtins/models/runbook.py b/calm/dsl/builtins/models/runbook.py index c7d19670f..aeb4caed0 100644 --- a/calm/dsl/builtins/models/runbook.py +++ b/calm/dsl/builtins/models/runbook.py @@ -1,6 +1,7 @@ import ast import sys import inspect +from distutils.version import LooseVersion as LV from .ref import ref from .task import dag @@ -12,6 +13,8 @@ from .validator import PropertyValidator from .node_visitor import GetCallNodes from calm.dsl.log import get_logging_handle +from calm.dsl.store.version import Version +from calm.dsl.constants import RESOURCE_TYPE, CLOUD_PROVIDER as PROVIDER from _ast import AST @@ -135,10 +138,16 @@ def __get__(self, instance=None, cls=None): LOG.exception(ex) sys.exit(-1) + subclasses = EntityType.get_entity_types() + is_runbook = ( + self.__class__ == runbook + or isinstance(cls, subclasses[PROVIDER.ENTITY_NAME]) + or isinstance(cls, subclasses[RESOURCE_TYPE.ENTITY_NAME]) + ) node_visitor = GetCallNodes( func_globals, target=self.task_target, - is_runbook=True if self.__class__ == runbook else False, + is_runbook=is_runbook, is_branch_present=self.is_branch_present, ) try: @@ -147,7 +156,7 @@ def __get__(self, instance=None, cls=None): LOG.exception(ex) sys.exit(-1) - tasks, variables, task_list = node_visitor.get_objects() + tasks, variables, task_list, outputs = node_visitor.get_objects() edges = [] child_tasks = [] @@ -201,7 +210,7 @@ def create_edges(_task_list, from_task=None): # First create the dag self.user_dag = dag( name=dag_name, - child_tasks=child_tasks if self.__class__ == runbook else tasks, + child_tasks=child_tasks if is_runbook else tasks, edges=edges, target=self.task_target, ) @@ -212,6 +221,10 @@ def create_edges(_task_list, from_task=None): self.user_runbook.tasks = [self.user_dag] + tasks self.user_runbook.variables = [variable for variable in variables.values()] + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) >= LV("4.0.0"): + self.user_runbook.outputs = outputs + # Finally create the runbook service, only for runbook class not action if self.__class__ == runbook: args = dict() diff --git a/calm/dsl/builtins/models/schemas/cloud_provider.yaml.jinja2 b/calm/dsl/builtins/models/schemas/cloud_provider.yaml.jinja2 new file mode 100644 index 000000000..343ddf5d6 --- /dev/null +++ b/calm/dsl/builtins/models/schemas/cloud_provider.yaml.jinja2 @@ -0,0 +1,61 @@ +{% macro CloudProvider() -%} + +title: CloudProvider +type: object +x-calm-dsl-type: cloud_provider +properties: + type: + type: string + description: Type of the provider + enum: [CUSTOM] + default: CUSTOM + infra_type: + type: string + description: Infra type of the provider i.e..,. onprem or cloud + enum: [on_prem, cloud] + default: cloud + auth_schema_list: + type: array + description: Auth Schema of the provider + items: + $ref: '#/components/schemas/Variable' + x-calm-dsl-display-name: auth_schema_variables + endpoint_schema: + $ref: '#/components/schemas/ProviderEndpointSchema' + variable_list: + type: array + description: Variables of the provider + items: + $ref: '#/components/schemas/Variable' + x-calm-dsl-display-name: variables + resource_type_list: + type: array + description: ResourceTypes of the provider + items: + $ref: '#/components/schemas/ResourceType' + x-calm-dsl-display-name: resource_types + test_account: + type: object + description: Spec of test account to be associated with this provider + $ref: '#/components/schemas/ProviderTestAccount' + action_list: + type: array + fixedSize: 1 + description: Action list ([Verify]) of the provider + items: + $ref: '#/components/schemas/Action' + x-calm-dsl-display-name: actions + credential_definition_list: + description: Credential definitions for provider. + type: array + items: + $ref: '#/components/schemas/Credential' + x-calm-dsl-display-name: credentials + +{%- endmacro %} +{% macro CloudProviderSchema() -%} + +CloudProvider: + {{ CloudProvider()|indent(2) }} + +{%- endmacro %} diff --git a/calm/dsl/builtins/models/schemas/cloud_provider_payload.yaml.jinja2 b/calm/dsl/builtins/models/schemas/cloud_provider_payload.yaml.jinja2 new file mode 100644 index 000000000..b6b8114e2 --- /dev/null +++ b/calm/dsl/builtins/models/schemas/cloud_provider_payload.yaml.jinja2 @@ -0,0 +1,42 @@ +{% macro CloudProviderPayload() -%} + +title: CloudProviderPayload +type: object +x-calm-dsl-type: cloud_provider_payload +properties: + spec: + type: object + x-calm-dsl-type: dict + properties: + name: + type: string + description: Provider name + description: + type: string + description: Provider description + resources: + type: object + properties: + $ref: '#/components/schemas/CloudProvider' + + metadata: + type: object + x-calm-dsl-type: dict + properties: + name: + type: string + description: Provider name + kind: + type: string + description: Provider kind + uuid: + type: string + description: Provider uuid + +{%- endmacro %} +{% macro CloudProviderPayloadSchema() -%} + +CloudProviderPayload: + {{ CloudProviderPayload()|indent(2) }} + +{%- endmacro %} diff --git a/calm/dsl/builtins/models/schemas/custom_provider_account.yaml.jinja2 b/calm/dsl/builtins/models/schemas/custom_provider_account.yaml.jinja2 index 1ee43de29..52a15e34b 100644 --- a/calm/dsl/builtins/models/schemas/custom_provider_account.yaml.jinja2 +++ b/calm/dsl/builtins/models/schemas/custom_provider_account.yaml.jinja2 @@ -12,6 +12,8 @@ properties: type: string uuid: type: string + name: + type: string parent_reference: type: object x-calm-dsl-type: app_calm_ref diff --git a/calm/dsl/builtins/models/schemas/main.yaml.jinja2 b/calm/dsl/builtins/models/schemas/main.yaml.jinja2 index 037e6c2a8..ff7a4eaa5 100644 --- a/calm/dsl/builtins/models/schemas/main.yaml.jinja2 +++ b/calm/dsl/builtins/models/schemas/main.yaml.jinja2 @@ -63,6 +63,11 @@ {% import "k8s_karbon_account.yaml.jinja2" as k8s_karbon_account %} {% import "vmware_account.yaml.jinja2" as vmware_account %} {% import "gcp_account.yaml.jinja2" as gcp_account %} +{% import "provider_endpoint_schema.yaml.jinja2" as provider_endpoint_schema %} +{% import "provider_test_account.yaml.jinja2" as provider_test_account %} +{% import "cloud_provider_payload.yaml.jinja2" as cloud_provider_payload %} +{% import "cloud_provider.yaml.jinja2" as cloud_provider %} +{% import "resource_type.yaml.jinja2" as resource_type %} {% macro Schemas() -%} {{ config_attrs.AhvDiskRulesetSchema() }} @@ -135,6 +140,12 @@ {{ k8s_karbon_account.K8sKarbonAccountSchema() }} {{ vmware_account.VmwareAccountSchema() }} {{ gcp_account.GcpAccountSchema() }} +{{ provider_endpoint_schema.ProviderEndpointSchemaDefinition() }} +{{ provider_test_account.ProviderTestAccountDefinition() }} +{{ resource_type.ResourceTypeSchema() }} +{{ cloud_provider_payload.CloudProviderPayloadSchema() }} +{{ cloud_provider.CloudProviderSchema() }} + {%- endmacro %} diff --git a/calm/dsl/builtins/models/schemas/provider_endpoint_schema.yaml.jinja2 b/calm/dsl/builtins/models/schemas/provider_endpoint_schema.yaml.jinja2 new file mode 100644 index 000000000..116fc2062 --- /dev/null +++ b/calm/dsl/builtins/models/schemas/provider_endpoint_schema.yaml.jinja2 @@ -0,0 +1,30 @@ +{% import "ref.yaml.jinja2" as ref %} + +{% macro ProviderEndpointSchema() -%} + +title: Provider Endpoint Schema +description: Endpoint Schema of the provider +x-calm-dsl-type: provider_endpoint_schema +type: object +properties: + type: + type: string + description: Type of endpoint schema + enum: [NUTANIX_PC, VMWARE, GCP, AWS, AZURE, CUSTOM, NONE] + default: CUSTOM + variable_list: + type: array + description: List of variables + items: + $ref: '#/components/schemas/Variable' + x-calm-dsl-display-name: variables + +{%- endmacro %} + + +{% macro ProviderEndpointSchemaDefinition() -%} + +ProviderEndpointSchema: + {{ ProviderEndpointSchema()|indent(4) }} + +{%- endmacro %} diff --git a/calm/dsl/builtins/models/schemas/provider_test_account.yaml.jinja2 b/calm/dsl/builtins/models/schemas/provider_test_account.yaml.jinja2 new file mode 100644 index 000000000..8d4224168 --- /dev/null +++ b/calm/dsl/builtins/models/schemas/provider_test_account.yaml.jinja2 @@ -0,0 +1,31 @@ +{% import "ref.yaml.jinja2" as ref %} + +{% macro ProviderTestAccount() -%} + +title: Provider Test Account +description: Account Schema of the provider used for performing test executions +x-calm-dsl-type: provider_test_account +type: object +properties: + name: + type: string + description: "Name of the account. Example: my_test_account" + description: + type: string + description: "Notes of account" + variable_list: + type: array + description: List of variables + items: + $ref: '#/components/schemas/Variable' + x-calm-dsl-display-name: variables + +{%- endmacro %} + + +{% macro ProviderTestAccountDefinition() -%} + +ProviderTestAccount: + {{ ProviderTestAccount()|indent(4) }} + +{%- endmacro %} diff --git a/calm/dsl/builtins/models/schemas/resource_type.yaml.jinja2 b/calm/dsl/builtins/models/schemas/resource_type.yaml.jinja2 new file mode 100644 index 000000000..76d50a535 --- /dev/null +++ b/calm/dsl/builtins/models/schemas/resource_type.yaml.jinja2 @@ -0,0 +1,49 @@ +{% macro ResourceType() -%} + +title: Resource Type +type: object +x-calm-dsl-type: resource_type +properties: + name: + type: string + description: ResourceType name + description: + type: string + description: ResourceType description + schema_list: + type: array + description: Common schema variables of the resource_type + items: + $ref: '#/components/schemas/Variable' + x-calm-dsl-display-name: schemas + variable_list: + type: array + description: Variables of the resource_type + items: + $ref: '#/components/schemas/Variable' + x-calm-dsl-display-name: variables + action_list: + type: array + description: Action list of the resource_type + items: + $ref: '#/components/schemas/Action' + x-calm-dsl-display-name: actions + resource_kind: + type: string + description: Resource Type Kind + icon_file: + type: string + description: Icon File for the resource_type + icon_name: + type: string + description: Icon File for the resource_type + +{%- endmacro %} + + +{% macro ResourceTypeSchema() -%} + +ResourceType: + {{ ResourceType()|indent(2) }} + +{%- endmacro %} diff --git a/calm/dsl/builtins/models/schemas/runbook.yaml.jinja2 b/calm/dsl/builtins/models/schemas/runbook.yaml.jinja2 index 38ddf785f..4719298f0 100644 --- a/calm/dsl/builtins/models/schemas/runbook.yaml.jinja2 +++ b/calm/dsl/builtins/models/schemas/runbook.yaml.jinja2 @@ -19,6 +19,12 @@ properties: type: array items: $ref: '#/components/schemas/Variable' + output_variable_list: + x-calm-dsl-display-name: outputs + x-calm-dsl-min-version: 4.0.0 + type: array + items: + $ref: '#/components/schemas/Variable' {%- endmacro %} diff --git a/calm/dsl/builtins/models/schemas/task.yaml.jinja2 b/calm/dsl/builtins/models/schemas/task.yaml.jinja2 index 3319f6432..b8ea3edc1 100644 --- a/calm/dsl/builtins/models/schemas/task.yaml.jinja2 +++ b/calm/dsl/builtins/models/schemas/task.yaml.jinja2 @@ -18,6 +18,12 @@ properties: type: object additionalProperties: true x-calm-dsl-type: dict + status_map_list: + type: array + x-calm-dsl-min-version: 3.9.0 + items: + type: object + x-calm-dsl-type: dict child_tasks_local_reference_list: type: array items: diff --git a/calm/dsl/builtins/models/schemas/variable.yaml.jinja2 b/calm/dsl/builtins/models/schemas/variable.yaml.jinja2 index ac64dde1e..007f56dfd 100644 --- a/calm/dsl/builtins/models/schemas/variable.yaml.jinja2 +++ b/calm/dsl/builtins/models/schemas/variable.yaml.jinja2 @@ -61,6 +61,11 @@ properties: additionalProperties: true type: object x-calm-dsl-type: dict + exec_target_reference: + x-calm-dsl-min-version: 4.0.0 + additionalProperties: true + type: object + x-calm-dsl-type: dict is_hidden: type: boolean diff --git a/calm/dsl/builtins/models/task.py b/calm/dsl/builtins/models/task.py index 2adfea3bc..f9ec0b1dc 100644 --- a/calm/dsl/builtins/models/task.py +++ b/calm/dsl/builtins/models/task.py @@ -2,7 +2,7 @@ import uuid import os import sys - +from distutils.version import LooseVersion as LV from .entity import EntityType, Entity from .validator import PropertyValidator @@ -13,6 +13,7 @@ from .helper import common as common_helper from .utils import is_compile_secrets +from calm.dsl.store.version import Version from calm.dsl.log import get_logging_handle from calm.dsl.store import Cache from calm.dsl.builtins.models.ndb import ( @@ -27,6 +28,8 @@ LOG = get_logging_handle(__name__) +CALM_VERSION = Version.get_version("Calm") + class Status(enum.Enum): @@ -41,6 +44,89 @@ class Status(enum.Enum): Status.DONT_CARE: "dont_care", } + +def http_response_code_map(status, code_ranges, code): + if code: + if LV(CALM_VERSION) >= LV("3.9.0"): + if code_ranges: + response_code_status_map = { + "status": status, + "code_range_list": code_ranges, + "code": code, + } + else: + response_code_status_map = {"status": status, "code": code} + else: + response_code_status_map = {"status": status, "code": code} + else: + response_code_status_map = {"status": status, "code_range_list": code_ranges} + return response_code_status_map + + +class HTTPResponseHandle: + class TASK_STATUS: + Success = "SUCCESS" + Failure = "FAILURE" + Warning = "WARNING" + + class ResponseCode: + def __new__(cls, status, code_ranges=[], code=None): + return http_response_code_map( + status=status, + code_ranges=code_ranges, + code=code, + ) + + +def task_status_map(result, values): + task_status_map = { + "match_values": values, + "type": StatusHandle.Type.Status, + "result_status": result, + } + return task_status_map + + +def exit_code_map(result, values): + exit_code_status_map = { + "match_values": values, + "type": StatusHandle.Type.ExitCode, + "result_status": result, + } + return exit_code_status_map + + +class StatusHandle: + class Type: + Status = "status" + ExitCode = "exit_code" + + class Mapping: + """ + Exit Code Mapping is allowed for Execute, Set Variable and Decision Task. + Task Status Mapping is not allowed for Execute Escript task. + """ + + exit_code = exit_code_map + task_status = task_status_map + + class Result: + """ + Status can be mapped to only Warning + """ + + Warning = "WARNING" + + class Status: + """ + TaskFailure can be used for all Exec, Decision and Set Variable Tasks except Escript, VM Operations, HTTP Task. + Failure can be used only for Loop Task + """ + + TaskFailure = "TASK_FAILURE" + Failure = "FAILURE" + + # Task @@ -228,6 +314,7 @@ def _exec_create( cred=None, depth=2, tunnel=None, + status_map_list=[], **kwargs, ): if script is not None and filename is not None: @@ -254,6 +341,8 @@ def _exec_create( "type": "EXEC", "attrs": {"script_type": script_type, "script": script}, } + if LV(CALM_VERSION) >= LV("3.9.0"): + params["status_map_list"] = status_map_list if cred is not None: params["attrs"]["login_credential_local_reference"] = _get_target_ref(cred) if target is not None: @@ -264,6 +353,12 @@ def _exec_create( params["attrs"]["tunnel_reference"] = tunnel if "inherit_target" in kwargs: params["inherit_target"] = kwargs.get("inherit_target") + if kwargs.get("ip"): + params["attrs"]["ip"] = kwargs["ip"] + if kwargs.get("port"): + params["attrs"]["port"] = kwargs["port"] + if kwargs.get("connection_protocol"): + params["attrs"]["connection_protocol"] = kwargs["connection_protocol"] return _task_create(**params) @@ -276,6 +371,7 @@ def _decision_create( cred=None, depth=2, tunnel=None, + status_map_list=[], **kwargs, ): if script is not None and filename is not None: @@ -303,8 +399,13 @@ def _decision_create( params = { "name": name, "type": "DECISION", - "attrs": {"script_type": script_type, "script": script}, + "attrs": { + "script_type": script_type, + "script": script, + }, } + if LV(CALM_VERSION) >= LV("3.9.0"): + params["status_map_list"] = status_map_list if cred is not None: params["attrs"]["login_credential_local_reference"] = _get_target_ref(cred) if target is not None: @@ -313,6 +414,12 @@ def _decision_create( params["attrs"]["tunnel_reference"] = tunnel if "inherit_target" in kwargs: params["inherit_target"] = kwargs.get("inherit_target") + if kwargs.get("ip"): + params["attrs"]["ip"] = kwargs["ip"] + if kwargs.get("port"): + params["attrs"]["port"] = kwargs["port"] + if kwargs.get("connection_protocol"): + params["attrs"]["connection_protocol"] = kwargs["connection_protocol"] return _task_create(**params) @@ -380,7 +487,7 @@ def parallel_task(name=None, child_tasks=[], attrs={}): return _task_create(**kwargs) -def while_loop(name=None, child_tasks=[], attrs={}, **kwargs): +def while_loop(name=None, child_tasks=[], attrs={}, status_map_list=[], **kwargs): """ Create a WHILE LOOP Args: @@ -402,6 +509,8 @@ def while_loop(name=None, child_tasks=[], attrs={}, **kwargs): "type": "WHILE_LOOP", "attrs": attrs, } + if LV(CALM_VERSION) >= LV("3.9.0"): + params["status_map_list"] = status_map_list if "inherit_target" in kwargs: params["inherit_target"] = kwargs.get("inherit_target") return _task_create(**params) @@ -529,6 +638,7 @@ def exec_task_ssh( target_endpoint=None, cred=None, depth=2, + status_map_list=[], **kwargs, ): return _exec_create( @@ -540,6 +650,7 @@ def exec_task_ssh( target_endpoint=target_endpoint, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) @@ -551,6 +662,7 @@ def exec_task_escript( target=None, depth=2, tunnel=None, + status_map_list=[], **kwargs, ): return _exec_create( @@ -562,6 +674,7 @@ def exec_task_escript( target_endpoint=None, depth=depth, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) @@ -573,6 +686,7 @@ def exec_task_escript_py3( target=None, depth=2, tunnel=None, + status_map_list=[], **kwargs, ): return _exec_create( @@ -584,6 +698,7 @@ def exec_task_escript_py3( target_endpoint=None, depth=depth, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) @@ -596,6 +711,7 @@ def exec_task_powershell( target_endpoint=None, cred=None, depth=2, + status_map_list=[], **kwargs, ): return _exec_create( @@ -607,6 +723,7 @@ def exec_task_powershell( target_endpoint=target_endpoint, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) @@ -619,6 +736,7 @@ def exec_task_python( target_endpoint=None, cred=None, depth=2, + status_map_list=[], **kwargs, ): return _exec_create( @@ -630,12 +748,20 @@ def exec_task_python( target_endpoint=target_endpoint, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) def exec_task_ssh_runbook( - script=None, filename=None, name=None, target=None, cred=None, depth=2, **kwargs + script=None, + filename=None, + name=None, + target=None, + cred=None, + depth=2, + status_map_list=[], + **kwargs, ): """ This function is used to create exec task with shell target @@ -658,12 +784,20 @@ def exec_task_ssh_runbook( target=target, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) def exec_task_powershell_runbook( - script=None, filename=None, name=None, target=None, cred=None, depth=2, **kwargs + script=None, + filename=None, + name=None, + target=None, + cred=None, + depth=2, + status_map_list=[], + **kwargs, ): """ This function is used to create exec task with shell target @@ -686,12 +820,20 @@ def exec_task_powershell_runbook( target=target, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) def exec_task_python_runbook( - script=None, filename=None, name=None, target=None, cred=None, depth=2, **kwargs + script=None, + filename=None, + name=None, + target=None, + cred=None, + depth=2, + status_map_list=[], + **kwargs, ): """ This function is used to create exec task with python_remote target @@ -714,12 +856,20 @@ def exec_task_python_runbook( target=target, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) def decision_task_ssh( - script=None, filename=None, name=None, target=None, cred=None, depth=2, **kwargs + script=None, + filename=None, + name=None, + target=None, + cred=None, + depth=2, + status_map_list=[], + **kwargs, ): """ This function is used to create decision task with shell target @@ -742,12 +892,20 @@ def decision_task_ssh( target=target, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) def decision_task_powershell( - script=None, filename=None, name=None, target=None, cred=None, depth=2, **kwargs + script=None, + filename=None, + name=None, + target=None, + cred=None, + depth=2, + status_map_list=[], + **kwargs, ): """ This function is used to create decision task with powershell target @@ -770,12 +928,20 @@ def decision_task_powershell( target=target, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) def decision_task_python( - script=None, filename=None, name=None, target=None, cred=None, depth=2, **kwargs + script=None, + filename=None, + name=None, + target=None, + cred=None, + depth=2, + status_map_list=[], + **kwargs, ): """ This function is used to create decision task with python_remote target @@ -798,6 +964,7 @@ def decision_task_python( target=target, cred=cred, depth=depth, + status_map_list=status_map_list, **kwargs, ) @@ -809,6 +976,7 @@ def decision_task_escript( target=None, depth=2, tunnel=None, + status_map_list=[], **kwargs, ): """ @@ -832,6 +1000,7 @@ def decision_task_escript( target=target, depth=depth, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) @@ -843,6 +1012,7 @@ def decision_task_escript_py3( target=None, depth=2, tunnel=None, + status_map_list=[], **kwargs, ): """ @@ -866,6 +1036,7 @@ def decision_task_escript_py3( target=target, depth=depth, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) @@ -894,6 +1065,7 @@ def set_variable_task_ssh( variables=None, depth=3, cred=None, + status_map_list=[], **kwargs, ): """ @@ -917,6 +1089,7 @@ def set_variable_task_ssh( target_endpoint=target_endpoint, depth=depth, cred=cred, + status_map_list=status_map_list, **kwargs, ) return _set_variable_create(task, variables) @@ -930,6 +1103,7 @@ def set_variable_task_escript( variables=None, depth=3, tunnel=None, + status_map_list=[], **kwargs, ): """ @@ -952,6 +1126,7 @@ def set_variable_task_escript( target=target, depth=depth, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) return _set_variable_create(task, variables) @@ -965,6 +1140,7 @@ def set_variable_task_escript_py3( variables=None, depth=3, tunnel=None, + status_map_list=[], **kwargs, ): """ @@ -987,6 +1163,7 @@ def set_variable_task_escript_py3( target=target, depth=depth, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) return _set_variable_create(task, variables) @@ -1001,6 +1178,7 @@ def set_variable_task_powershell( variables=None, depth=3, cred=None, + status_map_list=[], **kwargs, ): """ @@ -1024,6 +1202,7 @@ def set_variable_task_powershell( target_endpoint=target_endpoint, depth=depth, cred=cred, + status_map_list=status_map_list, **kwargs, ) return _set_variable_create(task, variables) @@ -1038,6 +1217,7 @@ def set_variable_task_python( variables=None, depth=3, cred=None, + status_map_list=[], **kwargs, ): """ @@ -1061,6 +1241,7 @@ def set_variable_task_python( target_endpoint=target_endpoint, depth=depth, cred=cred, + status_map_list=status_map_list, **kwargs, ) return _set_variable_create(task, variables) @@ -1076,6 +1257,7 @@ def __new__( target=None, depth=2, tunnel=None, + status_map_list=[], **kwargs, ): return exec_task_escript_py3( @@ -1085,6 +1267,7 @@ def __new__( target=target, depth=depth + 1, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) @@ -1100,6 +1283,7 @@ def __new__( target=None, depth=2, tunnel=None, + status_map_list=[], **kwargs, ): return decision_task_escript_py3( @@ -1109,6 +1293,7 @@ def __new__( target=target, depth=depth + 1, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) @@ -1125,6 +1310,7 @@ def __new__( variables=None, depth=3, tunnel=None, + status_map_list=[], **kwargs, ): return set_variable_task_escript_py3( @@ -1135,6 +1321,7 @@ def __new__( variables=variables, depth=depth + 1, tunnel=tunnel, + status_map_list=status_map_list, **kwargs, ) @@ -1150,9 +1337,11 @@ def http_task_on_endpoint( secret_headers=None, content_type=None, status_mapping=None, + response_code_status_map=[], response_paths=None, name=None, target=None, + status_map_list=[], **kwargs, ): """ @@ -1166,6 +1355,7 @@ def http_task_on_endpoint( content_type (string): Request Content-Type (application/json, application/xml, etc.) status_mapping (dict): Mapping of Response status code (int) to task status (True: success, False: Failure) + response_code_status_map (list): List of Response code ranges mapping response_paths (dict): Mapping of variable name (str) to path in response (str) name (str): Task name target (Ref): Target entity that this task runs under. @@ -1182,9 +1372,11 @@ def http_task_on_endpoint( secret_headers=secret_headers, content_type=content_type, status_mapping=status_mapping, + response_code_status_map=response_code_status_map, response_paths=response_paths, name=name, target=target, + status_map_list=status_map_list, **kwargs, ) @@ -1242,7 +1434,8 @@ def http_task_delete_on_endpoint(**kwargs): def http_task_get( - url, + url="", + relative_url=None, body=None, headers=None, secret_headers=None, @@ -1253,11 +1446,14 @@ def http_task_get( retries=0, retry_interval=10, status_mapping=None, + response_code_status_map=[], response_paths=None, name=None, target=None, + target_endpoint=None, cred=None, tunnel=None, + status_map_list=[], ): """ @@ -1276,6 +1472,7 @@ def http_task_get( retry_interval (int): Time to wait in seconds between retries (Default: 10) status_mapping (dict): Mapping of Response status code (int) to task status (True: success, False: Failure) + response_code_status_map (list): List of Response code ranges mapping response_paths (dict): Mapping of variable name (str) to path in response (str) name (str): Task name target (Ref): Target entity that this task runs under. @@ -1286,6 +1483,7 @@ def http_task_get( return http_task( "GET", url, + relative_url=relative_url, body=None, headers=headers, secret_headers=secret_headers, @@ -1297,15 +1495,19 @@ def http_task_get( retries=retries, retry_interval=retry_interval, status_mapping=status_mapping, + response_code_status_map=response_code_status_map, response_paths=response_paths, name=name, target=target, + target_endpoint=target_endpoint, tunnel=tunnel, + status_map_list=status_map_list, ) def http_task_post( - url, + url="", + relative_url=None, body=None, headers=None, secret_headers=None, @@ -1316,11 +1518,14 @@ def http_task_post( retries=0, retry_interval=10, status_mapping=None, + response_code_status_map=[], response_paths=None, name=None, target=None, + target_endpoint=None, cred=None, tunnel=None, + status_map_list=[], ): """ @@ -1340,6 +1545,7 @@ def http_task_post( retry_interval (int): Time to wait in seconds between retries (Default: 10) status_mapping (dict): Mapping of Response status code (int) to task status (True: success, False: Failure) + response_code_status_map (list): List of Response code ranges mapping response_paths (dict): Mapping of variable name (str) to path in response (str) name (str): Task name target (Ref): Target entity that this task runs under. @@ -1350,6 +1556,7 @@ def http_task_post( return http_task( "POST", url, + relative_url=relative_url, body=body, headers=headers, secret_headers=secret_headers, @@ -1361,15 +1568,19 @@ def http_task_post( retries=retries, retry_interval=retry_interval, status_mapping=status_mapping, + response_code_status_map=response_code_status_map, response_paths=response_paths, name=name, target=target, + target_endpoint=target_endpoint, tunnel=tunnel, + status_map_list=status_map_list, ) def http_task_put( - url, + url="", + relative_url=None, body=None, headers=None, secret_headers=None, @@ -1380,11 +1591,14 @@ def http_task_put( retries=0, retry_interval=10, status_mapping=None, + response_code_status_map=[], response_paths=None, name=None, target=None, + target_endpoint=None, cred=None, tunnel=None, + status_map_list=[], ): """ @@ -1404,6 +1618,7 @@ def http_task_put( retry_interval (int): Time to wait in seconds between retries (Default: 10) status_mapping (dict): Mapping of Response status code (int) to task status (True: success, False: Failure) + response_code_status_map (list): List of Response code ranges mapping response_paths (dict): Mapping of variable name (str) to path in response (str) name (str): Task name target (Ref): Target entity that this task runs under. @@ -1414,6 +1629,7 @@ def http_task_put( return http_task( "PUT", url, + relative_url=relative_url, body=body, headers=headers, secret_headers=secret_headers, @@ -1425,15 +1641,19 @@ def http_task_put( retries=retries, retry_interval=retry_interval, status_mapping=status_mapping, + response_code_status_map=response_code_status_map, response_paths=response_paths, name=name, target=target, + target_endpoint=target_endpoint, tunnel=tunnel, + status_map_list=status_map_list, ) def http_task_delete( - url, + url="", + relative_url=None, body=None, headers=None, secret_headers=None, @@ -1444,11 +1664,14 @@ def http_task_delete( retries=0, retry_interval=10, status_mapping=None, + response_code_status_map=[], response_paths=None, name=None, target=None, + target_endpoint=None, cred=None, tunnel=None, + status_map_list=[], ): """ @@ -1468,6 +1691,7 @@ def http_task_delete( retry_interval (int): Time to wait in seconds between retries (Default: 10) status_mapping (dict): Mapping of Response status code (int) to task status (True: success, False: Failure) + response_code_status_map (list): List of Response code ranges mapping response_paths (dict): Mapping of variable name (str) to path in response (str) name (str): Task name target (Ref): Target entity that this task runs under. @@ -1478,6 +1702,7 @@ def http_task_delete( return http_task( "DELETE", url, + relative_url=relative_url, body=body, headers=headers, secret_headers=secret_headers, @@ -1489,10 +1714,13 @@ def http_task_delete( retries=retries, retry_interval=retry_interval, status_mapping=status_mapping, + response_code_status_map=response_code_status_map, response_paths=response_paths, name=name, target=target, + target_endpoint=target_endpoint, tunnel=tunnel, + status_map_list=status_map_list, ) @@ -1541,10 +1769,13 @@ def http_task( retries=0, retry_interval=10, status_mapping=None, + response_code_status_map=[], response_paths=None, name=None, target=None, + target_endpoint=None, tunnel=None, + status_map_list=[], **kwargs, ): """ @@ -1565,6 +1796,7 @@ def http_task( retry_interval (int): Time to wait in seconds between retries (Default: 10) status_mapping (dict): Mapping of Response status code (int) to task status (True: success, False: Failure) + response_code_status_map (list): List of Response code ranges mapping response_paths (dict): Mapping of variable name (str) to path in response (str) name (str): Task name target (Ref): Target entity that this task runs under. @@ -1625,6 +1857,9 @@ def http_task( }, } + if LV(CALM_VERSION) >= LV("3.9.0"): + params["status_map_list"] = status_map_list + if relative_url is not None: params["attrs"]["relative_url"] = relative_url @@ -1637,6 +1872,9 @@ def http_task( if target is not None: params["target_any_local_reference"] = _get_target_ref(target) + if target_endpoint is not None: + params["exec_target_reference"] = _get_target_ref(target_endpoint) + header_variables = [] if headers is not None: header_variables.extend(_header_variables_from_dict(headers)) @@ -1648,7 +1886,16 @@ def http_task( ) params["attrs"]["headers"] = header_variables + # If both response code status map and status mapping is present raise the error + if status_mapping and response_code_status_map: + err_msg = "Both status_mapping and response_code_status_map cannot be present together. status_mapping is now deprecated, start using response_code_status_map" + LOG.error(err_msg) + sys.exit(-1) + if status_mapping is not None: + LOG.warning( + "status_mapping will be deprecated soon, start using response_code_status_map" + ) LOG.debug("Status mapping for HTTP Task : {}".format(status_mapping)) if not isinstance(status_mapping, dict): raise TypeError( @@ -1675,6 +1922,9 @@ def http_task( ) params["attrs"]["expected_response_params"] = expected_response + if response_code_status_map: + params["attrs"]["expected_response_params"] = response_code_status_map + if response_paths is not None: LOG.debug("Response paths for HTTP Task : {}".format(response_paths)) if not isinstance(response_paths, dict): @@ -1782,7 +2032,9 @@ def delay_task(delay_seconds=None, name=None, target=None): return _task_create(**kwargs) -def vm_operation(name=None, type="VM_OPERATION", target=None, **kwargs): +def vm_operation( + name=None, type="VM_OPERATION", target=None, status_map_list=[], **kwargs +): """ Defines a vm_operation task i.e. POWERON/ POWEROFF/ RESTART Args: @@ -1794,6 +2046,9 @@ def vm_operation(name=None, type="VM_OPERATION", target=None, **kwargs): (Task): VM Operation task """ params = {"name": name, "type": type} + if LV(CALM_VERSION) >= LV("3.9.0"): + params["status_map_list"] = status_map_list + if target is not None: params["target_any_local_reference"] = _get_target_ref(target) if "inherit_target" in kwargs: @@ -1892,7 +2147,8 @@ class HTTP: def __new__( cls, method, - url, + url="", + relative_url=None, body=None, headers=None, secret_headers=None, @@ -1904,14 +2160,18 @@ def __new__( retries=0, retry_interval=10, status_mapping=None, + response_code_status_map=[], response_paths=None, name=None, target=None, + target_endpoint=None, tunnel=None, + status_map_list=[], ): return http_task( method, url, + relative_url, body=body, headers=headers, secret_headers=secret_headers, @@ -1923,10 +2183,13 @@ def __new__( retries=retries, retry_interval=retry_interval, status_mapping=status_mapping, + response_code_status_map=response_code_status_map, response_paths=response_paths, name=name, target=target, + target_endpoint=target_endpoint, tunnel=tunnel, + status_map_list=status_map_list, ) get = http_task_get @@ -1969,6 +2232,37 @@ def __new__(cls, config, name=None): return create_call_config(target, config, name) +class ProviderTask(CalmTask): + class Decision: + def __new__(cls, *args, **kwargs): + raise TypeError("'{}' is not callable".format(cls.__name__)) + + ssh = decision_task_ssh + powershell = decision_task_powershell + escript = EscriptTaskType.DecisionTask + python = decision_task_python + + class Loop: + def __new__( + cls, + iterations, + name=None, + child_tasks=[], + loop_variable="iteration", + exit_condition=Status.DONT_CARE, + **kwargs, + ): + attrs = {"iterations": str(iterations), "loop_variable": loop_variable} + exit_code = EXIT_CONDITION_MAP.get(exit_condition, None) + if exit_code: + attrs["exit_condition_type"] = exit_code + else: + raise ValueError( + "Valid Exit Conditions for loop are 'Status.SUCCESS/Status.FAILURE/Status.DONT_CARE'." + ) + return while_loop(name=name, child_tasks=child_tasks, attrs=attrs, **kwargs) + + class RunbookTask(BaseTask): class Decision: def __new__(cls, *args, **kwargs): @@ -2013,6 +2307,9 @@ def __new__( tag=tag, ) + class ResourceTypeAction(ResourceTypeOperationTask): + pass + class Loop: def __new__( cls, @@ -2021,6 +2318,7 @@ def __new__( child_tasks=[], loop_variable="iteration", exit_condition=Status.DONT_CARE, + status_map_list=[], **kwargs, ): attrs = {"iterations": str(iterations), "loop_variable": loop_variable} @@ -2031,7 +2329,13 @@ def __new__( raise ValueError( "Valid Exit Conditions for loop are 'Status.SUCCESS/Status.FAILURE/Status.DONT_CARE'." ) - return while_loop(name=name, child_tasks=child_tasks, attrs=attrs, **kwargs) + return while_loop( + name=name, + child_tasks=child_tasks, + attrs=attrs, + status_map_list=status_map_list, + **kwargs, + ) class HTTP: def __new__( @@ -2043,9 +2347,11 @@ def __new__( secret_headers=None, content_type=None, status_mapping=None, + response_code_status_map=[], response_paths=None, name=None, target=None, + status_map_list=[], **kwargs, ): return http_task_on_endpoint( @@ -2056,9 +2362,11 @@ def __new__( secret_headers=secret_headers, content_type=content_type, status_mapping=status_mapping, + response_code_status_map=response_code_status_map, response_paths=response_paths, name=name, target=target, + status_map_list=status_map_list, **kwargs, ) @@ -2076,16 +2384,34 @@ def __new__(cls, timeout=500, name=None): return confirm_task(timeout=timeout, name=name) class VMPowerOn: - def __new__(cls, name=None, target=None, **kwargs): - return vm_operation(name=name, type="VM_POWERON", target=target, **kwargs) + def __new__(cls, name=None, target=None, status_map_list=[], **kwargs): + return vm_operation( + name=name, + type="VM_POWERON", + target=target, + status_map_list=status_map_list, + **kwargs, + ) class VMPowerOff: - def __new__(cls, name=None, target=None, **kwargs): - return vm_operation(name=name, type="VM_POWEROFF", target=target, **kwargs) + def __new__(cls, name=None, target=None, status_map_list=[], **kwargs): + return vm_operation( + name=name, + type="VM_POWEROFF", + target=target, + status_map_list=status_map_list, + **kwargs, + ) class VMRestart: - def __new__(cls, name=None, target=None, **kwargs): - return vm_operation(name=name, type="VM_RESTART", target=target, **kwargs) + def __new__(cls, name=None, target=None, status_map_list=[], **kwargs): + return vm_operation( + name=name, + type="VM_RESTART", + target=target, + status_map_list=status_map_list, + **kwargs, + ) class NutanixDB: """This is the base class of all the NDB Task DSL Support (Not Callable)""" diff --git a/calm/dsl/builtins/models/utils.py b/calm/dsl/builtins/models/utils.py index 9f1612909..38a27c1e9 100644 --- a/calm/dsl/builtins/models/utils.py +++ b/calm/dsl/builtins/models/utils.py @@ -5,6 +5,7 @@ from ruamel import yaml import re + from calm.dsl.log import get_logging_handle from calm.dsl.config import get_context diff --git a/calm/dsl/builtins/models/variable.py b/calm/dsl/builtins/models/variable.py index 108fc7ec5..5c4f90b3a 100644 --- a/calm/dsl/builtins/models/variable.py +++ b/calm/dsl/builtins/models/variable.py @@ -211,9 +211,12 @@ def _advanced_variable( + ", is not valid, Expected one of" + " ['HTTP', 'EXEC'], got {}".format(task_type) ) + task_target_endpoint = getattr(task, "exec_target_reference", None) task_attrs["type"] = task_type kwargs["type_"] = task_type + "_" + type_ kwargs["options"] = {"type": task_type, "attrs": task_attrs} + if task_type == "EXEC" and task_target_endpoint: + kwargs["options"]["exec_target_reference"] = task_target_endpoint.get_dict() if value_type is not None: value_type = value_type.upper() if value_type not in VARIABLE_VALUE_TYPES.values(): @@ -1039,6 +1042,7 @@ def variable_string_with_options_from_task( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1053,6 +1057,7 @@ def variable_string_with_options_from_task( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1065,6 +1070,7 @@ def variable_int_with_options_from_task( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1079,6 +1085,7 @@ def variable_int_with_options_from_task( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1091,6 +1098,7 @@ def variable_date_with_options_from_task( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1105,6 +1113,7 @@ def variable_date_with_options_from_task( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1117,6 +1126,7 @@ def variable_time_with_options_from_task( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1131,6 +1141,7 @@ def variable_time_with_options_from_task( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1143,6 +1154,7 @@ def variable_datetime_with_options_from_task( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1157,6 +1169,7 @@ def variable_datetime_with_options_from_task( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1169,6 +1182,7 @@ def variable_multiline_with_options_from_task( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1183,6 +1197,7 @@ def variable_multiline_with_options_from_task( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1195,6 +1210,7 @@ def variable_string_with_options_from_task_array( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1209,6 +1225,7 @@ def variable_string_with_options_from_task_array( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1221,6 +1238,7 @@ def variable_int_with_options_from_task_array( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1235,6 +1253,7 @@ def variable_int_with_options_from_task_array( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1247,6 +1266,7 @@ def variable_date_with_options_from_task_array( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1261,6 +1281,7 @@ def variable_date_with_options_from_task_array( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1273,6 +1294,7 @@ def variable_time_with_options_from_task_array( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1287,6 +1309,7 @@ def variable_time_with_options_from_task_array( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1299,6 +1322,7 @@ def variable_datetime_with_options_from_task_array( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1313,6 +1337,7 @@ def variable_datetime_with_options_from_task_array( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1325,6 +1350,7 @@ def variable_multiline_with_options_from_task_array( is_hidden=False, is_mandatory=False, description="", + value="", ): return _advanced_variable( "LOCAL", @@ -1339,6 +1365,7 @@ def variable_multiline_with_options_from_task_array( is_mandatory=is_mandatory, runtime=True, description=description, + value=value, ) @@ -1541,6 +1568,7 @@ def __new__( is_hidden=False, is_mandatory=False, description="", + value="", ): return variable_string_with_options_from_task( task, @@ -1551,6 +1579,7 @@ def __new__( is_hidden=is_hidden, is_mandatory=is_mandatory, description=description, + value=value, ) string = variable_string_with_options_from_task @@ -1571,6 +1600,7 @@ def __new__( is_hidden=False, is_mandatory=False, description="", + value="", ): return variable_string_with_options_from_task_array( task, @@ -1581,6 +1611,7 @@ def __new__( is_hidden=is_hidden, is_mandatory=is_mandatory, description=description, + value=value, ) string = variable_string_with_options_from_task_array diff --git a/calm/dsl/cli/account_commands.py b/calm/dsl/cli/account_commands.py index c0088818c..808d21145 100644 --- a/calm/dsl/cli/account_commands.py +++ b/calm/dsl/cli/account_commands.py @@ -45,21 +45,14 @@ default=None, multiple=True, help="Search for accounts of specific provider", - type=click.Choice( - [ - "aws", - "k8s", - "vmware", - "azure", - "gcp", - "nutanix", - "nutanix_pc", - "custom_provider", - ] - ), ) def _get_accounts(name, filter_by, limit, offset, quiet, all_items, account_type): - """Get accounts, optionally filtered by a string""" + """Get accounts, optionally filtered by a string + + Supported values for account_type are:\n + - "aws", "k8s", "vmware", "azure", "gcp", "nutanix", "nutanix_pc", "custom_provider"\n + NOTE: For Calm versions >= 4.0.0 where users can define their own cloud Providers, the provider names can also be used in account_type filter + """ get_accounts(name, filter_by, limit, offset, quiet, all_items, account_type) @@ -91,11 +84,17 @@ def _sync_account(account_name): @verify.command("account", feature_min_version="3.0.0") @click.argument("account_name") -def _verify_account(account_name): +@click.option( + "--watch/--no-watch", + "-w", + default=False, + help="Watch verify execution. Applies only to accounts of user-defined providers", +) +def _verify_account(account_name, watch): """Verifies an account Args: account_name (string): name of the account to verify""" - verify_account(account_name) + verify_account(account_name, watch=watch) @create.command("account") @@ -122,7 +121,13 @@ def _verify_account(account_name): default=False, help="Verifies the account after successfull account creation", ) -def create_account_command(account_file, name, force, auto_verify): +@click.option( + "--watch-verify/--no-watch-verify", + "-w", + default=False, + help="Watch verify execution. Applies only to accounts of user-defined providers", +) +def create_account_command(account_file, name, force, auto_verify, watch_verify): """ Creates an account @@ -142,7 +147,7 @@ def create_account_command(account_file, name, force, auto_verify): return if auto_verify: - verify_account(account_data["name"]) + verify_account(account_data["name"], watch=watch_verify) @compile.command("account") diff --git a/calm/dsl/cli/accounts.py b/calm/dsl/cli/accounts.py index 1fff17bb8..e7cd81203 100644 --- a/calm/dsl/cli/accounts.py +++ b/calm/dsl/cli/accounts.py @@ -17,11 +17,13 @@ from calm.dsl.config import get_context from calm.dsl.builtins.models.metadata_payload import get_metadata_payload from .providers import ( - update_custom_provider, - get_custom_provider, - create_custom_provider, + update_provider, + get_provider as get_custom_provider, + create_provider, + watch_action_execution, ) from calm.dsl.providers import get_provider +from .providers import get_provider_from_cache from .resource_types import update_resource_types, create_resource_type from .utils import ( @@ -31,7 +33,7 @@ insert_uuid, _get_nested_messages, ) -from calm.dsl.constants import PROVIDER, ACCOUNT +from calm.dsl.constants import PROVIDER, ACCOUNT, VARIABLE from calm.dsl.store import Version from calm.dsl.tools import get_module_from_file from calm.dsl.log import get_logging_handle @@ -234,7 +236,7 @@ def create_account(client, account_payload, name=None, force_create=False): # For custom_provider type we create provider and resource_type before creating account if account_type == "custom_provider": if account_payload["provider"]: - create_custom_provider( + create_provider( provider_payload=account_payload.get("provider", {}), name=name ) if account_payload["resource_type"]: @@ -286,24 +288,12 @@ def create_account(client, account_payload, name=None, force_create=False): account_name = account_payload["spec"]["name"] - return client.account.create( + res, err = client.account.create( account_name, account_payload, force_create=force_create, ) - -def create_account_from_dsl(client, account_file, name=None, force_create=False): - - account_payload = compile_account(account_file) - - if account_payload is None: - LOG.error("User account not found in {}".format(account_file)) - sys.exit("User account not found") - - res, err = create_account( - client, account_payload, name=name, force_create=force_create - ) if err: LOG.error(err["error"]) sys.exit("Account creation failed") @@ -369,6 +359,36 @@ def create_account_from_dsl(client, account_file, name=None, force_create=False) } click.echo(json.dumps(stdout_dict, indent=4, separators=(",", ": "))) + # Sending back all the responses including the res and err to not break automation tests. + return res, err, stdout_dict + + +def create_account_from_dsl(client, account_file, name=None, force_create=False): + """ + Create an account from a DSL file. + + Args: + client (object): The client object for interacting with the API. + account_file (str): The path to the DSL file containing the account details. + name (str, optional): The name of the account. Defaults to None. + force_create (bool, optional): Whether to force the creation of the account if it already exists. Defaults to False. + + Returns: + object: The created account object. + + Raises: + SystemExit: If the user account is not found in the DSL file. + """ + + account_payload = compile_account(account_file) + + if account_payload is None: + LOG.error("User account not found in {}".format(account_file)) + sys.exit("User account not found") + + _, _, stdout_dict = create_account( + client, account_payload, name=name, force_create=force_create + ) return stdout_dict @@ -505,15 +525,16 @@ def create_resource_type_payload(UserAccount, provider_uuid): action_list = resources.get("action_list", []).copy() - name_uuid_map = {} action_list_with_uuid = copy.deepcopy(action_list) # Inserting uuids in action_list - insert_uuid( - action=action_list, - name_uuid_map=name_uuid_map, - action_list_with_uuid=action_list_with_uuid, - ) + if action_list: + for index, action in enumerate(action_list): + insert_uuid( + action=action, + name_uuid_map={}, + action_list_with_uuid=action_list_with_uuid[index], + ) for input_var in input_vars: input_var["uuid"] = str(uuid.uuid4()) @@ -657,7 +678,12 @@ def delete_account(account_names): # TODO Fix case for custom providers if delete_accounts_uuids: for _acc_id in delete_accounts_uuids: - Cache.delete_one(entity_type=CACHE.ENTITY.ACCOUNT, uuid=_acc_id) + try: + Cache.delete_one(entity_type=CACHE.ENTITY.ACCOUNT, uuid=_acc_id) + except Exception as ex: + LOG.debug( + "Cannot delete account from cache. Reason: {}".format(str(ex)) + ) click.echo(".[Done]", err=True) @@ -840,7 +866,7 @@ def describe_k8s_account(spec): click.echo(highlight_text(auth_type)) -def describe_custom_provider_account(client, spec): +def describe_cred_provider_account(client, spec): provider_name = resource_type_name = spec["provider_reference"]["name"] click.echo("Provider Name: {}".format(provider_name)) @@ -879,6 +905,22 @@ def describe_custom_provider_account(client, spec): click.echo("\t{}".format(highlight_text(variable["name"]))) +def describe_cloud_provider_account(provider_name, spec): + click.echo("Provider Name: {}".format(provider_name)) + click.echo("Account Variables") + for var_dict in spec["variable_list"]: + click.echo( + "\t{} (Value: {})".format( + highlight_text(var_dict["label"] or var_dict["name"]), + highlight_text( + var_dict["value"] + if var_dict["type"] not in VARIABLE.SECRET_TYPES + else "*******" + ), + ) + ) + + def describe_account(account_name): client = get_api_client() @@ -898,7 +940,14 @@ def describe_account(account_name): + ")" ) click.echo("Status: " + highlight_text(account["status"]["resources"]["state"])) - click.echo("Account Type: " + highlight_text(account_type.upper())) + click.echo( + "Account Type: " + + highlight_text( + account_type.upper() + if account_type in ACCOUNT.STANDARD_TYPES + else account_type + ) + ) click.echo( "Owner: " + highlight_text(account["metadata"]["owner_reference"]["name"]) ) @@ -937,7 +986,10 @@ def describe_account(account_name): describe_azure_account(provider_data) elif account_type == "custom_provider": - describe_custom_provider_account(client, provider_data) + describe_cred_provider_account(client, provider_data) + + elif get_provider_from_cache(account_type): + describe_cloud_provider_account(account_type, provider_data) else: click.echo("Provider details not present") @@ -981,7 +1033,7 @@ def sync_account(account_name): LOG.info("Syncing account successfull") -def verify_account(account_name): +def verify_account(account_name, watch=False): """Verify an account with corresponding account name""" client = get_api_client() @@ -992,13 +1044,32 @@ def verify_account(account_name): sys.exit(-1) LOG.info("Verifying account '{}'".format(account_name)) - _, err = client.account.verify(account_uuid) - + res, err = client.account.verify(account_uuid) if err: LOG.exception("[{}] - {}".format(err["code"], err["error"])) sys.exit("Account verification failed") - LOG.info("Account '{}' sucessfully verified".format(account_name)) + response = res.json() + is_async_verify = "runlog_uuid" in response + if not is_async_verify: + LOG.info("Account '{}' sucessfully verified".format(account_name)) + return + + LOG.info(response["description"]) + runlog_uuid = response["runlog_uuid"] + if watch: + LOG.debug("Watching on the execution status") + watch_action_execution(runlog_uuid) + else: + server_config = get_context().get_server_config() + run_url = ( + "https://{}:{}/console/#page/explore/calm/providers/runlogs/{}".format( + server_config["pc_ip"], server_config["pc_port"], runlog_uuid + ) + ) + LOG.info("Verify action execution url: {}".format(highlight_text(run_url))) + watch_cmd = "calm watch provider-verify-execution {}".format(runlog_uuid) + LOG.info("Command to watch the execution: {}".format(highlight_text(watch_cmd))) def update_account(client, account_payload, name=None, updated_name=None): @@ -1067,7 +1138,7 @@ def update_account_from_dsl(client, account_file, name=None, updated_name=None): # if is is a credential provider account if account_type == ACCOUNT.TYPE.CUSTOM_PROVIDER: - update_custom_provider( + update_provider( provider_payload=account_payload["provider"], name=name, updated_name=updated_name, diff --git a/calm/dsl/cli/acps.py b/calm/dsl/cli/acps.py index 876d04de8..10d75f735 100644 --- a/calm/dsl/cli/acps.py +++ b/calm/dsl/cli/acps.py @@ -13,7 +13,7 @@ from calm.dsl.builtins import Ref from calm.dsl.store import Version -from .constants import ACP, ACP_3_8_0, ACP_BEFORE_3_8_0, ROLE +from .constants import ACP, ACP_3_8_0, ACP_BEFORE_3_8_0, ACP_3_8_1, ROLE from .task_commands import watch_task from .utils import get_name_query, highlight_text @@ -815,6 +815,8 @@ def get_updated_acp_filter_list_for_3_8_0_global_scope( filter_list.extend(ACP_3_8_0.CUSTOM_ROLE_PERMISSIONS_FILTERS) else: filter_list.extend(ACP_3_8_0.ENTITY_FILTER_EXPRESSION_LIST.COMMON) + if LV(CALM_VERSION) >= LV("3.8.1"): + filter_list.extend(ACP_3_8_1.ENTITY_FILTER_EXPRESSION_LIST.COMMON) if role == ROLE.PROJECT_ADMIN: filter_list.extend(ACP_3_8_0.ENTITY_FILTER_EXPRESSION_LIST.PROJECT_ADMIN) diff --git a/calm/dsl/cli/apps.py b/calm/dsl/cli/apps.py index 1c1436d55..ac7a72adf 100644 --- a/calm/dsl/cli/apps.py +++ b/calm/dsl/cli/apps.py @@ -15,6 +15,7 @@ from calm.dsl.api import get_api_client from calm.dsl.config import get_context +from calm.dsl.constants import PROVIDER from .utils import get_name_query, get_states_filter, highlight_text, Display from .constants import APPLICATION, RUNLOG, SYSTEM_ACTIONS @@ -171,9 +172,19 @@ def _get_app(client, app_name, screen=Display(), all=False): return app +def echo_formatted(label, value, is_title=False): + if is_title: + click.echo(f"\t \t \t {label}{highlight_text(value)}") + else: + click.echo(f"\t \t \t \t {label:<15} : {highlight_text(value)}") + + def describe_app(app_name, out): client = get_api_client() app = _get_app(client, app_name, all=True) + status = app.get("status", {}) + resources = status.get("resources", {}) + metadata = app.get("metadata", {}) if out == "json": click.echo(json.dumps(app, indent=4, separators=(",", ": "))) @@ -188,35 +199,48 @@ def describe_app(app_name, out): + highlight_text(app["metadata"]["uuid"]) + ")" ) - click.echo("Status: " + highlight_text(app["status"]["state"])) + click.echo("Status: " + highlight_text(status.get("state", "N/A"))) + click.echo( - "Owner: " + highlight_text(app["metadata"]["owner_reference"]["name"]), nl=False + "Blueprint: " + + highlight_text( + resources.get("app_blueprint_reference", {}).get("name", "N/A") + ) ) + click.echo( - " Project: " + highlight_text(app["metadata"]["project_reference"]["name"]) + "Application Profile: " + + highlight_text( + resources.get("app_profile_config_reference", {}).get("name", "N/A") + ) ) + deployment_list = resources.get("deployment_list", []) + if deployment_list: + substrate_configuration = deployment_list[0].get("substrate_configuration", {}) + click.echo( + "Provider: " + highlight_text(substrate_configuration.get("type", "N/A")) + ) + click.echo( - "Blueprint: " - + highlight_text(app["status"]["resources"]["app_blueprint_reference"]["name"]) + "Project: " + + highlight_text(metadata.get("project_reference", {}).get("name", "N/A")) ) - created_on = int(app["metadata"]["creation_time"]) // 1000000 - past = arrow.get(created_on).humanize() click.echo( - "Created: {} ({})".format( - highlight_text(time.ctime(created_on)), highlight_text(past) - ) + "Owner: " + + highlight_text(metadata.get("owner_reference", {}).get("name", "N/A")) ) + created_on = int(metadata.get("creation_time", 0)) // 1000000 + past = arrow.get(created_on).humanize() click.echo( - "Application Profile: " - + highlight_text( - app["status"]["resources"]["app_profile_config_reference"]["name"] + "Created: {} ({})".format( + highlight_text(time.ctime(created_on)), highlight_text(past) ) ) - deployment_list = app["status"]["resources"]["deployment_list"] + deployment_list = resources.get("deployment_list", []) click.echo("Deployments [{}]:".format(highlight_text((len(deployment_list))))) for deployment in deployment_list: @@ -229,7 +253,11 @@ def describe_app(app_name, out): if num_services > 1: exta_suffix = "[" + str(i) + "]" - temp_var = "[" + str(deployment["state"][i]) + "]" + temp_var = ( + "[" + + str(deployment["service_list"][0]["element_list"][i]["state"]) + + "]" + ) click.echo( "\t Service : {}{} {}".format( highlight_text(deployment["service_list"][0]["name"]), @@ -237,185 +265,344 @@ def describe_app(app_name, out): highlight_text(temp_var), ) ) - click.echo("\t \t VM Details") - click.echo("\t \t \t Configuration") - click.echo( - "\t \t \t \t {:<15} : {}".format( - "Name", - highlight_text(deployment["substrate_configuration"]["name"]), - ) - ) - click.echo( - "\t \t \t \t {:<15} : {}".format( - "IP Address", - highlight_text( - deployment["substrate_configuration"]["element_list"][i][ - "address" - ] - ), - ) - ) - click.echo( - "\t \t \t \t {:<15} : {}".format( - "vCPUs", - highlight_text( - deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["resources"]["num_vcpus_per_socket"] - ), - ) - ) - click.echo( - "\t \t \t \t {:<15} : {}".format( - "Cores", - highlight_text( - deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["resources"]["num_sockets"] - ), - ) - ) - click.echo( - "\t \t \t \t {:<15} : {} {}".format( - "Memory", - highlight_text( - deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["resources"]["memory_size_mib"] - / 1024.0 - ), - highlight_text("GB"), - ) - ) - click.echo( - "\t \t \t \t {:<15} : {}".format( - "VM UUID", - highlight_text( - deployment["substrate_configuration"]["element_list"][i][ - "instance_id" - ] - ), - ) - ) - click.echo( - "\t \t \t \t {:<15} : {}".format( - "Image", - highlight_text( - deployment["substrate_configuration"]["create_spec"][ - "resources" - ]["disk_list"][0]["data_source_reference"]["uuid"] - ), - ) - ) - click.echo("\t \t \t Network Adapters (NICs)") if ( - len( - deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["resources"]["nic_list"] - ) - > 0 + deployment.get("substrate_configuration", {}).get("type", "") + == PROVIDER.TYPE.AHV ): - for nic in deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["resources"]["nic_list"]: - click.echo( - "\t \t \t \t {:<15} : {}".format( - "Type", highlight_text(nic["nic_type"]) + if ( + len( + deployment.get("substrate_configuration", {}).get( + "element_list", [] ) ) + <= i + ): + continue for variable in deployment["substrate_configuration"]["element_list"][ i - ]["variable_list"]: - if variable["name"] == "mac_address": - click.echo( - "\t \t \t \t {:<15} : {}".format( - "MAC Address", highlight_text(variable["value"]) - ) + ].get("variable_list", []): + if variable.get("name") == "platform_data": + click.echo("\t \t VM Details") + echo_formatted("Configuration", "", True) + value_dict = json.loads(variable.get("value", "{}")) + status = value_dict.get("status", {}) + resources = status.get("resources", {}) + metadata = value_dict.get("metadata", {}) + + echo_formatted( + "Name", value_dict.get("spec", {}).get("name", "") ) - for nic in deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["resources"]["nic_list"]: - click.echo( - "\t \t \t \t {:<15} : {}".format( - "Subnet", highlight_text(nic["subnet_reference"]["name"]) + echo_formatted( + "IP Address", + resources.get("nic_list", [{}])[0] + .get("ip_endpoint_list", [{}])[0] + .get("ip", ""), + ) + echo_formatted("vCPUs", resources.get("num_sockets", "")) + echo_formatted( + "Cores", resources.get("num_vcpus_per_socket", "") + ) + echo_formatted( + "Memory", + f"{resources.get('memory_size_mib', 0) / 1024.0} GB", + ) + echo_formatted("VM UUID", metadata.get("uuid", "")) + echo_formatted( + "Image", + deployment["substrate_configuration"]["create_spec"][ + "resources" + ]["disk_list"][0]["data_source_reference"].get("uuid", ""), ) - ) - else: - click.echo("\t \t \t \t {:<15} : ".format("Type")) - click.echo("\t \t \t \t {:<15} : ".format("MAC Address")) - click.echo("\t \t \t \t {:<15} : ".format("Subnet")) - if ( - deployment["substrate_configuration"]["element_list"][i]["create_spec"][ - "cluster_reference" - ] - != None + if resources.get("nic_list"): + echo_formatted("Network Adapters (NICs)", "", True) + for nic_dict in resources["nic_list"]: + echo_formatted("Type", nic_dict.get("nic_type", "")) + echo_formatted( + "MAC Address", nic_dict.get("mac_address", "") + ) + echo_formatted( + "Subnet", + nic_dict.get("subnet_reference", {}).get( + "name", "" + ), + ) + + cluster_ref = status.get("cluster_reference", {}) + if cluster_ref: + echo_formatted("Cluster Information", "", True) + echo_formatted("Cluster UUID", cluster_ref.get("uuid", "")) + echo_formatted("Cluster Name", cluster_ref.get("name", "")) + + if metadata.get("categories"): + echo_formatted("Categories", "", True) + for key, value in metadata["categories"].items(): + echo_formatted(key, value) + + elif ( + deployment.get("substrate_configuration", {}).get("type", "") + == PROVIDER.TYPE.VMWARE ): - click.echo("\t \t \t Cluster Information") - click.echo( - "\t \t \t \t {:<15} : {}".format( - "Cluster UUID", - highlight_text( - deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["cluster_reference"]["uuid"] - ), + if ( + len( + deployment.get("substrate_configuration", {}).get( + "element_list", [] + ) ) - ) - click.echo( - "\t \t \t \t {:<15} : {}".format( - "Cluster Name", - highlight_text( - deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["cluster_reference"]["name"] - ), + <= i + ): + continue + for variable in deployment["substrate_configuration"]["element_list"][ + i + ].get("variable_list", []): + if variable["name"] == "platform_data": + click.echo("\t \t VM Details") + echo_formatted("Configuration", "", True) + value_dict = json.loads(variable.get("value", "{}")) + + echo_formatted("Type", value_dict.get("instance_name", "")) + echo_formatted( + "IP Address", + value_dict.get("guest_info", {}).get("guest_ipaddress", ""), + ) + echo_formatted( + "Host", + value_dict.get("guest_info", {}).get("guest_hostname", ""), + ) + echo_formatted( + "Guest OS State", + value_dict.get("guest_info", {}).get("guest_state", ""), + ) + echo_formatted( + "Guest OS", + value_dict.get("guest_info", {}).get("guest_family", ""), + ) + echo_formatted("vCPUs", value_dict.get("num_sockets", "")) + echo_formatted( + "Cores Per Socket", + value_dict.get("num_vcpus_per_socket", ""), + ) + # if int(value_dict.get("num_vcpus_per_socket", "0"))>0 and int(value_dict.get("num_sockets", "0"))>0: + # echo_formatted("Sockets", int(value_dict.get("num_vcpus_per_socket")) / int(value_dict.get("num_sockets"))) + echo_formatted("Sockets", value_dict.get("num_sockets", "")) + if ( + len( + deployment.get("substrate_configuration", {}).get( + "element_list", [{}] + ) + ) + > i + ): + echo_formatted( + "Memory", + f"{deployment.get('substrate_configuration', {}).get('element_list', [{}])[i].get('create_spec', {}).get('resources', {}).get('memory_size_mib', 0) / 1024.0} GB", + ) + echo_formatted( + "Power State", value_dict.get("runtime.powerState", "") + ) + echo_formatted("VM Location", value_dict.get("folder", "")) + + elif ( + deployment.get("substrate_configuration", {}).get("type", "") + == PROVIDER.TYPE.AWS + ): + if ( + len( + deployment.get("substrate_configuration", {}).get( + "element_list", [] + ) ) - ) - else: - click.echo("\t \t \t Cluster Information") - click.echo("\t \t \t \t {:<15} : ".format("Cluster UUID")) - click.echo("\t \t \t \t {:<15} : ".format("Cluster Name")) - - categories = deployment["substrate_configuration"]["element_list"][i][ - "create_spec" - ]["categories"] - if len(categories) > 0: - click.echo("\t \t \t Categories") - for key, value in categories.items(): - click.echo( - "\t \t \t \t {:<15} : {}".format(key, highlight_text(value)) + <= i + ): + continue + for variable in deployment["substrate_configuration"]["element_list"][ + i + ].get("variable_list", []): + if variable["name"] == "platform_data": + click.echo("\t \t VM Details") + echo_formatted("Configuration", "", True) + + value_dict = json.loads(variable.get("value", "{}")) + resources_dict = json.loads(variable["value"])["status"][ + "resources" + ] + echo_formatted( + "Name", value_dict.get("status", {}).get("name", "") + ) + echo_formatted( + "IP Address", resources_dict.get("public_ip_address", "") + ) + echo_formatted("Region", resources_dict.get("region", "")) + echo_formatted( + "Availability Zone", + resources_dict.get("availability_zone", ""), + ) + echo_formatted("Image ID", resources_dict.get("image_id", "")) + echo_formatted( + "Public DNS Name", resources_dict.get("public_dns_name", "") + ) + echo_formatted( + "Private IP Address", + resources_dict.get("private_ip_address", ""), + ) + echo_formatted( + "Private DNS Name", + resources_dict.get("private_dns_name", ""), + ) + echo_formatted( + "Root Device Type", + resources_dict.get("root_device_type", ""), + ) + echo_formatted( + "Launch Time", resources_dict.get("launch_time", "") + ) + echo_formatted("State", resources_dict.get("state", "")) + security_group_list = resources_dict.get( + "security_group_list", [] + ) + if security_group_list: + echo_formatted( + "Security Group List", + security_group_list[0].get("security_group_id", ""), + ) + echo_formatted( + "IAM Role", resources_dict.get("instance_profile_name", "") + ) + echo_formatted( + "Shutdown Behavior", + resources_dict.get( + "instance_initiated_shutdown_behavior", "" + ), + ) + tag_list = resources_dict.get("tag_list", []) + echo_formatted("TAGS ", f"({len(tag_list)})", True) + for tag in tag_list: + echo_formatted(tag.get("key", ""), tag.get("value", "")) + + elif ( + deployment.get("substrate_configuration", {}).get("type", "") + == PROVIDER.TYPE.AZURE + ): + if ( + len( + deployment.get("substrate_configuration", {}).get( + "element_list", [] + ) ) + <= i + ): + continue + for variable in deployment["substrate_configuration"]["element_list"][ + i + ].get("variable_list", []): + if variable["name"] == "platform_data": + click.echo("\t \t VM Details") + echo_formatted("Configuration", "", True) + + value_dict = json.loads(variable.get("value", "{}")) + azure_data = value_dict.get("azureData", {}) + properties = azure_data.get("properties", {}) + storage_profile = properties.get("storageProfile", {}) + os_disk = storage_profile.get("osDisk", {}) + + echo_formatted("Name", azure_data.get("name", "")) + echo_formatted("Location", azure_data.get("location", "")) + echo_formatted("VM Id", properties.get("vmId", "")) + echo_formatted("OS Type", os_disk.get("osType", "")) + echo_formatted("Size", str(os_disk.get("diskSizeGB", ""))) + echo_formatted( + "Hardware Profile", + properties.get("hardwareProfile", {}).get("vmSize", ""), + ) + echo_formatted( + "Public IP", value_dict.get("publicIPAddress", "") + ) + echo_formatted( + "Private IP", value_dict.get("privateIPAddress", "") + ) - if ( - len(deployment["service_list"]) > 0 - and len( - deployment["service_list"][0]["element_list"][i]["variable_list"] - ) - > 0 + echo_formatted("Network Profile", "", True) + nic_list = value_dict.get("nicList", "").split(",") + for index, nic in enumerate(nic_list): + echo_formatted(f"NIC-{index}", nic) + + echo_formatted("Data Disks", "", True) + for data_disk_dict in storage_profile.get("dataDisks", []): + echo_formatted("Disk Name", data_disk_dict.get("name", "")) + + tags = azure_data.get("tags", {}) + echo_formatted("TAGS ", f"({len(tags)})", True) + for key, value in tags.items(): + echo_formatted(key, value) + + elif ( + deployment.get("substrate_configuration", {}).get("type", "") + == PROVIDER.TYPE.GCP ): - click.echo("\t \t Variables") - for variable in deployment["service_list"][0]["element_list"][i][ - "variable_list" - ]: - if ( - variable["type"] == "LOCAL" or variable["type"] == "HTTP_LOCAL" - ) and variable["value"] != "": - click.echo( - "\t \t \t \t {:<15} : {}".format( - variable["name"], highlight_text(variable["value"]) - ) + if ( + len( + deployment.get("substrate_configuration", {}).get( + "element_list", [] ) - elif variable["type"] == "SECRET": - click.echo( - "\t \t \t \t {:<15} : {}".format( - variable["name"], highlight_text("********") - ) + ) + <= i + ): + continue + for variable in deployment["substrate_configuration"]["element_list"][ + i + ].get("variable_list", []): + if variable["name"] == "platform_data": + click.echo("\t \t VM Details") + echo_formatted("Configuration", "", True) + + value_dict = json.loads(variable.get("value", "{}")) + + echo_formatted("Name", value_dict.get("name", "")) + echo_formatted( + "IP Address", + value_dict.get("networkInterfaces", [{}])[0] + .get("accessConfigs", [{}])[0] + .get("natIP", ""), ) - else: - click.echo("\t \t \t \t {:<15}".format(variable["name"])) + echo_formatted( + "Zone", value_dict.get("zone", "").split("/")[-1] + ) + echo_formatted( + "Machine Type", + value_dict.get("machineType", "").split("/")[-1], + ) + echo_formatted("Status", value_dict.get("status", "")) + echo_formatted( + "Creation Time", value_dict.get("creationTimestamp", "") + ) + echo_formatted( + "Network Tags", + ", ".join(value_dict.get("tags", {}).get("items", [])), + ) + + labels = value_dict.get("labels", {}) + echo_formatted("TAGS ", f"({len(labels)})", True) + for key, value in labels.items(): + echo_formatted(key, value) + + else: + pass + + service_list = deployment.get("service_list", []) + if len(service_list) > 0: + element_list = service_list[0].get("element_list", []) + if i < len(element_list): + variable_list = element_list[i].get("variable_list", []) + if len(variable_list) > 0: + click.echo("\t \t Variables") + for variable in variable_list: + var_value = variable.get("value", "") + var_name = variable.get("name", "") + if variable.get("attrs", {}).get("type", "") == "SECRET": + var_value = "********" + echo_formatted(var_name, var_value) + click.echo(" ") action_list = app["status"]["resources"]["action_list"] diff --git a/calm/dsl/cli/bps.py b/calm/dsl/cli/bps.py index d6a561233..d82142bc9 100644 --- a/calm/dsl/cli/bps.py +++ b/calm/dsl/cli/bps.py @@ -1706,6 +1706,28 @@ def launch_blueprint_simple( } } + # (CALM-39565) Block bp launch if: + # 1. Snapshot config present in bp but protection policy/rule not specified + # 2. And launching via ignoring runtime editables. + if runtime_editables and not patch_editables: + for _config in runtime_editables.get("snapshot_config_list", []): + for _attrs in _config.get("value", {}).get("attrs_list", []): + if not _attrs.get("app_protection_policy_reference"): + LOG.error( + "Protection policy not specified in blueprint {}".format( + blueprint_name + ) + ) + sys.exit("Protection policy not specified in blueprint") + + if not _attrs.get("app_protection_rule_reference"): + LOG.error( + "Protection rule not specified in blueprint {}".format( + blueprint_name + ) + ) + sys.exit("Protection rule not specified in blueprint") + if runtime_editables and patch_editables: runtime_editables_json = json.dumps( runtime_editables, indent=4, separators=(",", ": ") diff --git a/calm/dsl/cli/constants.py b/calm/dsl/cli/constants.py index 0bc11fcfa..9e81d585a 100644 --- a/calm/dsl/cli/constants.py +++ b/calm/dsl/cli/constants.py @@ -132,6 +132,22 @@ class STATES: UPDATING = "updating" +class ENTITY: + class KIND: + PROVIDER = "provider" + RESOURCE_TYPE = "resource_type" + + +class PROVIDER: + class STATES: + DELETED = "DELETED" + DRAFT = "DRAFT" + ACTIVE = "ACTIVE" + + class TYPE: + CUSTOM = "CUSTOM" + + class ACCOUNT: class STATES: DELETED = "DELETED" @@ -212,6 +228,7 @@ class TASK_TYPES: EXEC = "EXEC" SET_VARIABLE = "SET_VARIABLE" HTTP = "HTTP" + RT_OPERATION = "RT_OPERATION" class SCRIPT_TYPES: POWERSHELL = "npsscript" @@ -618,6 +635,17 @@ class ENTITY_FILTER_EXPRESSION_LIST: } +class ACP_3_8_1: + class ENTITY_FILTER_EXPRESSION_LIST: + COMMON = [ + { + "operator": "IN", + "left_hand_side": {"entity_type": "container"}, + "right_hand_side": {"collection": "ALL"}, + }, + ] + + class ACP_3_8_0: class ENTITY_FILTER_EXPRESSION_LIST: COMMON = [ diff --git a/calm/dsl/cli/init_command.py b/calm/dsl/cli/init_command.py index c241987bb..e2bbd5e8f 100644 --- a/calm/dsl/cli/init_command.py +++ b/calm/dsl/cli/init_command.py @@ -17,10 +17,10 @@ from calm.dsl.db import init_db_handle from calm.dsl.api import get_resource_api, get_client_handle_obj from calm.dsl.store import Cache -from calm.dsl.init import init_bp, init_runbook +from calm.dsl.init import init_bp, init_runbook, init_provider from calm.dsl.providers import get_provider_types from calm.dsl.store import Version -from calm.dsl.constants import POLICY, STRATOS, DSL_CONFIG +from calm.dsl.constants import POLICY, STRATOS, DSL_CONFIG, CLOUD_PROVIDERS from calm.dsl.builtins import file_exists from calm.dsl.api.util import get_auth_info from .main import init, set @@ -184,15 +184,34 @@ def initialize_engine( click.echo("\nHINT: To get started, follow the 3 steps below:") click.echo("1. Initialize an example blueprint DSL: calm init bp") click.echo( - "2. Create and validate the blueprint: calm create bp --file HelloBlueprint/blueprint.py" + "2. Add vm image details according to your use in generated HelloBlueprint/blueprint.py" ) click.echo( - "3. Start an application using the blueprint: calm launch bp Hello --app_name HelloApp01 -i" + "3. Create and validate the blueprint: calm create bp --file HelloBlueprint/blueprint.py" + ) + click.echo( + "4. Start an application using the blueprint: calm launch bp Hello --app_name HelloApp01 -i" ) click.echo("\nKeep Calm and DSL On!\n") +def _fetch_cp_feature_status(client): + """ + Fetch custom provider feature status + """ + Obj = get_resource_api( + "features/custom_provider/status", client.connection, calm_api=True + ) + res, err = Obj.read() + if err: + click.echo("[Fail]") + raise Exception("[{}] - {}".format(err["code"], err["error"])) + + result = json.loads(res.content) + return result.get("status", {}).get("feature_status", {}).get("is_enabled", False) + + def set_server_details( ip, port, @@ -316,6 +335,13 @@ def set_server_details( LOG.debug("Stratos is not supported") stratos_status = False + if LV(calm_version) >= LV(CLOUD_PROVIDERS.MIN_SUPPORTED_VERSION): + cp_status = _fetch_cp_feature_status(client) + LOG.info("CP enabled={}".format(cp_status)) + else: + LOG.debug("Cloud Providers are not supported") + cp_status = False + if project_name != DSL_CONFIG.EMPTY_CONFIG_ENTITY_NAME: LOG.info("Verifying the project details") project_name_uuid_map = client.project.get_name_uuid_map( @@ -342,12 +368,13 @@ def set_server_details( config_file=config_file, db_location=db_file, local_dir=local_dir, - retries_enabled=retries_enabled, - connection_timeout=connection_timeout, - read_timeout=read_timeout, policy_status=policy_status, approval_policy_status=approval_policy_status, stratos_status=stratos_status, + retries_enabled=retries_enabled, + connection_timeout=connection_timeout, + read_timeout=read_timeout, + cp_status=cp_status, ) # Updating context for using latest config data @@ -413,6 +440,23 @@ def init_dsl_runbook(runbook_name, dir_name): init_runbook(runbook_name, dir_name) +@init.command("provider", feature_min_version="4.0.0", experimental=True) +@click.option("--name", "-n", "provider_name", default="hello", help="Name of provider") +@click.option( + "--dir_name", "-d", default=os.getcwd(), help="Directory path for the provider" +) +def init_dsl_provider(provider_name, dir_name): + """ + Creates a starting file for provider + """ + + if not provider_name.isidentifier(): + LOG.error("Provider name '{}' is not a valid identifier".format(provider_name)) + sys.exit(-1) + + init_provider(provider_name, dir_name) + + # @init.command("scheduler", feature_min_version="3.3.0", experimental=True) # @click.option("--name", "-n", "job_name", default="Hello", help="Name of job") # @click.option( @@ -637,6 +681,13 @@ def _set_config( LOG.debug("Stratos is not supported") stratos_status = False + if LV(calm_version) >= LV(CLOUD_PROVIDERS.MIN_SUPPORTED_VERSION): + cp_status = _fetch_cp_feature_status(client) + LOG.info("CP enabled={}".format(cp_status)) + else: + LOG.debug("Cloud Providers are not supported") + cp_status = False + if project_name != DSL_CONFIG.EMPTY_CONFIG_ENTITY_NAME: LOG.info("Verifying the project details") project_name_uuid_map = client.project.get_name_uuid_map( @@ -681,12 +732,13 @@ def _set_config( log_level=log_level, local_dir=local_dir, config_file=config_file, - retries_enabled=retries_enabled, - connection_timeout=connection_timeout, - read_timeout=read_timeout, policy_status=policy_status, approval_policy_status=approval_policy_status, stratos_status=stratos_status, + retries_enabled=retries_enabled, + connection_timeout=connection_timeout, + read_timeout=read_timeout, + cp_status=cp_status, ) LOG.info("Configuration changed successfully") diff --git a/calm/dsl/cli/main.py b/calm/dsl/cli/main.py index bd37dcadc..7eed7f2ba 100644 --- a/calm/dsl/cli/main.py +++ b/calm/dsl/cli/main.py @@ -51,7 +51,7 @@ default=False, help="Update cache before running command", ) -@click.version_option("3.8.1") +@click.version_option("4.0.0") @click.pass_context def main(ctx, config_file, sync): """Calm CLI @@ -441,6 +441,12 @@ def run(): pass +@main.group(cls=FeatureFlagGroup) +def test(): + """Test run provider & resource_type actions""" + pass + + @main.group(cls=FeatureFlagGroup) def watch(): """Track actions running on apps or runbook executions""" @@ -637,3 +643,9 @@ def run_script(script_type, script_file, project_name, endpoint_file): else: LOG.error("Invalid script type {}. Use one of {}".format(test_scripts_type())) sys.exit(-1) + + +@main.group(cls=FeatureFlagGroup) +def clone(): + """Clone entities""" + pass diff --git a/calm/dsl/cli/marketplace.py b/calm/dsl/cli/marketplace.py index b85985de5..a6f6af51f 100644 --- a/calm/dsl/cli/marketplace.py +++ b/calm/dsl/cli/marketplace.py @@ -18,12 +18,14 @@ from .runbooks import get_runbook, poll_action, watch_runbook from .apps import watch_app from .runlog import get_runlog_status +from .accounts import get_account from .endpoints import get_endpoint from calm.dsl.builtins.models.helper.common import get_project from .environments import get_project_environment from calm.dsl.log import get_logging_handle from calm.dsl.store import Version -from .constants import MARKETPLACE_ITEM +from .constants import MARKETPLACE_ITEM, TASKS +from calm.dsl.constants import PROVIDER LOG = get_logging_handle(__name__) APP_STATES = [ @@ -573,16 +575,17 @@ def launch_marketplace_bp( ) app_name = app_name or "Mpi-App-{}-{}".format(name, str(uuid.uuid4())[-10:]) - launch_blueprint_simple( + app_launch_state = launch_blueprint_simple( patch_editables=patch_editables, profile_name=profile_name, app_name=app_name, blueprint=bp_payload, launch_params=launch_params, ) - LOG.info("App {} creation is successful".format(app_name)) - if watch: + # if app_launch_state=True that is if blueprint launch is successful then only we will enter in watch mode + if app_launch_state and watch: + LOG.info("App {} creation is successful".format(app_name)) def display_action(screen): watch_app(app_name, screen, poll_interval=poll_interval) @@ -701,16 +704,17 @@ def launch_marketplace_item( ) app_name = app_name or "Mpi-App-{}-{}".format(name, str(uuid.uuid4())[-10:]) - launch_blueprint_simple( + app_launch_state = launch_blueprint_simple( patch_editables=patch_editables, profile_name=profile_name, app_name=app_name, blueprint=bp_payload, launch_params=launch_params, ) - LOG.info("App {} creation is successful".format(app_name)) - if watch: + # if app_launch_state=True that is if blueprint launch is successful then only we will enter in watch mode + if app_launch_state and watch: + LOG.info("App {} creation is successful".format(app_name)) def display_action(screen): watch_app(app_name, screen, poll_interval=poll_interval) @@ -829,6 +833,7 @@ def publish_bp_to_marketplace_manager( icon_file=None, projects=[], all_projects=False, + publish_without_platform_data=False, ): client = get_api_client() @@ -859,6 +864,12 @@ def publish_bp_to_marketplace_manager( ) sys.exit(-1) + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) >= LV("3.8.0"): + if publish_without_platform_data: + LOG.info("Removing platform dependent fields from blueprint") + _remove_platform_spec_data(bp_spec=bp_data["spec"]) + bp_template = { "spec": { "name": marketplace_bp_name, @@ -950,6 +961,7 @@ def publish_bp_as_new_marketplace_bp( icon_name=None, icon_file=None, all_projects=False, + publish_without_platform_data=False, ): # Search whether this marketplace item exists or not @@ -984,6 +996,7 @@ def publish_bp_as_new_marketplace_bp( icon_file=icon_file, projects=projects, all_projects=all_projects, + publish_without_platform_data=publish_without_platform_data, ) if publish_to_marketplace or auto_approve: @@ -1021,6 +1034,7 @@ def publish_bp_as_existing_marketplace_bp( icon_name=None, icon_file=None, all_projects=False, + publish_without_platform_data=False, ): LOG.info( @@ -1075,6 +1089,15 @@ def publish_bp_as_existing_marketplace_bp( ) sys.exit(-1) + CALM_VERSION = Version.get_version("Calm") + + # adding icon, projects in accordance with latest existing MPI (CALM-34004) + if LV(CALM_VERSION) >= LV("4.0.0"): + latest_mpi_data = get_latest_mpi_data(entity_results) + icon_name, projects = set_latest_mpi_data( + latest_mpi_data, projects=projects, icon_name=icon_name + ) + publish_bp_to_marketplace_manager( bp_name=bp_name, marketplace_bp_name=marketplace_bp_name, @@ -1086,6 +1109,7 @@ def publish_bp_as_existing_marketplace_bp( icon_file=icon_file, projects=projects, all_projects=all_projects, + publish_without_platform_data=publish_without_platform_data, ) if publish_to_marketplace or auto_approve: @@ -1934,6 +1958,15 @@ def publish_runbook_as_existing_marketplace_item( ) sys.exit(-1) + CALM_VERSION = Version.get_version("Calm") + + # adding icon, projects in accordance with latest existing MPI (CALM-34004) + if LV(CALM_VERSION) >= LV("4.0.0"): + latest_mpi_data = get_latest_mpi_data(entity_results) + icon_name, projects = set_latest_mpi_data( + latest_mpi_data, projects=projects, icon_name=icon_name + ) + publish_runbook_to_marketplace_manager( runbook_name=runbook_name, marketplace_item_name=marketplace_item_name, @@ -2049,7 +2082,7 @@ def execute_marketplace_runbook( }, } - patch_runbook_endpoints(client, mpi_data, payload) + patch_rb_endpoints_and_accounts(client, mpi_data, payload) if not ignore_runtime_variables: patch_runbook_runtime_editables(client, mpi_data, payload) @@ -2105,7 +2138,7 @@ def poll_runlog_status(): screen.refresh() -def patch_runbook_endpoints(client, mpi_data, payload): +def patch_rb_endpoints_and_accounts(client, mpi_data, payload): template_info = mpi_data["status"]["resources"].get("runbook_template_info", {}) runbook = template_info.get("runbook_template", {}) @@ -2126,11 +2159,20 @@ def patch_runbook_endpoints(client, mpi_data, payload): tasks = runbook["spec"]["resources"]["runbook"]["task_definition_list"] used_endpoints = [] + used_accounts = [] for task in tasks: target_name = task.get("target_any_local_reference", {}).get("name", "") if target_name: used_endpoints.append(target_name) + # TODO: Check if NDB tasks need to be skipped + if task.get("type") == TASKS.TASK_TYPES.RT_OPERATION: + account_name = ( + task.get("attrs", {}).get("account_reference", {}).get("name") + ) + if account_name: + used_accounts.append(account_name) + endpoints_description_map = {} for ep_info in runbook["spec"]["resources"].get("endpoints_information", []): ep_name = ep_info.get("endpoint_reference", {}).get("name", "") @@ -2152,7 +2194,22 @@ def patch_runbook_endpoints(client, mpi_data, payload): endpoint_id = endpoint.get("metadata", {}).get("uuid", "") endpoints_mapping[used_endpoint] = endpoint_id + if used_accounts: + LOG.info( + "Please select an account belonging to the selected project for every account used in the marketplace item." + ) + used_accounts = list(set(used_accounts)) + accounts_mapping = {} + for used_account in used_accounts: + selected_account = input("{}:".format(used_account)) + if selected_account: + account = get_account(client, selected_account) + account_uuid = account.get("metadata", {}).get("uuid", "") + accounts_mapping[used_account] = account_uuid + payload["spec"]["resources"]["endpoints_mapping"] = endpoints_mapping + payload["spec"]["resources"]["accounts_mapping"] = accounts_mapping + LOG.debug("Payload with mapped endpoints & accounts: {}".format(payload)) def patch_runbook_runtime_editables(client, mpi_data, payload): @@ -2182,3 +2239,190 @@ def patch_runbook_runtime_editables(client, mpi_data, payload): payload["spec"]["resources"]["args"] = args return payload + + +def set_field_to_null(data, key_to_set_null): + """ + Sets the specified key in the given json dict to it's equivalent null value. + Performs a recursive traversal in nested json dict to set the key to null. + + Equivalent null value for different types are: + - dict: {} + - list: [] + - int: None + - str: "" + - bool: None + - Nonetype values: None + + Args: + data (dict): Nested json dict. + key_to_set_null (str): The key whose value should be set to it's corresponding null equivalence. + + Returns: + None + """ + EQUIVALENCE_NULL = { + dict: {}, + list: [], + int: None, + str: "", + bool: None, + type(None): None, + } + + try: + if isinstance(data, dict): + + # If key is found in dict, set it to equivalent null value. + if key_to_set_null in data: + LOG.debug( + "Setting equivalence null value to {}".format(key_to_set_null) + ) + data[key_to_set_null] = EQUIVALENCE_NULL[type(data[key_to_set_null])] + + # If key is not found in dict then recursively call same function for each key in dict. + for item in data: + if item == "metadata": # ignore any modification in metadata + continue + set_field_to_null(data[item], key_to_set_null) + + elif isinstance(data, list): + + for _, item in enumerate(data): + + # If key is found in list, set it to equivalent null value. + if key_to_set_null == item: + LOG.debug( + "Setting equivalence null value to {}".format(key_to_set_null) + ) + item = EQUIVALENCE_NULL[type(item)] + + # If key is not found in list then recursively call same function for each item. + else: + set_field_to_null(item, key_to_set_null) + + else: + LOG.debug("Data is not of type dict or list".format(data)) + return + + except Exception as e: + LOG.debug( + "Error while setting equivalence null value to {}".format(key_to_set_null) + ) + LOG.debug(e) + return + + +def _remove_platform_spec_data(bp_spec): + """ + Removes platform spec data from blueprint spec before publishing to marketplace manager. + + Args: + bp_spec (dict): Blueprint spec data + + Returns: + None + """ + + VALID_SUBSTRATE_TYPES = [ + PROVIDER.TYPE.AHV, + PROVIDER.TYPE.VMWARE, + PROVIDER.TYPE.AWS, + PROVIDER.TYPE.AZURE, + PROVIDER.TYPE.GCP, + ] + + client = get_api_client() + substrate_types = [] + if not bp_spec.get("resources", {}): + LOG.debug("No resources found in blueprint spec".format(bp_spec)) + return + + substrates = bp_spec["resources"].get("substrate_definition_list", []) + + # storing all substrate types present in blueprint + for substrate in substrates: + if substrate.get("type"): + substrate_types.append(substrate["type"]) + + # creating api client object to get platform dependent fields + Obj = get_resource_api( + "platform_dependent_fields", client.connection, calm_api=True + ) + payload = {"filters": {"cloud_providers": substrate_types}} + res, err = Obj.list(payload) + + if err: + raise Exception("[{}] - {}".format(err["code"], err["error"])) + + platform_dependent_fields = res.json() or {} + + if not platform_dependent_fields: + LOG.warning("There are no platform dependent fields to remove") + return + + for substrate_type, fields in platform_dependent_fields.items(): + if substrate_type not in VALID_SUBSTRATE_TYPES: + LOG.warning( + "Platform dependent fields removal for '{}' is not supported".format( + substrate_type + ) + ) + continue + + substrates = bp_spec["resources"].get("substrate_definition_list", []) + + for substrate in substrates: + if substrate.get("type") == substrate_type: + for field in fields: + set_field_to_null(substrate.get("create_spec"), field) + + +def get_latest_mpi_data(entities): + latest_version_data = [] + latest_version = None + for entity in entities: + for _data in entity.get("data", []): + if _data.get("name", "") != "version": + continue + + # version is expected to be of format x.y.z following guide: https://semver.org/ + _version = _data["values"][0]["values"][0] + + # assigning first version as latest version + if latest_version is None: + latest_version = _version + + # check if subsequently fetched version is greater than latest version + if LV(_version) >= LV(latest_version): + latest_version_data = entity["data"] + + # store latest existing mpi data in a map for access of values + latest_existing_mpi_data_map = {} + for data in latest_version_data: + value = data.get("values", []) + if value: + latest_existing_mpi_data_map[data["name"]] = value[0].get("values", []) + + return latest_existing_mpi_data_map + + +def set_latest_mpi_data(latest_existing_mpi_data_map, **kwargs): + projects = kwargs.get("projects", []) + icon_name = kwargs.get("icon_name", None) + + client = get_api_client() + + # if project is already supplied then don't assign it from latest existing mpi data + if not projects: + projects = latest_existing_mpi_data_map.get("project_names", []) + + # if icon_name is already supplied then don't assign it from latest existing mpi data + if not icon_name: + icon_list = latest_existing_mpi_data_map.get("icon_list", []) + app_icon_name_uuid_map = client.app_icon.get_uuid_name_map() + if icon_list: + icon_uuid = json.loads(icon_list[0]).get("icon_uuid") + icon_name = app_icon_name_uuid_map[icon_uuid] if icon_uuid else None + + return icon_name, projects diff --git a/calm/dsl/cli/marketplace_bp_commands.py b/calm/dsl/cli/marketplace_bp_commands.py index 203aff7d8..983555a99 100644 --- a/calm/dsl/cli/marketplace_bp_commands.py +++ b/calm/dsl/cli/marketplace_bp_commands.py @@ -28,6 +28,7 @@ unpublish_marketplace_bp, ) from .constants import MARKETPLACE_ITEM +from .utils import FeatureDslOption APP_STATES = [ MARKETPLACE_ITEM.STATES.PENDING, @@ -353,6 +354,15 @@ def _decompile_marketplace_bp( default=False, help="Publishes bp to all projects", ) +@click.option( + "--without-platform-data", + "-wp", + "publish_without_platform_data", + is_flag=True, + default=False, + type=FeatureDslOption(feature_min_version="3.8.0"), + help="Publishes bp without platform fields.", +) def publish_bp( bp_name, name, @@ -367,6 +377,7 @@ def publish_bp( icon_name=False, icon_file=None, all_projects=False, + publish_without_platform_data=False, ): """Publish a blueprint to marketplace manager""" @@ -388,6 +399,7 @@ def publish_bp( icon_name=icon_name, icon_file=icon_file, all_projects=all_projects, + publish_without_platform_data=publish_without_platform_data, ) else: @@ -404,6 +416,7 @@ def publish_bp( icon_name=icon_name, icon_file=icon_file, all_projects=all_projects, + publish_without_platform_data=publish_without_platform_data, ) diff --git a/calm/dsl/cli/provider_commands.py b/calm/dsl/cli/provider_commands.py index d5762dd75..34d4eee99 100644 --- a/calm/dsl/cli/provider_commands.py +++ b/calm/dsl/cli/provider_commands.py @@ -1,9 +1,35 @@ import click +import json +import sys from calm.dsl.log import get_logging_handle +from calm.dsl.config import get_context -from .main import describe -from .providers import describe_custom_provider +from .main import ( + describe, + get, + create, + compile, + delete, + test, + watch, + abort, + decompile, + format, +) +from .providers import ( + describe_provider, + create_provider_from_dsl, + compile_provider_command, + get_providers, + delete_providers, + run_provider_verify_command, + watch_action_execution, + abort_action_execution, + run_resource_type_action_command, + decompile_provider, + format_provider_file, +) LOG = get_logging_handle(__name__) @@ -21,4 +47,360 @@ def _describe_provider(provider_name, out): """Describe a provider""" - describe_custom_provider(provider_name, out) + describe_provider(provider_name, out) + + +@get.command("providers", feature_min_version="4.0.0", experimental=True) +@click.option("--name", "-n", default=None, help="Search for provider by name") +@click.option( + "--filter", "filter_by", "-f", default=None, help="Filter provider by this string" +) +@click.option("--limit", "-l", default=20, help="Number of results to return") +@click.option( + "--offset", "-s", default=0, help="Offset results by the specified amount" +) +@click.option( + "--quiet", "-q", is_flag=True, default=False, help="Show only provider names" +) +@click.option( + "--all-items", "-a", is_flag=True, help="Get all items, including deleted ones" +) +@click.option( + "--out", + "-o", + "out", + type=click.Choice(["text", "json"]), + default="text", + help="output format", +) +def _get_providers(name, filter_by, limit, offset, quiet, all_items, out): + """Get providers, optionally filtered by a string""" + + get_providers(name, filter_by, limit, offset, quiet, all_items, out=out) + + +@create.command("provider", feature_min_version="4.0.0", experimental=True) +@click.option( + "--file", + "-f", + "provider_file", + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + required=True, + help="Path of Provider file to upload", +) +@click.option("--name", "-n", default=None, help="Provider name (Optional)") +@click.option( + "--force", + "-fc", + is_flag=True, + default=False, + help="Deletes existing provider with the same name before create.", +) +@click.option( + "--icon-file", + "-I", + "icon_file", + default=None, + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + help="Path of image to be uploaded as provider icon", +) +@click.option( + "--icon-name", + "-i", + default=None, + help="Provider icon name", +) +@click.option( + "--passphrase", + "-ps", + "passphrase", + default=None, + help="Passphrase for the encrypted secret values in provider", +) +def _create_provider( + provider_file, + name, + force, + icon_name=None, + icon_file=None, + passphrase=None, +): + """ + Creates a custom provider + """ + + if provider_file.endswith(".py"): + res, err = create_provider_from_dsl( + provider_file, + name=name, + force_create=force, + icon_name=icon_name, + icon_file=icon_file, + passphrase=passphrase, + ) + else: + LOG.error("Unknown file format {}".format(provider_file)) + return + + if err: + LOG.error(err["error"]) + return + + provider = res.json() + + provider_name = provider["metadata"]["name"] + provider_uuid = provider["metadata"]["uuid"] + provider_status = provider.get("status", {}) + provider_state = provider_status.get("state", "DRAFT") + LOG.debug("Provider {} has state: {}".format(provider_name, provider_state)) + + if provider_state == "DRAFT": + msg_list = provider_status.get("message_list", []) + + if not msg_list: + LOG.error("Provider {} created with errors.".format(provider_name)) + LOG.debug(json.dumps(provider_status)) + sys.exit(-1) + + msgs = [] + for msg_dict in msg_list: + msg = "" + path = msg_dict.get("path", "") + if path: + msg = path + ": " + msgs.append(msg + msg_dict.get("message", "")) + + LOG.error( + "Provider {} created with {} error(s):".format(provider_name, len(msg_list)) + ) + click.echo("\n".join(msgs)) + sys.exit(-1) + + LOG.info("Provider {} created successfully.".format(provider_name)) + + context = get_context() + server_config = context.get_server_config() + pc_ip = server_config["pc_ip"] + pc_port = server_config["pc_port"] + link = "https://{}:{}/dm/self_service/providers/{}".format( + pc_ip, pc_port, provider_uuid + ) + stdout_dict = { + "name": provider_name, + "uuid": provider_uuid, + "link": link, + "state": provider_state, + } + click.echo(json.dumps(stdout_dict, indent=4, separators=(",", ": "))) + + +@compile.command("provider", feature_min_version="4.0.0", experimental=True) +@click.option( + "--file", + "-f", + "provider_file", + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + required=True, + help="Path of Provider file to compile", +) +@click.option( + "--out", + "-o", + "out", + type=click.Choice(["json", "yaml"]), + default="json", + help="output format", +) +def _compile_provider_command(provider_file, out): + """Compiles a DSL (Python) provider into JSON or YAML""" + compile_provider_command(provider_file, out) + + +@delete.command("providers", feature_min_version="4.0.0", experimental=True) +@click.argument("provider_names", nargs=-1) +def _delete_provider(provider_names): + """Deletes one or more providers""" + + delete_providers(provider_names) + + +@test.command("provider-verify", feature_min_version="4.0.0", experimental=True) +@click.argument("provider_name") +@click.option( + "--ignore_input_variables", + "-i", + is_flag=True, + default=False, + help="Ignore input variables and use defaults", +) +@click.option( + "--input-file", + "input_file", + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + required=False, + help="Path to python file for input variables", +) +@click.option("--watch/--no-watch", "-w", default=False, help="Watch scrolling output") +def _run_provider_verify_command( + provider_name, watch, ignore_input_variables, input_file=None +): + """Execute the verify action of Provider specified by name. All input variables will be prompted by default. When passing the 'ignore_input_variables' flag, no variables will be prompted and all default values will be used. The default values can be overridden by passing a Python file via 'input_file'. When passing a Python file, no variables will be prompted. + + \b + >: input_file: Python file consisting of variables 'variable_list' + Ex: variable_list = [ + { + "value": "", + "name": "" + } + ]""" + run_provider_verify_command( + provider_name, watch, ignore_input_variables, input_file=input_file + ) + + +@watch.command( + "provider-verify-execution", feature_min_version="4.0.0", experimental=True +) +@click.argument("runlog_uuid") +def _watch_verify_action_execution(runlog_uuid): + """Watch the verify action execution using given runlog UUID""" + + watch_action_execution(runlog_uuid) + + +@abort.command( + "provider-verify-execution", feature_min_version="4.0.0", experimental=True +) +@click.argument("runlog_uuid") +def _abort_verify_action_execution(runlog_uuid): + """Abort the verify action execution""" + abort_action_execution(runlog_uuid) + + +@test.command("resource-type-action", feature_min_version="4.0.0", experimental=True) +@click.argument("provider_name") +@click.argument("resource_type_name") +@click.argument("action_name") +@click.option( + "--ignore_input_variables", + "-i", + is_flag=True, + default=False, + help="Ignore input variables and use defaults", +) +@click.option( + "--input-file", + "input_file", + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + required=False, + help="Path to python file for input variables", +) +@click.option("--watch/--no-watch", "-w", default=False, help="Watch scrolling output") +def _run_resource_type_action_command( + provider_name, + resource_type_name, + action_name, + watch, + ignore_input_variables, + input_file=None, +): + """Execute a ResourceType action specified by name. All input variables will be prompted by default. When passing the 'ignore_input_variables' flag, no variables will be prompted and all default values will be used. The default values can be overridden by passing a Python file via 'input_file'. When passing a Python file, no variables will be prompted. + + \b + >: input_file: Python file consisting of variables 'variable_list' + Ex: variable_list = [ + { + "value": "", + "name": "" + } + ]""" + run_resource_type_action_command( + provider_name, + resource_type_name, + action_name, + watch, + ignore_input_variables, + input_file=input_file, + ) + + +@watch.command( + "resource-type-action-execution", feature_min_version="4.0.0", experimental=True +) +@click.argument("runlog_uuid") +def _watch_rt_action_execution(runlog_uuid): + """Watch the ResourceType action execution using given runlog UUID""" + + watch_action_execution(runlog_uuid) + + +@abort.command( + "resource-type-action-execution", feature_min_version="4.0.0", experimental=True +) +@click.argument("runlog_uuid") +def _abort_rt_action_execution(runlog_uuid): + """Abort the ResourceType action execution""" + abort_action_execution(runlog_uuid) + + +@decompile.command("provider", feature_min_version="4.0.0", experimental=True) +@click.argument("name", required=False) +@click.option( + "--file", + "-f", + "provider_file", + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + help="Path to Provider file", +) +@click.option( + "--with_secrets", + "-w", + is_flag=True, + default=False, + help="Interactive Mode to provide the value for secrets", +) +@click.option( + "--passphrase", + "-ps", + "passphrase", + default=None, + required=False, + help="Passphrase to import secrets", +) +@click.option( + "--prefix", + "-p", + default="", + help="Prefix used for appending to entities name(Reserved name cases)", +) +@click.option( + "--dir", + "-d", + "provider_dir", + default=None, + help="Provider directory location used for placing decompiled entities", +) +def _decompile_provider( + name, provider_file, with_secrets, prefix, provider_dir, passphrase +): + """Decompiles provider present on server or json file""" + + decompile_provider( + name, provider_file, with_secrets, prefix, provider_dir, passphrase + ) + + +@format.command("provider", feature_min_version="4.0.0", experimental=True) +@click.option( + "--file", + "-f", + "provider_file", + type=click.Path(exists=True, file_okay=True, dir_okay=False, readable=True), + required=True, + help="Path of Provider file to format", +) +def _format_provider_command(provider_file): + """Formats Provider DSL file using black""" + + format_provider_file(provider_file) diff --git a/calm/dsl/cli/providers.py b/calm/dsl/cli/providers.py index c6c756129..5ea51a9d7 100644 --- a/calm/dsl/cli/providers.py +++ b/calm/dsl/cli/providers.py @@ -1,36 +1,199 @@ +import arrow +import click +import copy +import json +import os +import pathlib import sys import time -import json +import uuid -import arrow -import click +from black import format_file_in_place, WriteBack, FileMode +from prettytable import PrettyTable +from ruamel import yaml from calm.dsl.api import get_api_client +from calm.dsl.builtins import file_exists, get_valid_identifier +from calm.dsl.builtins.models.cloud_provider import CloudProvider, CloudProviderType +from calm.dsl.builtins.models.cloud_provider_payload import ( + create_cloud_provider_payload, +) +from calm.dsl.builtins.models.helper.common import get_provider_uuid +from calm.dsl.config import get_context +from calm.dsl.constants import ENTITY_KIND, VARIABLE, CACHE +from calm.dsl.db.table_config import ResourceTypeCache, ProviderCache +from calm.dsl.log import get_logging_handle +from calm.dsl.store import Cache +from calm.dsl.tools import get_module_from_file + +from calm.dsl.decompile.decompile_render import create_provider_dir +from calm.dsl.decompile.file_handler import get_provider_dir + +# TODO: Test various supported flags for decompile command +from calm.dsl.decompile.bp_file_helper import decrypt_decompiled_secrets_file +from calm.dsl.decompile.main import init_decompile_context +from .constants import PROVIDER, ENTITY from .utils import ( + get_name_query, + get_states_filter, highlight_text, + insert_uuid, + Display, + import_var_from_file, ) - -from calm.dsl.builtins.models.helper.common import get_provider_uuid -from calm.dsl.log import get_logging_handle +from .runbooks import poll_action, watch_runbook, abort_runbook_execution +from .runlog import get_runlog_status LOG = get_logging_handle(__name__) +ICON_ALREADY_EXISTS = "Image with same name already exists" +ICON_ALREADY_EXISTS_WITH_NAME = "Image with name '{}' already exists" + + +def _prepare_var_dict(var_dict, cred_name_uuid_map): + """ + Helper function to prepare variable dict as per API format. + Does the following: + - generate and insert uuid + - for secret variables, modifies the attrs to ensure the value configured in var_dict["value"] is honored + """ + if not var_dict.get("uuid"): + var_dict["uuid"] = str(uuid.uuid4()) + + if var_dict["type"] in VARIABLE.SECRET_TYPES: + var_dict["attrs"] = { + "is_secret_modified": True, + "type": VARIABLE.SECRET_ATTRS_TYPE, + } + + # Handle shell/powershell dynamic variables + cred_ref = None + options_type = var_dict.get("options", {}).get("type") + if options_type == VARIABLE.OPTIONS.TYPE.EXEC: + cred_ref = var_dict["options"]["attrs"].get("login_credential_local_reference") + + elif options_type == VARIABLE.OPTIONS.TYPE.HTTP: + http_attrs = var_dict["options"]["attrs"] + cred_ref = http_attrs.get("authentication", {}).get( + "credential_local_reference" + ) + + # Insert uuids to header variables + headers = http_attrs.get("headers", []) + for header_dict in headers: + _prepare_var_dict(header_dict, cred_name_uuid_map) + + if cred_ref: + cred_ref["uuid"] = cred_name_uuid_map[cred_ref["name"]] + + +def create_or_fetch_icon_info(icon_name=None, icon_file=None, client=None): + """ + If icon_file is not None, upload the icon file + If icon_name is provided, use that name for uploading. + Else, extract & use the file name as icon name + Else + Check if icon with provided name exists + If so, fetch the icon reference + + Args: + icon_name (String): Name of the icon + icon_file (String): Icon file + client (Object): API client handle + Returns: + (Dict) Icon reference + Raises: + Exception if icon_file is not provided & icon with provided icon_name + does not exist + """ + if icon_name or icon_file: + if not client: + from calm.dsl.api import get_api_client + + client = get_api_client() + + icon_name = icon_name or os.path.splitext(os.path.basename(icon_file))[0] + if icon_file: + _, err = client.app_icon.upload(icon_name, icon_file) + if err and ICON_ALREADY_EXISTS in str(err): + LOG.warning( + ICON_ALREADY_EXISTS_WITH_NAME.format(icon_name) + + " & will be reused here. " + + "Instead, if the provided icon MUST be uploaded, " + + "use a different icon name for it & retry the operation" + ) + + icons_name_uuid_map = client.app_icon.get_name_uuid_map( + params={"filter": "name=={}".format(icon_name)} + ) + icon_uuid = icons_name_uuid_map.get(icon_name, None) + if not icon_uuid: + LOG.error("Icon named '{}' not found".format(icon_name)) + sys.exit(-1) + + return {"kind": ENTITY_KIND.APP_ICON, "name": icon_name, "uuid": icon_uuid} + + +def _prepare_resource_type_dict(resource_type_dict, name_uuid_map, for_create): + """ + Helper function to prepare resource_type dict as per API format. + Does the following: + - generate and insert uuids in variable & action dictionaries + """ + if not resource_type_dict: + return + + resource_type_dict["uuid"] = str(uuid.uuid4()) + schema_variables = resource_type_dict.get("schema_list", []) + for var in schema_variables: + _prepare_var_dict(var, name_uuid_map) + variables = resource_type_dict.get("variable_list", []) + for var in variables: + _prepare_var_dict(var, name_uuid_map) -def get_custom_provider(name, provider_uuid=None): + action_list = resource_type_dict.get("action_list") + if action_list: + action_list_with_uuid = copy.deepcopy(action_list) + modified_name_uuid_map = { + name: {"non_action": uuid} for name, uuid in name_uuid_map.items() + } + for index, action in enumerate(action_list): + insert_uuid( + action=action, + name_uuid_map=copy.deepcopy(modified_name_uuid_map), + action_list_with_uuid=action_list_with_uuid[index], + ) + resource_type_dict["action_list"] = action_list_with_uuid + + if for_create: + icon_name = resource_type_dict.pop("icon_name", None) + icon_file = resource_type_dict.pop("icon_file", None) + icon_reference = create_or_fetch_icon_info( + icon_name=icon_name, icon_file=icon_file + ) + if icon_reference: + resource_type_dict["icon_reference"] = icon_reference + + +def get_provider(name, provider_uuid=None, is_bulk_get=False): """returns provider get call data""" client = get_api_client() if not provider_uuid: provider_uuid = get_provider_uuid(name=name) - res, err = client.provider.read(provider_uuid) + + if is_bulk_get: + res, err = client.provider.bulk_read(provider_uuid) + else: + res, err = client.provider.read(provider_uuid) if err: raise Exception("[{}] - {}".format(err["code"], err["error"])) return res.json() -def update_custom_provider(provider_payload, name=None, updated_name=None): +def update_provider(provider_payload, name=None, updated_name=None, is_bulk_save=False): """Updates a provider""" client = get_api_client() @@ -44,7 +207,7 @@ def update_custom_provider(provider_payload, name=None, updated_name=None): provider_resources = provider_payload["spec"]["resources"] provider_name = provider_payload["spec"]["name"] - provider = get_custom_provider(name=name) + provider = get_provider(name=name) uuid = provider["metadata"]["uuid"] spec_version = provider["metadata"]["spec_version"] @@ -59,10 +222,21 @@ def update_custom_provider(provider_payload, name=None, updated_name=None): "api_version": "3.0", } + if is_bulk_save: + return client.provider.bulk_update(uuid, provider_payload) return client.provider.update(uuid, provider_payload) -def create_custom_provider(provider_payload, name): +def create_provider( + provider_payload, + name, + force_create=False, + icon_name=None, + icon_file=None, + is_bulk_save=False, + passphrase=None, + decompiled_secrets=None, +): """ creates provider @@ -74,22 +248,80 @@ def create_custom_provider(provider_payload, name): if name: provider_payload["spec"]["name"] = name + provider_payload["spec"]["resources"].get("test_account", {})["type"] = name - LOG.info("Creting provider") - - res, err = client.provider.create(provider_payload) + icon_reference = create_or_fetch_icon_info( + icon_name=icon_name, icon_file=icon_file, client=client + ) + if icon_reference: + provider_payload["spec"]["resources"]["icon_reference"] = icon_reference + provider_name = provider_payload["spec"]["name"] + provider, err = client.provider.check_if_provider_already_exists(provider_name) if err: - LOG.error(err) + LOG.error( + "Error checking if a provider with same name already exists: {}".format( + err["error"] + ) + ) sys.exit(-1) + if provider: + if not force_create: + LOG.error( + "Provider {} already exists. Use --force to first delete existing provider before create.".format( + provider_name + ) + ) + sys.exit(-1) + + # --force option used in create. Delete existing provider with same name. + provider_uuid = provider["metadata"]["uuid"] + _, err = client.provider.delete(provider_uuid) + if err: + LOG.error("Error deleting existing provider: {}".format(err["error"])) + sys.exit(-1) + delete_provider_and_associated_rts_from_cache(provider_name, provider_uuid) + LOG.debug( + "Existing provider with name '{}' force deleted".format(provider_name) + ) + + LOG.info("Creating User provider '{}'".format(provider_name)) + if passphrase and decompiled_secrets: + res, err = client.provider.upload_with_decompiled_secrets( + provider_payload, passphrase, decompiled_secrets=decompiled_secrets + ) + elif is_bulk_save: + res, err = client.provider.bulk_create(provider_payload) + else: + res, err = client.provider.create(provider_payload) - LOG.info("Provider Created") + if not err: + response_json = res.json() + LOG.info("Adding provider to cache ...") + ProviderCache.add_one_by_entity_dict(response_json) + resource_type_list = response_json["status"]["resources"].get( + "resource_type_list", [] + ) + if resource_type_list: + LOG.info("Adding resource types to cache ...") + for resource_type in resource_type_list: + ResourceTypeCache.add_one_by_entity_dict( + { + "status": { + "state": resource_type["state"], + "name": resource_type["name"], + "resources": resource_type, + }, + "metadata": {"uuid": resource_type["uuid"]}, + } + ) + return res, err -def describe_custom_provider(provider_name, out): +def describe_provider(provider_name, out): """Displays provider data""" - provider = get_custom_provider(provider_name) + provider = get_provider(provider_name, is_bulk_get=True) if out == "json": provider.pop("status", None) @@ -118,27 +350,891 @@ def describe_custom_provider(provider_name, out): ) provider_resources = provider.get("status").get("resources", {}) click.echo("Infra Type: " + highlight_text(provider_resources["infra_type"])) - click.echo("Provider Type: " + highlight_text(provider_resources["type"] + "\n")) + click.echo("Provider Type: " + highlight_text(provider_resources["type"])) - auth_schema_list = provider["spec"]["resources"]["auth_schema_list"] + if provider_resources.get("action_list", []): + click.echo( + "Verify Action: " + + highlight_text("Configured") + + " (uuid: {})".format( + highlight_text(provider_resources["action_list"][0]["uuid"]) + ) + ) + else: + click.echo("Verify Action: " + highlight_text("Not Configured")) - click.echo("Variables") - for schema_variable in auth_schema_list: - if (provider_name == "NDB") and (schema_variable["name"] == "ndb__insecure"): - continue + test_account = provider_resources.get("test_account") + if test_account: + click.echo( + "Test Account: " + + highlight_text(test_account["name"]) + + " (uuid: {})".format(highlight_text(test_account["uuid"])) + ) + else: + click.echo("Test Account: " + highlight_text("Not Configured")) + + if provider_resources.get("icon_reference"): + click.echo( + "Icon Name: " + + highlight_text(provider_resources["icon_reference"].get("name", "")) + + " (uuid: {})".format( + highlight_text(provider_resources["icon_reference"]["uuid"]) + ) + ) + else: + click.echo("Icon: " + highlight_text("Not Configured")) + + if provider_resources.get("credential_definition_list", []): + click.echo("\nCredentials: " + highlight_text("Configured")) + for cred in provider_resources["credential_definition_list"]: + click.echo( + "\tName: {}, Type: {}, Username: {} (uuid: {})".format( + highlight_text(cred["name"]), + highlight_text(cred["type"]), + highlight_text(cred["username"]), + highlight_text(cred["uuid"]), + ) + ) + else: + click.echo("Credentials: " + highlight_text("Not Configured")) + + def print_var_info(var_dict, tab_spaces=0): click.echo( - "\t{}".format( + "\t" * tab_spaces + + "{}".format( highlight_text( "{} ({}, {}, {})".format( - schema_variable["label"], - schema_variable["val_type"], - schema_variable["type"], - ( - "Mandatory" - if schema_variable["is_mandatory"] - else "Not Mandatory" - ), + var_dict["label"] or var_dict["name"], + var_dict["val_type"], + var_dict["type"], + "Mandatory" if var_dict["is_mandatory"] else "Not Mandatory", + ) + ) + ) + ) + + if provider_resources.get("endpoint_schema"): + click.echo("\n" + "Endpoint Schema") + click.echo( + "\tType: " + highlight_text(provider_resources["endpoint_schema"]["type"]) + ) + endpoint_schema_variables = provider_resources["endpoint_schema"].get( + "variable_list", [] + ) + if endpoint_schema_variables: + click.echo("\tVariables") + for schema_variable in endpoint_schema_variables: + print_var_info(schema_variable, tab_spaces=2) + + auth_schema_list = provider["spec"]["resources"]["auth_schema_list"] + click.echo("\nAuth Schema Variables") + for schema_variable in auth_schema_list: + if (provider_name == "NDB") and (schema_variable["name"] == "ndb__insecure"): + continue + print_var_info(schema_variable, tab_spaces=1) + + click.echo("\n----Resource Types Summary----") + + resource_types = provider_resources.get("resource_type_list") + if resource_types: + click.echo("\n" + "Resource Types") + for resource_type in resource_types: + click.echo( + "\tName: {} (uuid: {})".format( + highlight_text(resource_type["name"]), + highlight_text(resource_type["uuid"]), + ) + ) + tags = resource_type.get("tags") + if tags: + click.echo("\tResource Kind: {}".format(highlight_text(tags[0]))) + icon_reference = resource_type.get("icon_reference") + if icon_reference: + click.echo( + "\tIcon Name: " + + highlight_text(icon_reference.get("name", "")) + + " (uuid: {})".format(highlight_text(icon_reference["uuid"])) + ) + else: + click.echo("\tIcon: " + highlight_text("Not Configured")) + actions = resource_type.get("action_list") + if actions: + click.echo( + "\tActions: {}".format( + highlight_text(", ".join([act["name"] for act in actions])) + ) + ) + schema_variables = resource_type.get("schema_list") + if schema_variables: + click.echo( + "\tSchema variables: {}".format( + highlight_text( + ", ".join([var["name"] for var in schema_variables]) + ) + ) + ) + variables = resource_type.get("variable_list") + if variables: + click.echo( + "\tVariables: {}".format( + highlight_text(", ".join([var["name"] for var in variables])) ) ) + click.echo() + else: + click.echo("\nResource Types: " + highlight_text("Not Configured") + "\n") + + +def create_provider_from_dsl( + provider_file, + name=None, + force_create=False, + icon_name=None, + icon_file=None, + passphrase=None, +): + + provider_payload = compile_provider(provider_file, for_create=True) + + if provider_payload is None: + err_msg = "User provider not found in {}".format(provider_file) + err = {"error": err_msg, "code": -1} + return None, err + + decompiled_secrets = decrypt_decompiled_secrets_file( + pth=provider_file.rsplit("/", 1)[0] + ) + if passphrase and not decompiled_secrets: + LOG.warning("Decompiled secrets metadata not found. No need to pass passphrase") + elif not passphrase and decompiled_secrets: + LOG.warning( + "Decompiled secrets metadata found. Use `--passphrase/-ps` cli option to create provider with decompiled secrets" + ) + + return create_provider( + provider_payload, + name=name, + force_create=force_create, + icon_name=icon_name, + icon_file=icon_file, + is_bulk_save=True, + passphrase=passphrase, + decompiled_secrets=decompiled_secrets, + ) + + +def compile_provider(provider_file, for_create=False): + """Returns the compiled payload from an provider_file""" + + provider_module = get_provider_module_from_file(provider_file) + CloudProviderClass = get_provider_class_from_module(provider_module) + if CloudProviderClass is None: + return None + + CloudProviderPayload = create_cloud_provider_payload(CloudProviderClass) + cloud_provider_dict = CloudProviderPayload.get_dict() + cloud_provider_resources_dict = cloud_provider_dict.get("spec", {}).get( + "resources", {} + ) + + cred_name_uuid_map = {} + credential_list = cloud_provider_resources_dict.get( + "credential_definition_list", [] + ) + for cred in credential_list: + if not cred.get("uuid"): + cred["uuid"] = str(uuid.uuid4()) + cred_name_uuid_map[cred["name"]] = cred["uuid"] + + auth_schema_list = cloud_provider_resources_dict.get("auth_schema_list", []) + for auth_schema_var in auth_schema_list: + _prepare_var_dict(auth_schema_var, cred_name_uuid_map) + + variable_list = cloud_provider_resources_dict.get("variable_list", []) + for variable in variable_list: + _prepare_var_dict(variable, cred_name_uuid_map) + + test_account = cloud_provider_resources_dict.pop("test_account", None) + if test_account: + test_account["uuid"] = str(uuid.uuid4()) + test_account["type"] = cloud_provider_dict["spec"]["name"] + for acc_var in test_account.get("data", {}).get("variable_list", []): + _prepare_var_dict(acc_var, cred_name_uuid_map) + cloud_provider_resources_dict["test_account"] = test_account + + endpoint_schema = cloud_provider_resources_dict.get("endpoint_schema") + if endpoint_schema: + for variable in endpoint_schema.get("variable_list", []): + _prepare_var_dict(variable, cred_name_uuid_map) + else: + cloud_provider_resources_dict.pop("endpoint_schema", None) + + consolidated_action_list = [] + action_list = cloud_provider_resources_dict.get("action_list", []) + if len(action_list) == 1: + action_list_with_uuid = copy.deepcopy(action_list) + modified_name_uuid_map = { + name: {"non_action": uuid} for name, uuid in cred_name_uuid_map.items() + } + insert_uuid( # Inserting uuids in action_list + action=action_list[0], + name_uuid_map=copy.deepcopy(modified_name_uuid_map), + action_list_with_uuid=action_list_with_uuid[0], + ) + cloud_provider_dict["spec"]["resources"]["action_list"] = action_list_with_uuid + consolidated_action_list.extend(action_list_with_uuid) + + resource_type_list = cloud_provider_resources_dict.get("resource_type_list", []) + for resource_type in resource_type_list: + _prepare_resource_type_dict( + resource_type, + copy.deepcopy(cred_name_uuid_map), + for_create=for_create, + ) + consolidated_action_list.extend(resource_type["action_list"]) + + for action in consolidated_action_list: + for variable in action.get("runbook", {}).get("variable_list", []): + _prepare_var_dict(variable, cred_name_uuid_map) + + return cloud_provider_dict + + +def get_provider_module_from_file(provider_file): + """Returns Provider module given a provider dsl file (.py)""" + return get_module_from_file("calm.dsl.cloud_provider", provider_file) + + +def get_provider_class_from_module(cloud_provider_module): + """Returns provider class given a module""" + + CloudProviderClass = None + for item in dir(cloud_provider_module): + obj = getattr(cloud_provider_module, item) + if isinstance(obj, (type(CloudProvider))): + CloudProviderClass = obj + + return CloudProviderClass + + +def compile_provider_command(provider_file, out): + + provider_payload = compile_provider(provider_file) + + if provider_payload is None: + LOG.error("User provider not found in {}".format(provider_file)) + return + + if out == "json": + click.echo(json.dumps(provider_payload, indent=4, separators=(",", ": "))) + elif out == "yaml": + click.echo(yaml.dump(provider_payload, default_flow_style=False)) + else: + LOG.error("Unknown output format {} given".format(out)) + + +def get_providers(name, filter_by, limit, offset, quiet, all_items, out=None): + """Get the providers, optionally filtered by a string""" + + client = get_api_client() + ContextObj = get_context() + + params = {"length": limit, "offset": offset} + + filter_query = "" + + if name: + filter_query = get_name_query([name]) + if filter_by: + filter_query = filter_query + ";(" + filter_by + ")" + if all_items: + filter_query += get_states_filter(PROVIDER.STATES) + + if filter_query.startswith(";"): + filter_query = filter_query[1:] + + if filter_query: + params["filter"] = filter_query + + res, err = client.provider.list(params) + + if err: + server_config = ContextObj.get_server_config() + pc_ip = server_config["pc_ip"] + + LOG.warning("Cannot fetch providers from {}".format(pc_ip)) + return + + res = res.json() + total_matches = res["metadata"]["total_matches"] + if total_matches > limit: + LOG.warning( + "Displaying {} out of {} entities. Please use --limit and --offset option for more results.".format( + limit, total_matches + ) + ) + + if out == "json": + click.echo(json.dumps(res, indent=4, separators=(",", ": "))) + return + + json_rows = res["entities"] + if not json_rows: + click.echo(highlight_text("No provider found !!!\n")) + return + + if quiet: + for _row in json_rows: + row = _row["status"] + click.echo(highlight_text(row["name"])) + return + + table = PrettyTable() + table.field_names = [ + "NAME", + "PROVIDER TYPE", + "STATE", + "OWNER", + "CREATED ON", + "LAST UPDATED", + "UUID", + ] + + for _row in json_rows: + row = _row["status"] + metadata = _row["metadata"] + + creation_time = int(metadata["creation_time"]) // 1000000 + last_update_time = int(metadata["last_update_time"]) // 1000000 + if "owner_reference" in metadata: + owner_reference_name = metadata["owner_reference"]["name"] + else: + owner_reference_name = "-" + + table.add_row( + [ + highlight_text(row["name"]), + highlight_text(row["resources"]["type"]), + highlight_text(row["state"]), + highlight_text(owner_reference_name), + highlight_text(time.ctime(creation_time)), + "{}".format(arrow.get(last_update_time).humanize()), + highlight_text(metadata["uuid"]), + ] + ) + click.echo(table) + + +def delete_provider_and_associated_rts_from_cache(provider_name, provider_uuid): + try: + LOG.debug("Deleting provider '{}' from cache".format(provider_name)) + Cache.delete_one(entity_type=CACHE.ENTITY.PROVIDER, uuid=provider_uuid) + LOG.debug("Done") + except Exception as exp: + LOG.warning( + "Exception while trying to delete provider from cache: {}".format(str(exp)) + ) + + try: + LOG.debug( + "Deleting resource types corresponding to provider '{}' from cache".format( + provider_name + ) + ) + num_deleted_entities = ResourceTypeCache.delete_by_provider(provider_name) + LOG.debug("Deleted {} resource types from cache".format(num_deleted_entities)) + except Exception as exp: + LOG.warning( + "Exception while trying to delete resource types from cache: {}".format( + str(exp) + ) + ) + + +def delete_providers(provider_names): + """Deletes one or more provider""" + + client = get_api_client() + + for name in provider_names: + provider_uuid = get_provider_uuid(name=name) + res, err = client.provider.delete(provider_uuid) + if err: + LOG.error("Delete of '{}' failed with error: {}".format(name, err["error"])) + raise Exception("[{}] - {}".format(err["code"], err["error"])) + LOG.debug("Response JSON: {0}".format(res.json())) + LOG.info("{} deleted successfully".format(name)) + + # Delete from cache too + delete_provider_and_associated_rts_from_cache(name, provider_uuid) + + +def _render_action_execution( + client, screen, provider_uuid, runlog_uuid, input_data=None, watch=True +): + def poll_runlog_status(): + return client.provider.poll_action_run(provider_uuid, runlog_uuid) + + screen.refresh() + should_continue = poll_action(poll_runlog_status, get_runlog_status(screen)) + if not should_continue: + return + + res, err = client.provider.poll_action_run(provider_uuid, runlog_uuid) + if err: + raise Exception("[{}] - {}".format(err["code"], err["error"])) + + response = res.json() + runbook = response["status"]["runbook_json"]["resources"]["runbook"] + if watch: + screen.refresh() + + def poll_func(): + return client.provider.list_child_runlogs(provider_uuid, runlog_uuid) + + def output_func(runlog_uuid, child_runlog_uuid): + return client.provider.runlog_output( + provider_uuid, runlog_uuid, child_runlog_uuid + ) + + watch_runbook( + runlog_uuid, + runbook, + screen=screen, + input_data=input_data, + poll_function=poll_func, + rerun_on_failure=False, + output_function=output_func, + ) + + +def run_provider_or_resource_type_action( + screen, + entity_kind, + entity_uuid, + action, + watch, + payload={}, + provider_uuid=None, +): + """Runs action confiugred on the provider or resource_type""" + client = get_api_client() + if entity_kind == ENTITY.KIND.PROVIDER: + provider_uuid = entity_uuid + run_fn = client.provider.run + else: + run_fn = client.resource_types.run + res, err = run_fn(entity_uuid, action["uuid"], payload) + if err: + raise Exception("[{}] - {}".format(err["code"], err["error"])) + + response = res.json() + runlog_uuid = response["status"]["runlog_uuid"] + + screen.clear() + screen.print_at("Action '{}' queued for run".format(action["name"]), 0, 0) + + _render_action_execution( + client, + screen, + provider_uuid, + runlog_uuid, + input_data=payload, + watch=watch, + ) + + if not watch: + server_config = get_context().get_server_config() + run_url = ( + "https://{}:{}/console/#page/explore/calm/providers/runlogs/{}".format( + server_config["pc_ip"], server_config["pc_port"], runlog_uuid + ) + ) + screen.print_at( + "Verify action execution url: {}".format(highlight_text(run_url)), 0, 0 + ) + action_type = ( + "provider-verify" + if entity_kind == ENTITY.KIND.PROVIDER + else "resource-type-action" + ) + watch_cmd = "calm watch {}-execution {}".format(action_type, runlog_uuid) + screen.print_at( + "Command to watch the execution: {}".format(highlight_text(watch_cmd)), 0, 0 + ) + screen.refresh() + + +def parse_input_file(action, input_file): + + if file_exists(input_file) and input_file.endswith(".py"): + input_variable_list = import_var_from_file(input_file, "variable_list", []) + else: + LOG.error("Invalid input_file passed! Must be a valid and existing.py file!") + sys.exit(-1) + + args = [] + variable_list = action["runbook"].get("variable_list", []) + for variable in variable_list: + filtered_input_runtime_var = list( + filter(lambda e: e["name"] == variable.get("name"), input_variable_list) + ) + if len(filtered_input_runtime_var) == 1: + new_val = filtered_input_runtime_var[0].get("value", "") + if new_val: + args.append( + { + "name": variable.get("name"), + "value": type(variable.get("value"))(new_val), + } + ) + + return {"spec": {"args": args}} + + +def prompt_and_set_input_values(action): + args = [] + variable_list = action["runbook"].get("variable_list", []) + for variable in variable_list: + new_val = input( + "Value for Variable {} in Runbook (default value={}): ".format( + variable.get("name"), variable.get("value", "") + ) + ) + if new_val: + args.append( + { + "name": variable.get("name"), + "value": type(variable.get("value"))(new_val), + } + ) + + return {"spec": {"args": args}} + + +def get_payload(action, ignore_input_variables, input_file=None): + payload = {} + if input_file is None and not ignore_input_variables: + payload = prompt_and_set_input_values(action) + if input_file: + payload = parse_input_file(action, input_file) + return payload + + +def run_provider_verify_command( + provider_name, watch, ignore_input_variables, input_file=None +): + """Handle for run provier-verify command""" + provider = get_provider(provider_name) + provider_uuid = provider["metadata"]["uuid"] + action_list = provider["status"]["resources"]["action_list"] + if not action_list: + LOG.error("Verify action not configured on the provider") + sys.exit(-1) + + action = action_list[0] + payload = get_payload(action, ignore_input_variables, input_file=input_file) + + def render_action_execution(screen): + screen.clear() + screen.refresh() + run_provider_or_resource_type_action( + screen, ENTITY.KIND.PROVIDER, provider_uuid, action, watch, payload=payload + ) + screen.wait_for_input(10.0) + + Display.wrapper(render_action_execution, watch) + + +def watch_action_execution(runlog_uuid): + """Watch test execution of a Provider/ResourceType action""" + client = get_api_client() + + provider_uuid = get_provider_uuid_from_runlog(client, runlog_uuid) + + def render_execution_watch(screen): + screen.clear() + screen.refresh() + _render_action_execution(client, screen, provider_uuid, runlog_uuid) + screen.wait_for_input(10.0) + + Display.wrapper(render_execution_watch, True) + + +def abort_action_execution(runlog_uuid): + """Abort test execution of a Provider/ResourceType action""" + client = get_api_client() + server_config = get_context().get_server_config() + link = "https://{}:{}/console/#page/explore/calm/providers/runlogs/{}".format( + server_config["pc_ip"], server_config["pc_port"], runlog_uuid + ) + + provider_uuid = get_provider_uuid_from_runlog(client, runlog_uuid) + + def poll_func(runlog_uuid): + return client.provider.poll_action_run(provider_uuid, runlog_uuid) + + def abort_func(runlog_uuid): + return client.provider.abort(provider_uuid, runlog_uuid) + + abort_runbook_execution( + runlog_uuid, + poll_fn=poll_func, + abort_fn=abort_func, + link=link, + ) + + +def fetch_resource_type_by_name_and_provider_name(resource_type_name, provider_name): + get_provider(provider_name) # Validating if provider exists + query_payload = { + "filter": "name=={};provider_name=={};provider_type=={}".format( + resource_type_name, provider_name, PROVIDER.TYPE.CUSTOM + ) + } + client = get_api_client() + res, err = client.resource_types.list(payload=query_payload) + if err: + LOG.error("Error fetching resource_types: {}".format(err["error"])) + sys.exit(-1) + + resource_types = res.json()["entities"] + if not resource_types: + LOG.error( + "ResourceType '{}' not found under provider named '{}'".format( + resource_type_name, provider_name ) ) + sys.exit(-1) + elif len(resource_types) > 1: + LOG.error( + "Multiple ResourceTypes named '{}' found under provider '{}'".format( + resource_type_name, provider_name + ) + ) + sys.exit(-1) + return resource_types[0] + + +def run_resource_type_action_command( + provider_name, + resource_type_name, + action_name, + watch, + ignore_input_variables, + input_file=None, +): + resource_type = fetch_resource_type_by_name_and_provider_name( + resource_type_name, provider_name + ) + provider_uuid = resource_type["status"]["resources"]["provider_reference"]["uuid"] + resource_type_uuid = resource_type["metadata"]["uuid"] + action_list = resource_type["status"]["action_list"] + action = None + for act in action_list: + if act["name"] == action_name: + action = act + break + if not action: + LOG.error( + "Action named '{}' not found under ResourceType '{}'".format( + action_name, resource_type_name + ) + ) + sys.exit(-1) + + payload = get_payload(action, ignore_input_variables, input_file=input_file) + + def render_action_execution(screen): + screen.clear() + screen.refresh() + run_provider_or_resource_type_action( + screen, + ENTITY.KIND.RESOURCE_TYPE, + resource_type_uuid, + action, + watch, + payload=payload, + provider_uuid=provider_uuid, + ) + screen.wait_for_input(10.0) + + Display.wrapper(render_action_execution, watch) + + +def get_provider_from_cache(provider_name): + """ + Attempts to fetch the provider entity from cache by name. + If not found in existing cache, updates the cache implicitly & searches again + """ + provider = Cache.get_entity_data(CACHE.ENTITY.PROVIDER, provider_name) + if provider: + return provider + + LOG.debug("Updating cache & attempting again") + Cache.sync_table(CACHE.ENTITY.PROVIDER) + return Cache.get_entity_data(CACHE.ENTITY.PROVIDER, provider_name) + + +def decompile_provider( + name, + provider_file, + with_secrets=False, + prefix="", + provider_dir=None, + passphrase=None, +): + """helper to decompile provider""" + + if name and provider_file: + LOG.error( + "Please provide either provider file location or server provider name" + ) + sys.exit(-1) + + if name: + decompile_provider_from_server( + name=name, + with_secrets=with_secrets, + prefix=prefix, + provider_dir=provider_dir, + passphrase=passphrase, + ) + + elif provider_file: + decompile_provider_from_file( + filename=provider_file, + with_secrets=with_secrets, + prefix=prefix, + provider_dir=provider_dir, + passphrase=passphrase, + ) + + else: + LOG.error( + "Please provide either provider file location or server provider name" + ) + sys.exit(-1) + + +def decompile_provider_from_server( + name, with_secrets=False, prefix="", provider_dir=None, passphrase=None +): + """decompiles the provider by fetching it from server, along with secrets""" + + client = get_api_client() + provider = get_provider(name) + provider_uuid = provider["metadata"]["uuid"] + + exported_provider_res, err = client.provider.export_file( + provider_uuid, passphrase=passphrase + ) + if err: + raise Exception("[{}] - {}".format(err["code"], err["error"])) + + exported_provider_res_payload = exported_provider_res.json() + + _decompile_provider( + provider_payload=exported_provider_res_payload, + with_secrets=with_secrets, + prefix=prefix, + provider_dir=provider_dir, + contains_encrypted_secrets=True if passphrase else False, + ) + + +def decompile_provider_from_file( + filename, with_secrets=False, prefix="", provider_dir=None, passphrase=None +): + """decompile provider from local provider file""" + + provider_payload = json.loads(open(filename).read()) + _decompile_provider( + provider_payload=provider_payload, + with_secrets=with_secrets, + prefix=prefix, + provider_dir=provider_dir, + contains_encrypted_secrets=True if passphrase else False, + ) + + +def _decompile_provider( + provider_payload, + with_secrets=False, + prefix="", + provider_dir=None, + contains_encrypted_secrets=False, + **kwargs, +): + """decompiles the provider from payload""" + + init_decompile_context() + + provider = provider_payload["spec"]["resources"] + provider_name = provider_payload["spec"].get("name", "Dslprovider") + provider_description = provider_payload["spec"].get("description", "") + + LOG.info("Decompiling provider {}".format(provider_name)) + + prefix = get_valid_identifier(prefix) + provider_cls = CloudProviderType.decompile(provider, prefix=prefix) + provider_cls.__name__ = get_valid_identifier(provider_name) + provider_cls.__doc__ = provider_description + + create_provider_dir( + provider_cls=provider_cls, + with_secrets=with_secrets, + provider_dir=provider_dir, + contains_encrypted_secrets=contains_encrypted_secrets, + ) + click.echo( + "\nSuccessfully decompiled. Directory location: {}. provider location: {}".format( + get_provider_dir(), os.path.join(get_provider_dir(), "provider.py") + ) + ) + + +def get_provider_uuid_from_runlog(client, runlog_uuid): + """ + Given a runlog_uuid, fetches the provider_uuid corresponding to it + + Args: + client (Object): API Client object + runlog_uuid (String): Runlog UUID + Returns: + (String) Provider UUID if a valid runlog + Raises: + Exception if runlog_uuid is not a valid provider/RT-action runlog + """ + response, err = client.provider.list_runlogs( + payload={"filter": "uuid=={}".format(runlog_uuid)} + ) + if err: + LOG.error("Error while fetching runlog info: {}".format(str(err))) + sys.exit(-1) + + response_json = response.json() + entities = response_json["entities"] + if len(entities) != 1: + LOG.error( + "Runlog with UUID '{}' is not a valid provider/RT-Action runlog".format( + runlog_uuid + ) + ) + sys.exit(-1) + return entities[0]["status"]["provider_reference"]["uuid"] + + +def format_provider_file(provider_file): + path = pathlib.Path(provider_file) + LOG.debug("Formatting provider {} using black".format(path)) + if format_file_in_place( + path, fast=False, mode=FileMode(), write_back=WriteBack.DIFF + ): + LOG.info("Patching above diff to provider - {}".format(path)) + format_file_in_place( + path, fast=False, mode=FileMode(), write_back=WriteBack.YES + ) + LOG.info("All done!") + else: + LOG.info("Provider {} left unchanged.".format(path)) diff --git a/calm/dsl/cli/resource_types.py b/calm/dsl/cli/resource_types.py index cfc104fce..8ce665daf 100644 --- a/calm/dsl/cli/resource_types.py +++ b/calm/dsl/cli/resource_types.py @@ -6,7 +6,7 @@ import click from calm.dsl.api import get_api_client -from .providers import get_custom_provider +from .providers import get_provider from calm.dsl.log import get_logging_handle @@ -44,7 +44,7 @@ def update_resource_types( uuid = resource_type["metadata"]["uuid"] spec_version = resource_type["metadata"]["spec_version"] - provider = get_custom_provider(updated_name or name) + provider = get_provider(updated_name or name) resource_type_resources["provider_reference"]["uuid"] = provider["metadata"]["uuid"] diff --git a/calm/dsl/cli/runbook_commands.py b/calm/dsl/cli/runbook_commands.py index f51dd6a03..910d6acb9 100644 --- a/calm/dsl/cli/runbook_commands.py +++ b/calm/dsl/cli/runbook_commands.py @@ -16,6 +16,7 @@ pause, resume, abort, + clone, ) from .runbooks import ( get_runbook_list, @@ -32,6 +33,7 @@ resume_runbook_execution, pause_runbook_execution, abort_runbook_execution, + clone_runbook, ) LOG = get_logging_handle(__name__) @@ -291,3 +293,12 @@ def _abort_runbook_execution(runlog_uuid): """Abort the runbook execution""" abort_runbook_execution(runlog_uuid) + + +@clone.command("runbook", feature_min_version="4.0.0", experimental=True) +@click.argument("original_runbook_name", required=True) +@click.argument("cloned_runbook_name", required=True) +def _clone_runbook(original_runbook_name, cloned_runbook_name): + """Clone a runbook""" + + clone_runbook(original_runbook_name, cloned_runbook_name) diff --git a/calm/dsl/cli/runbooks.py b/calm/dsl/cli/runbooks.py index 9595244df..0f2343c53 100644 --- a/calm/dsl/cli/runbooks.py +++ b/calm/dsl/cli/runbooks.py @@ -5,6 +5,7 @@ import uuid import pathlib +from distutils.version import LooseVersion as LV from ruamel import yaml import arrow import click @@ -28,6 +29,7 @@ from calm.dsl.builtins.models.calm_ref import Ref from calm.dsl.constants import CACHE, DSL_CONFIG from calm.dsl.store import Cache +from calm.dsl.store.version import Version from calm.dsl.tools import get_module_from_file from .utils import ( Display, @@ -812,7 +814,16 @@ def poll_runlog_status(): Display.wrapper(render_runbook_execution, True) -def watch_runbook(runlog_uuid, runbook, screen, poll_interval=10, input_data={}): +def watch_runbook( + runlog_uuid, + runbook, + screen, + poll_interval=10, + input_data={}, + poll_function=None, + rerun_on_failure=True, + output_function=None, +): client = get_api_client() @@ -831,9 +842,20 @@ def poll_func(): for t in task_list: top_level_tasks.append(t.get("uuid", "")) + # For runbooks calling runbooks or runbooks calling custom provider actions + # we need to build the task_type_map for tasks present in the called actions + # as well. Since this involves multiple API calls, leaving this unimplemented for now + # Impact would be that in watch output, users will see meta tasks as well, which + # we're hiding so far in DSL. Note: Meta tasks are still seen in UI audit logs + # if task.get('type', '') == 'RT_OPERATION': + # # TODO: Build task type map for the whole RTAction + # pass + poll_action( - poll_func, - get_completion_func(screen), + poll_function or poll_func, + get_completion_func( + screen, rerun_on_failure=rerun_on_failure, output_function=output_function + ), poll_interval=poll_interval, task_type_map=task_type_map, top_level_tasks=top_level_tasks, @@ -1004,31 +1026,38 @@ def resume_runbook_execution(runlog_uuid): click.echo(json.dumps(stdout_dict, indent=4, separators=(",", ": "))) -def abort_runbook_execution(runlog_uuid): +def abort_runbook_execution(runlog_uuid, poll_fn=None, abort_fn=None, link=None): client = get_api_client() - res, err = client.runbook.poll_action_run(runlog_uuid) + if not poll_fn: + poll_fn = client.runbook.poll_action_run + res, err = poll_fn(runlog_uuid) if err: raise Exception("[{}] - {}".format(err["code"], err["error"])) + response = res.json() state = response["status"]["state"] if state in RUNLOG.TERMINAL_STATES: LOG.warning("Runbook Execution is in terminal state: {}".format(state)) sys.exit(0) - res, err = client.runbook.abort(runlog_uuid) + + if not abort_fn: + abort_fn = client.runbook.abort + res, err = abort_fn(runlog_uuid) if err: raise Exception("[{}] - {}".format(err["code"], err["error"])) response = res.json() state = response["status"]["state"] LOG.info("Abort triggered for the given runbook execution.") - ContextObj = get_context() - server_config = ContextObj.get_server_config() - pc_ip = server_config["pc_ip"] - pc_port = server_config["pc_port"] - link = "https://{}:{}/console/#page/explore/calm/runbooks/runlogs/{}".format( - pc_ip, pc_port, runlog_uuid - ) + if not link: + ContextObj = get_context() + server_config = ContextObj.get_server_config() + pc_ip = server_config["pc_ip"] + pc_port = server_config["pc_port"] + link = "https://{}:{}/console/#page/explore/calm/runbooks/runlogs/{}".format( + pc_ip, pc_port, runlog_uuid + ) stdout_dict = {"link": link, "state": state} click.echo(json.dumps(stdout_dict, indent=4, separators=(",", ": "))) @@ -1116,3 +1145,59 @@ def displayTaskNode(node, pre): ) else: click.echo("\t{}{}".format(pre, highlight_text(node.name))) + + +def clone_runbook(original_runbook_name, cloned_runbook_name): + calm_version = Version.get_version("Calm") + + if LV(calm_version) < LV("4.0.0"): + LOG.error( + "Runbook clone is supported from Calm version 4.0.0. Please upgrade your Calm version to use this feature." + ) + sys.exit("Runbook clone is supported from Calm version 4.0.0 onwards") + + client = get_api_client() + + try: + duplicate_runbook = get_runbook(client, cloned_runbook_name) + if duplicate_runbook: + LOG.error( + "Duplicate runbook name '{}' used for cloning".format( + cloned_runbook_name + ) + ) + sys.exit( + "Duplicate runbook name '{}' used for cloning".format( + cloned_runbook_name + ) + ) + except Exception as e: + if str(e).startswith("No runbook found with name"): + LOG.info("Runbook name '{}' is valid.".format(cloned_runbook_name)) + else: + LOG.error(e) + sys.exit(e) + + request_spec = { + "api_version": "3.0", + "runbook_name": cloned_runbook_name, + "metadata": { + "kind": "runbook", + "uuid": str(uuid.uuid4()), + }, + } + + LOG.info("Cloning runbook to '{}'".format(cloned_runbook_name)) + original_runbook = get_runbook(client, original_runbook_name, all=True) + runbook_res, err = client.runbook.clone( + original_runbook["metadata"]["uuid"], request_spec + ) + if err: + LOG.error("[{}] - {}".format(err["code"], err["error"])) + sys.exit(-1) + + LOG.info("Success") + runbook_res = runbook_res.json() + click.echo( + "Successfully cloned. Runbook uuid is: {}".format(runbook_res["runbook_uuid"]) + ) diff --git a/calm/dsl/cli/runlog.py b/calm/dsl/cli/runlog.py index fb743b25a..5ca0dc18e 100644 --- a/calm/dsl/cli/runlog.py +++ b/calm/dsl/cli/runlog.py @@ -341,7 +341,7 @@ def displayRunLog(screen, obj, pre, fill, line): return idx() -def get_completion_func(screen): +def get_completion_func(screen, rerun_on_failure=True, output_function=None): def is_action_complete( response, task_type_map=[], @@ -350,7 +350,6 @@ def is_action_complete( runlog_uuid=None, **kwargs, ): - client = get_api_client() global input_tasks global input_payload @@ -399,7 +398,10 @@ def is_action_complete( runlog["status"]["machine_name"] = "-" continue # this runlog corresponds to endpoint loop elif machine: - machine = "{} ({})".format(machine[1], machine[0]) + if machine[0].startswith("Machine"): # CALM-42913 + machine = machine[1] + else: + machine = "{} ({})".format(machine[1], machine[0]) if runlog["status"]["type"] == "task_runlog": @@ -413,7 +415,8 @@ def is_action_complete( and task_type_map[task_id] not in ["INPUT", "CONFIRM", "WHILE_LOOP"] ): - res, err = client.runbook.runlog_output(runlog_uuid, uuid) + output_fn = output_function or client.runbook.runlog_output + res, err = output_fn(runlog_uuid, uuid) if err: raise Exception( "\n[{}] - {}".format(err["code"], err["error"]) @@ -441,7 +444,9 @@ def is_action_complete( parent_type = parent_runlog["status"]["type"] while ( parent_type == "task_runlog" - and task_type_map[parent_runlog["status"]["task_reference"]["uuid"]] + and task_type_map.get( + parent_runlog["status"]["task_reference"]["uuid"], "" + ) == "META" ) or parent_runlog["status"].get("machine_name", None) == "-": parent_uuid = parent_runlog["status"]["parent_reference"]["uuid"] @@ -582,14 +587,15 @@ def is_action_complete( msg = "Action failed." if os.isatty(sys.stdout.fileno()): msg += " Exit screen?" - screen.play([Scene([RerunFrame(state, screen)], -1)]) - if rerun.get("rerun", False): - client.runbook.rerun(runlog_uuid) - msg = "Triggered rerun for the Runbook Runlog" - displayRunLogTree( - screen, root, completed_tasks, total_tasks, msg=msg - ) - return (False, "") + if rerun_on_failure: + screen.play([Scene([RerunFrame(state, screen)], -1)]) + if rerun.get("rerun", False): + client.runbook.rerun(runlog_uuid) + msg = "Triggered rerun for the Runbook Runlog" + displayRunLogTree( + screen, root, completed_tasks, total_tasks, msg=msg + ) + return (False, "") displayRunLogTree( screen, root, completed_tasks, total_tasks, msg=msg ) diff --git a/calm/dsl/cli/scheduler.py b/calm/dsl/cli/scheduler.py index af292a812..33392cddc 100644 --- a/calm/dsl/cli/scheduler.py +++ b/calm/dsl/cli/scheduler.py @@ -685,7 +685,7 @@ def delete_job(job_names): for job_name in job_names: - job_get_res = get_job(client, job_name, all=True) + job_get_res = get_job(client, job_name) job_uuid = job_get_res["metadata"]["uuid"] diff --git a/calm/dsl/cli/utils.py b/calm/dsl/cli/utils.py index d2ff8640e..d7ae01e68 100644 --- a/calm/dsl/cli/utils.py +++ b/calm/dsl/cli/utils.py @@ -9,7 +9,7 @@ from calm.dsl.tools import get_module_from_file from calm.dsl.api import get_api_client -from calm.dsl.constants import PROVIDER_ACCOUNT_TYPE_MAP +from calm.dsl.constants import PROVIDER_ACCOUNT_TYPE_MAP, RESOURCE_TYPE, ACTION from calm.dsl.store import Version from calm.dsl.log import get_logging_handle @@ -253,23 +253,43 @@ def get_account_details( } -def insert_uuid(action, name_uuid_map, action_list_with_uuid): +def insert_uuid(action, name_uuid_map, action_list_with_uuid, entity_key=""): """ Helper function to insert uuids in action_list """ # if action is of type list then recursively call insert_uuid for each element if isinstance(action, list): + entity_name_uuid_map = name_uuid_map + # CALM-43094 - Passing a separate map for variable_list to avoid uuid duplication in variables or other entities with same names. + if entity_key in ["variable_list", "headers", "output_variable_list"]: + entity_name_uuid_map = {} for i in range(len(action)): + # CALM-44963 - Adding uuids to each edge in attrs of dag task to retain task ordering when rendered in UI + if entity_key == "edges" and ( + "uuid" not in action_list_with_uuid[i] + or not action_list_with_uuid[i]["uuid"] + ): + action_list_with_uuid[i]["uuid"] = str(uuid.uuid4()) if isinstance(action[i], (dict, list)): - insert_uuid(action[i], name_uuid_map, action_list_with_uuid[i]) + insert_uuid(action[i], entity_name_uuid_map, action_list_with_uuid[i]) elif isinstance(action, dict): for key, value in action.items(): # if the key is name then assign a unique uuid to it if not already assigned if key == "name": + # Adding an extra condition to assign different uuids for task and action with same names. + entity_type = ( + "action" + if action.get("type", "") + in (ACTION.TYPES + RESOURCE_TYPE.ACTION_TYPES) + else "non_action" + ) if value not in name_uuid_map: - name_uuid_map[value] = str(uuid.uuid4()) + name_uuid_map[value] = {entity_type: str(uuid.uuid4())} + else: + if entity_type not in name_uuid_map[value]: + name_uuid_map[value][entity_type] = str(uuid.uuid4()) # inserting the uuid using name_uuid_map - action_list_with_uuid["uuid"] = name_uuid_map[value] + action_list_with_uuid["uuid"] = name_uuid_map[value][entity_type] elif isinstance(value, (dict, list)): - insert_uuid(value, name_uuid_map, action_list_with_uuid[key]) + insert_uuid(value, name_uuid_map, action_list_with_uuid[key], key) diff --git a/calm/dsl/config/config.ini.jinja2 b/calm/dsl/config/config.ini.jinja2 index 252ee4f75..13118c641 100644 --- a/calm/dsl/config/config.ini.jinja2 +++ b/calm/dsl/config/config.ini.jinja2 @@ -22,6 +22,9 @@ approval_policy_status = {{approval_policy_status}} [STRATOS] stratos_status = {{stratos_status}} +[CLOUD_PROVIDERS] +cp_status = {{cp_status}} + [CONNECTION] retries_enabled = {{retries_enabled}} connection_timeout = {{connection_timeout}} diff --git a/calm/dsl/config/config.py b/calm/dsl/config/config.py index ad80999a7..3a8a07972 100644 --- a/calm/dsl/config/config.py +++ b/calm/dsl/config/config.py @@ -89,6 +89,21 @@ def get_stratos_config(self): return stratos_config + def get_cp_config(self): + """returns custom provider config""" + + cp_config = {} + if "CLOUD_PROVIDERS" in self._CONFIG_PARSER_OBJECT: + for k, v in self._CONFIG_PARSER_OBJECT.items("CLOUD_PROVIDERS"): + if k == CONFIG.CLOUD_PROVIDERS.STATUS: + cp_config[k] = self._CONFIG_PARSER_OBJECT[ + "CLOUD_PROVIDERS" + ].getboolean(k) + else: + cp_config[k] = v + + return cp_config + def get_categories_config(self): """returns categories config""" @@ -137,6 +152,7 @@ def __init__(self, config_file=None): self.policy_config = config_obj.get_policy_config() self.approval_policy_config = config_obj.get_approval_policy_config() self.stratos_config = config_obj.get_stratos_config() + self.cp_config = config_obj.get_cp_config() self.categories_config = config_obj.get_categories_config() self.connection_config = config_obj.get_connection_config() @@ -164,9 +180,13 @@ def get_approval_policy_config(self): return self.approval_policy_config def get_stratos_config(self): - """returns approval policy status""" + """returns stratos status""" return self.stratos_config + def get_cp_config(self): + """returns custom provider feature status""" + return self.cp_config + def get_categories_config(self): """returns config categories""" @@ -193,12 +213,13 @@ def _render_config_template( api_key_location, project_name, log_level, - retries_enabled, - connection_timeout, - read_timeout, policy_status, approval_policy_status, stratos_status, + retries_enabled, + connection_timeout, + read_timeout, + cp_status, schema_file="config.ini.jinja2", ): """renders the config template""" @@ -214,12 +235,13 @@ def _render_config_template( api_key_location=api_key_location, project_name=project_name, log_level=log_level, - retries_enabled=retries_enabled, - connection_timeout=connection_timeout, - read_timeout=read_timeout, policy_status=policy_status, approval_policy_status=approval_policy_status, stratos_status=stratos_status, + retries_enabled=retries_enabled, + connection_timeout=connection_timeout, + read_timeout=read_timeout, + cp_status=cp_status, ) return text.strip() + os.linesep @@ -234,12 +256,13 @@ def update_config_file( api_key_location, project_name, log_level, - retries_enabled, - connection_timeout, - read_timeout, policy_status, approval_policy_status, stratos_status, + retries_enabled, + connection_timeout, + read_timeout, + cp_status, ): """Updates the config file data""" @@ -253,12 +276,13 @@ def update_config_file( api_key_location, project_name, log_level, - retries_enabled, - connection_timeout, - read_timeout, policy_status, approval_policy_status, stratos_status, + retries_enabled, + connection_timeout, + read_timeout, + cp_status, ) LOG.debug("Writing configuration to '{}'".format(config_file)) @@ -283,12 +307,13 @@ def set_dsl_config( db_location, local_dir, config_file, - retries_enabled, - connection_timeout, - read_timeout, policy_status, approval_policy_status, stratos_status, + retries_enabled, + connection_timeout, + read_timeout, + cp_status, ): """ @@ -314,10 +339,11 @@ def set_dsl_config( api_key_location=api_key_location, project_name=project_name, log_level=log_level, - retries_enabled=retries_enabled, - connection_timeout=connection_timeout, - read_timeout=read_timeout, policy_status=policy_status, approval_policy_status=approval_policy_status, stratos_status=stratos_status, + retries_enabled=retries_enabled, + connection_timeout=connection_timeout, + read_timeout=read_timeout, + cp_status=cp_status, ) diff --git a/calm/dsl/config/constants.py b/calm/dsl/config/constants.py index 950119bb7..caa56837e 100644 --- a/calm/dsl/config/constants.py +++ b/calm/dsl/config/constants.py @@ -24,6 +24,9 @@ class LOG(IterableConstants): class STRATOS(IterableConstants): STATUS = "stratos_status" + class CLOUD_PROVIDERS(IterableConstants): + STATUS = "cp_status" + class APPROVAL_POLICY(IterableConstants): STATUS = "approval_policy_status" diff --git a/calm/dsl/config/context.py b/calm/dsl/config/context.py index c34ac6f37..b9a362ae5 100644 --- a/calm/dsl/config/context.py +++ b/calm/dsl/config/context.py @@ -33,10 +33,11 @@ def initialize_configuration(self): self.project_config = config_handle.get_project_config() self.log_config = config_handle.get_log_config() self.categories_config = config_handle.get_categories_config() - self.connection_config = config_handle.get_connection_config() self.policy_config = config_handle.get_policy_config() self.approval_policy_config = config_handle.get_approval_policy_config() self.stratos_config = config_handle.get_stratos_config() + self.cp_config = config_handle.get_cp_config() + self.connection_config = config_handle.get_connection_config() # Override with env data self.server_config.update(EnvConfig.get_server_config()) self.project_config.update(EnvConfig.get_project_config()) @@ -143,6 +144,14 @@ def get_stratos_config(self): return config + def get_cp_config(self): + """returns custom-provider configuration""" + config = self.cp_config + if not config.get(CONFIG.CLOUD_PROVIDERS.STATUS): + config[CONFIG.CLOUD_PROVIDERS.STATUS] = False + + return config + def get_log_config(self): """returns logging configuration""" @@ -194,10 +203,11 @@ def print_config(self): server_config = self.get_server_config() project_config = self.get_project_config() log_config = self.get_log_config() - connection_config = self.get_connection_config() policy_config = self.get_policy_config() approval_policy_config = self.get_approval_policy_config() stratos_status = self.get_stratos_config() + cp_status = self.get_cp_config() + connection_config = self.get_connection_config() ConfigHandle = get_config_handle() config_str = ConfigHandle._render_config_template( @@ -218,6 +228,7 @@ def print_config(self): retries_enabled=connection_config[CONFIG.CONNECTION.RETRIES_ENABLED], connection_timeout=connection_config[CONFIG.CONNECTION.CONNECTION_TIMEOUT], read_timeout=connection_config[CONFIG.CONNECTION.READ_TIMEOUT], + cp_status=cp_status[CONFIG.CLOUD_PROVIDERS.STATUS], ) print(config_str) diff --git a/calm/dsl/constants.py b/calm/dsl/constants.py index f855bf498..717226510 100644 --- a/calm/dsl/constants.py +++ b/calm/dsl/constants.py @@ -120,6 +120,10 @@ class PROVIDER: NDB = "NDB" +class CLOUD_PROVIDERS: + MIN_SUPPORTED_VERSION = "4.0.0" + + class NETWORK_GROUP_TUNNEL_TASK: class STATUS: SUCCESS = "Succeeded" @@ -161,6 +165,21 @@ class TYPE: NDB = "NDB" CUSTOM_PROVIDER = "custom_provider" + STANDARD_TYPES = [ + TYPE.AHV, + TYPE.AHV_PE, + TYPE.AWS, + TYPE.AWS_C2S, + TYPE.AZURE, + TYPE.GCP, + TYPE.VMWARE, + TYPE.K8S_KARBON, + TYPE.K8S_VANILLA, + TYPE.CREDENTIAL_PROVIDER, + TYPE.CUSTOM_PROVIDER, + TYPE.NDB, + ] + class PROVIDER: class TYPE: @@ -213,6 +232,90 @@ class OPENAPI_TYPE: CUSTOM_PROVIDER = "app_custom_provider_account" +class CLOUD_PROVIDER: + "Provider constants" + + ENTITY_NAME = "CloudProvider" + + class TYPE: + CUSTOM = "CUSTOM" + + class INFRA_TYPE: + ONPREM = "on_prem" + CLOUD = "cloud" + + class ENDPOINT_KIND: + NUTANIX_PC = "NUTANIX_PC" + VMWARE = "VMWARE" + AWS = "AWS" + GCP = "GCP" + AZURE = "AZURE" + CUSTOM = "CUSTOM" + NONE = "NONE" + + INFRA_TYPES = [INFRA_TYPE.ONPREM, INFRA_TYPE.CLOUD] + VERIFY_ACTION_NAME = "Verify" + + ENDPOINT_KINDS = [ + ENDPOINT_KIND.NUTANIX_PC, + ENDPOINT_KIND.VMWARE, + ENDPOINT_KIND.AWS, + ENDPOINT_KIND.AZURE, + ENDPOINT_KIND.GCP, + ENDPOINT_KIND.CUSTOM, + ENDPOINT_KIND.NONE, + ] + + +class ACTION: + class TYPE: + SYSTEM = "system" + USER = "user" + WORKFLOW = "workflow" + FRAGMENT = "fragment" + RULE = "rule" + PATCH = "PATCH" + PROVIDER = "provider" + + TYPES = [ + TYPE.SYSTEM, + TYPE.USER, + TYPE.WORKFLOW, + TYPE.FRAGMENT, + TYPE.RULE, + TYPE.PATCH, + TYPE.PROVIDER, + ] + + +class RESOURCE_TYPE: + "ResourceType constants" + + class TYPE: + USER = "USER" + + class ACTION_TYPE: + LIST = "resource_type_list" + CREATE = "resource_type_create" + DELETE = "resource_type_delete" + GENERIC = "resource_type_generic" + + ENTITY_NAME = "ResourceType" + + ACTION_TYPES = [ + ACTION_TYPE.LIST, + ACTION_TYPE.CREATE, + ACTION_TYPE.DELETE, + ACTION_TYPE.GENERIC, + ] + + +class CREDENTIAL: + class CRED_CLASS: + STATIC = "static" + DYNAMIC = "dynamic" + + class DSL_CONFIG: EMPTY_CONFIG_ENTITY_NAME = "-" EMPTY_PROJECT_MESSAGE = "Project configuration not available. Use command `calm set config -pj ` to set it." @@ -220,6 +323,48 @@ class DSL_CONFIG: SAAS_LOGIN_WARN = "Seems like you are trying to authenticate saas instance. Please provide API key location." +class ENTITY_KIND: + "'kind' string for different entities" + APP_ICON = "app_icon" + + +class VARIABLE: + class TYPE: + LOCAL = "LOCAL" + SECRET = "SECRET" + INPUT = "INPUT" + EXTERNAL = "EXTERNAL" + SYS_LOCAL = "SYS_LOCAL" + SYS_SECRET = "SYS_SECRET" + EXEC_LOCAL = "EXEC_LOCAL" + HTTP_LOCAL = "HTTP_LOCAL" + EXEC_SECRET = "EXEC_SECRET" + HTTP_SECRET = "HTTP_SECRET" + EXEC_EXTERNAL = "EXEC_EXTERNAL" + HTTP_EXTERNAL = "HTTP_EXTERNAL" + SYS_EXEC_LOCAL = "SYS_EXEC_LOCAL" + SYS_HTTP_LOCAL = "SYS_HTTP_LOCAL" + SYS_EXEC_SECRET = "SYS_EXEC_SECRET" + SYS_HTTP_SECRET = "SYS_HTTP_SECRET" + INTERNAL_LOCAL = "INTERNAL_LOCAL" + + class OPTIONS: + class TYPE: + EXEC = "EXEC" + HTTP = "HTTP" + + SECRET_TYPES = [ + TYPE.SECRET, + TYPE.EXEC_SECRET, + TYPE.HTTP_SECRET, + TYPE.SYS_SECRET, + TYPE.SYS_EXEC_SECRET, + TYPE.SYS_HTTP_SECRET, + ] + + SECRET_ATTRS_TYPE = "SECRET" + + class SUBSTRATE: POWER_ON = "action_poweron" POWER_OFF = "action_poweroff" diff --git a/calm/dsl/db/table_config.py b/calm/dsl/db/table_config.py index f38e52403..b4ed486c4 100644 --- a/calm/dsl/db/table_config.py +++ b/calm/dsl/db/table_config.py @@ -173,6 +173,14 @@ def add_one(cls, uuid, **kwargs): "add_one helper not implemented for {} table".format(cls.get_cache_type()) ) + @classmethod + def add_one_by_entity_dict(cls, entity): + raise NotImplementedError( + "add_one_by_entity_dict method not implemented for {} table".format( + cls.get_cache_type() + ) + ) + @classmethod def delete_one(cls, uuid, **kwargs): raise NotImplementedError( @@ -436,6 +444,8 @@ class ProviderCache(CacheTableBase): infra_type = CharField() state = CharField() auth_schema_list = BlobField() + variable_list = BlobField() + endpoint_schema = BlobField() last_update_time = DateTimeField(default=datetime.datetime.now()) def get_detail_dict(self, *args, **kwargs): @@ -448,9 +458,48 @@ def get_detail_dict(self, *args, **kwargs): "parent_uuid": self.parent_uuid, "use_parent_auth": self.use_parent_auth, "auth_schema_list": json.loads(self.auth_schema_list), + "variable_list": json.loads(self.variable_list), + "endpoint_schema": json.loads(self.endpoint_schema), "last_update_time": self.last_update_time, } + @classmethod + def _get_dict_for_db_upsert(cls, entity): + return { + "name": entity["status"]["name"], + "uuid": entity["metadata"]["uuid"], + "_type": entity["status"]["resources"].get("type", ""), + "infra_type": entity["status"]["resources"].get("infra_type", ""), + "parent_uuid": entity["status"]["resources"] + .get("parent_reference", {}) + .get("uuid", ""), + "use_parent_auth": entity["status"]["resources"].get( + "use_parent_auth", False + ), + "auth_schema_list": json.dumps( + entity["status"]["resources"].get("auth_schema_list", {}) + ), + "variable_list": json.dumps( + entity["status"]["resources"].get("variable_list", {}) + ), + "endpoint_schema": json.dumps( + entity["status"]["resources"].get("endpoint_schema", {}) + ), + "state": entity["status"]["state"], + } + + @classmethod + def add_one_by_entity_dict(cls, entity): + """adds one entry to provider table""" + db_data = cls._get_dict_for_db_upsert(entity) + cls.create_entry(**db_data) + + @classmethod + def delete_one(cls, uuid, **kwargs): + """deletes one provider entity from cache""" + obj = cls.get(cls.uuid == uuid) + obj.delete_instance() + @classmethod def clear(cls): """removes entire data from table""" @@ -491,6 +540,8 @@ def create_entry(cls, name, uuid, **kwargs): use_parent_auth = kwargs.get("use_parent_auth", False) state = kwargs.get("state", "") auth_schema_list = kwargs.get("auth_schema_list", "[]") + variable_list = kwargs.get("variable_list", "[]") + endpoint_schema = kwargs.get("endpoint_schema", "{}") super().create( name=name, @@ -501,6 +552,8 @@ def create_entry(cls, name, uuid, **kwargs): state=state, use_parent_auth=use_parent_auth, auth_schema_list=auth_schema_list, + variable_list=variable_list, + endpoint_schema=endpoint_schema, ) @classmethod @@ -513,29 +566,24 @@ def sync(cls): client = get_api_client() payload = {"length": 250, "filter": ""} + ContextObj = get_context() + stratos_status = ContextObj.get_stratos_config().get("stratos_status", False) + cp_status = ContextObj.get_cp_config().get("cp_status", False) + if cp_status: + additional_fltr = "type==SYS_CUSTOM|CUSTOM|CREDENTIAL|SYS_CREDENTIAL" + if not stratos_status: # Exclude NDB provider if stratos is not enabled + additional_fltr += ";name!=NDB" + payload["filter"] = additional_fltr + elif stratos_status: + payload["filter"] = "type==SYS_CUSTOM|CUSTOM|CREDENTIAL|SYS_CREDENTIAL" + res, err = client.provider.list(payload) if err: raise Exception("[{}] - {}".format(err["code"], err["error"])) res = res.json() for entity in res.get("entities", []): - query_obj = { - "name": entity["status"]["name"], - "uuid": entity["metadata"]["uuid"], - "_type": entity["status"]["resources"].get("type", ""), - "infra_type": entity["status"]["resources"].get("infra_type", ""), - "parent_uuid": entity["status"]["resources"] - .get("parent_reference", {}) - .get("uuid", ""), - "use_parent_auth": entity["status"]["resources"].get( - "use_parent_auth", False - ), - "auth_schema_list": json.dumps( - entity["status"]["resources"].get("auth_schema_list", {}) - ), - "state": entity["status"]["state"], - } - + query_obj = cls._get_dict_for_db_upsert(entity) cls.create_entry(**query_obj) @classmethod @@ -578,6 +626,36 @@ class ResourceTypeCache(CacheTableBase): action_list = BlobField() last_update_time = DateTimeField(default=datetime.datetime.now()) + @classmethod + def _get_dict_for_db_upsert(cls, entity): + return { + "name": entity["status"]["name"], + "uuid": entity["metadata"]["uuid"], + "state": entity["status"]["state"], + "_type": entity["status"]["resources"].get("type", ""), + "tags": json.dumps(entity["status"]["resources"].get("tags", [])), + "provider_uuid": entity["status"]["resources"] + .get("provider_reference", {}) + .get("uuid", ""), + "provider_name": entity["status"]["resources"] + .get("provider_reference", {}) + .get("name", ""), + "action_list": json.dumps( + entity["status"]["resources"].get("action_list", []) + ), + } + + @classmethod + def add_one_by_entity_dict(cls, entity): + """adds one entry to provider table""" + db_data = cls._get_dict_for_db_upsert(entity) + cls.create_entry(**db_data) + + @classmethod + def delete_by_provider(cls, provider_name): + query = cls.delete().where(cls.provider_name == provider_name) + return query.execute() + def get_detail_dict(self, *args, **kwargs): return { "name": self.name, @@ -671,11 +749,20 @@ def sync(cls): } ContextObj = get_context() - stratos_config = ContextObj.get_stratos_config() - if stratos_config.get("stratos_status", False): + stratos_status = ContextObj.get_stratos_config().get("stratos_status", False) + cp_status = ContextObj.get_cp_config().get("cp_status", False) + if cp_status: + additional_fltr = ( + "provider_type==SYS_CUSTOM|CUSTOM|CREDENTIAL|SYS_CREDENTIAL" + ) + if not stratos_status: # Exclude NDB provider if stratos is not enabled + additional_fltr += ";provider_name!=NDB" + payload["filter"] = additional_fltr + + elif stratos_status: payload[ "filter" - ] += ";provider_type==SYS_CUSTOM|CUSTOM|CREDENTIAL|SYS_CREDENTIAL" + ] = "provider_type==SYS_CUSTOM|CUSTOM|CREDENTIAL|SYS_CREDENTIAL" res, err = client.resource_types.list(payload) if err: @@ -683,22 +770,7 @@ def sync(cls): res = res.json() for entity in res.get("entities", []): - query_obj = { - "name": entity["status"]["name"], - "uuid": entity["metadata"]["uuid"], - "state": entity["status"]["state"], - "_type": entity["status"]["resources"].get("type", ""), - "tags": json.dumps(entity["status"]["resources"].get("tags", [])), - "provider_uuid": entity["status"]["resources"] - .get("provider_reference", {}) - .get("uuid", ""), - "provider_name": entity["status"]["resources"] - .get("provider_reference", {}) - .get("name", ""), - "action_list": json.dumps( - entity["status"]["resources"].get("action_list", []) - ), - } + query_obj = cls._get_dict_for_db_upsert(entity) cls.create_entry(**query_obj) @classmethod diff --git a/calm/dsl/decompile/action.py b/calm/dsl/decompile/action.py index 5058dd1d6..0bad65bec 100644 --- a/calm/dsl/decompile/action.py +++ b/calm/dsl/decompile/action.py @@ -10,6 +10,7 @@ from calm.dsl.constants import SUBSTRATE from calm.dsl.log import get_logging_handle from calm.dsl.decompile.ref_dependency import get_power_action_substrate_map +from calm.dsl.decompile.decompile_helpers import modify_var_format LOG = get_logging_handle(__name__) RUNBOOK_ACTION_MAP = {} @@ -23,6 +24,8 @@ def render_action_template( secrets_dict=[], endpoints=[], ep_list=[], + credential_list=[], + rendered_credential_list=[], ): global RUNBOOK_ACTION_MAP @@ -46,7 +49,9 @@ def render_action_template( # NOTE Not using main_task_local_reference for now, # bcz type of main task is "DAG" - root_node, task_child_map = get_task_order(runbook.tasks) + root_node, task_child_map, decision_tasks, while_loop_tasks = get_task_order( + runbook.tasks + ) tasks = [] if task_child_map: @@ -59,6 +64,10 @@ def render_action_template( CONFIG_SPEC_MAP, context=runbook_context, secrets_dict=secrets_dict, + decision_tasks=decision_tasks, + while_tasks=while_loop_tasks, + credentials_list=credential_list, + rendered_credential_list=rendered_credential_list, ) ) @@ -70,6 +79,10 @@ def render_action_template( entity_context, context=runbook_context, secrets_dict=secrets_dict, + credentials_list=credential_list, + rendered_credential_list=rendered_credential_list, + endpoints=endpoints, + ep_list=ep_list, ) ) @@ -121,6 +134,22 @@ def __vm_power_on__(): "endpoints": endpoints, } + runbook_outputs = getattr(runbook, "outputs", []) + if runbook_outputs: + outputs = [] + for output in runbook.outputs: + var_template = render_variable_template( + output, + entity_context, + context=runbook_context, + variable_context="output_variable", + ) + outputs.append(modify_var_format(var_template)) + user_attrs["outputs"] = outputs + + if cls.type != "user": + user_attrs["type"] = cls.type # Only set if non-default + gui_display_name = getattr(cls, "name", "") or cls.__name__ if gui_display_name != cls.__name__: user_attrs["gui_display_name"] = gui_display_name @@ -161,6 +190,80 @@ def get_task_order(task_list): # Edges between tasks edges = dag_task.attrs["edges"] + # for decision and while_loop tasks, their subtasks are not linked with the main task tree + # populate the decision tasks. + # populate the while_loop tasks + decision_tasks = {} + decision_child_tasks = [] + + def insert_decision_task_node(task): + failure_meta_task_name = task.attrs["failure_child_reference"]["name"] + success_meta_task_name = task.attrs["success_child_reference"]["name"] + failure_meta_task = list( + filter(lambda x: x.name == failure_meta_task_name, task_list) + )[0] + success_meta_task = list( + filter(lambda x: x.name == success_meta_task_name, task_list) + )[0] + parent = task + if parent.name not in decision_tasks: + decision_tasks[parent.name] = {"data": parent} + for child_task in failure_meta_task.child_tasks_local_reference_list: + if "failure_tasks" not in decision_tasks[parent.name]: + decision_tasks[parent.name]["failure_tasks"] = [] + real_child_task = list( + filter(lambda x: x.name == child_task.name, task_list) + )[0] + decision_tasks[parent.name]["failure_tasks"].append( + {"name": child_task.name, "data": real_child_task} + ) + decision_child_tasks.append(child_task.name) + + for child_task in success_meta_task.child_tasks_local_reference_list: + if "success_tasks" not in decision_tasks[parent.name]: + decision_tasks[parent.name]["success_tasks"] = [] + real_child_task = list( + filter(lambda x: x.name == child_task.name, task_list) + )[0] + decision_tasks[parent.name]["success_tasks"].append( + {"name": child_task.name, "data": real_child_task} + ) + decision_child_tasks.append(child_task.name) + + while_loop_tasks = {} + while_loop_child_tasks = [] + + def insert_while_loop_tasks(task): + while_loop_tasks[task.name] = {"data": task, "child_tasks": []} + child_meta_task_name = task.child_tasks_local_reference_list[0].name + child_meta_task = list( + filter(lambda x: x.name == child_meta_task_name, task_list) + )[0] + child_tasks = child_meta_task.child_tasks_local_reference_list + for child_task in child_tasks: + real_child_task = list( + filter(lambda x: x.name == child_task.name, task_list) + )[0] + while_loop_tasks[task.name]["child_tasks"].append(real_child_task) + while_loop_child_tasks.append(real_child_task.name) + + for task in task_list: + if task.type == "DECISION": + insert_decision_task_node(task) + elif task.type == "WHILE_LOOP": + insert_while_loop_tasks(task) + + # remove meta tasks as they are not rendered. + task_list = list( + filter( + lambda x: ( + x.type != "META" + and (x.name not in (decision_child_tasks + while_loop_child_tasks)) + ), + task_list, + ) + ) + # Final resultant task list with level as index res_task_list = [] @@ -237,7 +340,7 @@ def get_task_order(task_list): n -= 1 - return root_node, task_child_map + return root_node, task_child_map, decision_tasks, while_loop_tasks def init_action_globals(): diff --git a/calm/dsl/decompile/cloud_provider.py b/calm/dsl/decompile/cloud_provider.py new file mode 100644 index 000000000..5c6359420 --- /dev/null +++ b/calm/dsl/decompile/cloud_provider.py @@ -0,0 +1,115 @@ +from calm.dsl.builtins.models.cloud_provider import CloudProviderType + +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.decompile.action import render_action_template +from calm.dsl.decompile.decompile_helpers import modify_var_format +from calm.dsl.decompile.render import render_template +from calm.dsl.decompile.credential import get_cred_var_name +from calm.dsl.decompile.variable import render_variable_template +from calm.dsl.log import get_logging_handle + +LOG = get_logging_handle(__name__) + + +def render_cloud_provider_template( + cls, secrets_dict, credential_list=[], rendered_credential_list=[] +): + + LOG.debug("Rendering {} provider template".format(cls.__name__)) + if not isinstance(cls, CloudProviderType): + raise TypeError("{} is not of type {}".format(cls, CloudProviderType)) + + user_attrs = cls.get_user_attrs() + user_attrs["name"] = cls.__name__ + user_attrs["description"] = cls.__doc__ or "" + + entity_context = "CloudProvider_" + cls.__name__ + auth_schema_variables = [] + for entity in user_attrs.get("auth_schema_variables", []): + var_template = render_variable_template( + entity, + entity_context, + secrets_dict=secrets_dict, + variable_context="auth_schema", + credentials_list=credential_list, + rendered_credential_list=rendered_credential_list, + ) + auth_schema_variables.append(modify_var_format(var_template)) + + provider_variables = [] + for entity in user_attrs.get("variables", []): + var_template = render_variable_template( + entity, + entity_context, + secrets_dict=secrets_dict, + credentials_list=credential_list, + rendered_credential_list=rendered_credential_list, + ) + provider_variables.append(modify_var_format(var_template)) + + endpoint_schema_variables = [] + endpoint_schema = user_attrs.get("endpoint_schema") + if endpoint_schema: + if endpoint_schema.type == PROVIDER.ENDPOINT_KIND.CUSTOM: + for entity in endpoint_schema.variables: + var_template = render_variable_template( + entity, + entity_context, + secrets_dict=secrets_dict, + variable_context="endpoint_schema", + credentials_list=credential_list, + rendered_credential_list=rendered_credential_list, + ) + endpoint_schema_variables.append(modify_var_format(var_template)) + + test_account_variables = [] + test_account = user_attrs.get("test_account") + if test_account: + for entity in test_account.variables: + var_template = render_variable_template( + entity, + entity_context, + secrets_dict=secrets_dict, + variable_context="test_account_variable", + credentials_list=credential_list, + rendered_credential_list=rendered_credential_list, + ) + test_account_variables.append(modify_var_format(var_template)) + + resource_types = [] + for resource_type in cls.resource_types: + resource_types.append(resource_type.__name__) + + action_list = [] + for action in user_attrs.get("actions", []): + action_list.append( + render_action_template( + action, + entity_context=entity_context, + secrets_dict=secrets_dict, + credential_list=credential_list, + rendered_credential_list=rendered_credential_list, + ) + ) + + credential_names = [] + for cred in cls.credentials: + credential_names.append( + get_cred_var_name(getattr(cred, "name", "") or cred.__name__) + ) + credential_names.extend([cred["name_in_file"] for cred in credential_list]) + + user_attrs.update( + { + "auth_schema_variables": auth_schema_variables, + "variables": provider_variables, + "endpoint_schema_variables": endpoint_schema_variables, + "test_account_variables": test_account_variables, + "resource_types": ", ".join(resource_types), + "credentials": ", ".join(credential_names), + "actions": action_list, + } + ) + + text = render_template("cloud_provider.py.jinja2", obj=user_attrs) + return text.strip() diff --git a/calm/dsl/decompile/cloud_provider_file_helper.py b/calm/dsl/decompile/cloud_provider_file_helper.py new file mode 100644 index 000000000..d30ce44d9 --- /dev/null +++ b/calm/dsl/decompile/cloud_provider_file_helper.py @@ -0,0 +1,160 @@ +import click +import sys +import traceback +import os + +# from io import StringIO +# import json +# import ast +# from Crypto.Cipher import AES + +from calm.dsl.builtins import get_valid_identifier +from calm.dsl.builtins.models.cloud_provider import CloudProviderType +from calm.dsl.builtins.models.resource_type import ResourceTypeEntity +from calm.dsl.decompile.bp_file_helper import encrypt_decompile_secrets +from calm.dsl.decompile.render import render_template +from calm.dsl.decompile.resource_type import render_resource_type_template +from calm.dsl.decompile.credential import render_credential_template, get_cred_files +from calm.dsl.decompile.cloud_provider import render_cloud_provider_template +from calm.dsl.decompile.variable import get_secret_variable_files +from calm.dsl.decompile.ref_dependency import update_entity_gui_dsl_name +from calm.dsl.decompile.file_handler import get_local_dir +from calm.dsl.log import get_logging_handle + +LOG = get_logging_handle(__name__) + +SECRETS_FILE_ENCRYPTION_KEY = ( + b"dslengine@calm23" # the key must be a multiple of 16 bytes +) + + +def render_cloud_provider_file_template( + cls, with_secrets=False, contains_encrypted_secrets=False +): + + if not isinstance(cls, CloudProviderType): + raise TypeError("{} is not of type {}".format(cls, CloudProviderType)) + + user_attrs = cls.get_user_attrs() + user_attrs["name"] = cls.__name__ + user_attrs["description"] = cls.__doc__ + + # Step-1: Decompile creds configured on the Provider. Since these are referred to, at + # multiple places, its crucial this happens first. + secrets_dict = [] + credential_list = [] + cred_file_dict = dict() + for _, cred in enumerate(cls.credentials): + cred_name = getattr(cred, "name", "") or cred.__name__ + cred_type = cred.type + cred_file_name = "PROVIDER_CRED_{}_{}".format( + get_valid_identifier(cred_name), cred_type + ) + + credential_list.append(render_credential_template(cred, context="PROVIDER")) + cred_file_dict[cred_file_name] = getattr(cred, "secret", "").get("value", "") + secrets_dict.append( + { + "context": "credential_definition_list." + cred_name, + "secret_name": cred_name, + "secret_value": cred_file_dict[cred_file_name], + } + ) + + # Step-2: Decompile Resource Types + entity_name_text_map = {} # Map to store the [Name: Rendered template for entity] + for resource_type in cls.resource_types: + entity_name_text_map[resource_type.get_ref().name] = resource_type + + dependepent_entities = [v for v in entity_name_text_map.values()] + for k, v in enumerate(dependepent_entities): + update_entity_gui_dsl_name(v.get_gui_name(), v.__name__) + + # Rendering templates + implied_credentials = [] + implied_credential_templates = [] + for k, v in enumerate(dependepent_entities): + if isinstance(v, ResourceTypeEntity): + dependepent_entities[k] = render_resource_type_template( + v, + secrets_dict, + credential_list=implied_credentials, + rendered_credential_list=implied_credential_templates, + ) + + # Step-3: Decompile Provider fields + provider = render_cloud_provider_template( + cls, + secrets_dict, + credential_list=implied_credentials, + rendered_credential_list=implied_credential_templates, + ) + + # Step-4: Decompile implicit credentials i.e..,. + # credentials created out of HTTP tasks/variables using basic auth + for index, cred in enumerate(implied_credentials): + cred_name = cred["name_in_file"] + cred_file_name = "{}_{}".format(cred_name, cred["type"]) + credential_list.append(implied_credential_templates[index]) + cred_file_dict[cred_file_name] = cred.get("password", {}).get("value", "") + secrets_dict.append( + { + "context": "credential_definition_list." + cred_name, + "secret_name": cred["name"], + "secret_value": cred_file_dict[cred_file_name], + } + ) + + # Getting the local files used for secrets + secret_files = get_secret_variable_files() + secret_files.extend(get_cred_files()) + + if with_secrets or contains_encrypted_secrets: + # If contains_encrypted_secrets is True then populate secrets directly from payload + # Fill the secret if flag is set + if secret_files and (not contains_encrypted_secrets): + click.secho("Enter the value to be used in secret files") + for file_name in secret_files: + if contains_encrypted_secrets: + try: + secret_val = cred_file_dict[file_name] + except KeyError: + + # Secret files corresponding to non-cred secrets. Value would have been + # already written to the file if available in the payload. No need to write again + continue + except Exception as exp: + LOG.debug("Got traceback\n{}".format(traceback.format_exc())) + LOG.error("Secret value not found due to {}".format(exp)) + sys.exit(-1) + else: + secret_val = click.prompt( + "\nValue for {}".format(file_name), + default="", + show_default=False, + hide_input=True, + ) + file_loc = os.path.join(get_local_dir(), file_name) + with open(file_loc, "w+") as fd: + fd.write(secret_val) + + is_any_secret_value_available = False + for _e in secrets_dict: + if _e.get("secret_value", ""): + is_any_secret_value_available = True + break + if is_any_secret_value_available: + LOG.info("Creating secret metadata file") + encrypt_decompile_secrets(secrets_dict=secrets_dict) + + user_attrs.update( + { + "secret_files": secret_files, + "credentials": credential_list, + "dependent_entities": dependepent_entities, + "provider": provider, + "contains_encrypted_secrets": contains_encrypted_secrets, + } + ) + text = render_template("cloud_provider_file_helper.py.jinja2", obj=user_attrs) + return text.strip() diff --git a/calm/dsl/decompile/credential.py b/calm/dsl/decompile/credential.py index e4c11aec5..58a8bf1f0 100644 --- a/calm/dsl/decompile/credential.py +++ b/calm/dsl/decompile/credential.py @@ -11,7 +11,7 @@ CRED_FILES = [] -def render_credential_template(cls): +def render_credential_template(cls, context="BP"): global CRED_VAR_NAME_MAP, CRED_FILES LOG.debug("Rendering {} credential template".format(cls.__name__)) @@ -23,7 +23,7 @@ def render_credential_template(cls): cred_type = user_attrs.get("cred_class", "") - var_name = "BP_CRED_{}".format(get_valid_identifier(cls.__name__)) + var_name = "{}_CRED_{}".format(context, get_valid_identifier(cls.__name__)) user_attrs["var_name"] = var_name if user_attrs.get("editables", {}): user_attrs["editables"] = user_attrs["editables"].get_dict() diff --git a/calm/dsl/decompile/decompile_helpers.py b/calm/dsl/decompile/decompile_helpers.py index 2c6ebd767..c56d8819c 100644 --- a/calm/dsl/decompile/decompile_helpers.py +++ b/calm/dsl/decompile/decompile_helpers.py @@ -13,6 +13,20 @@ def process_variable_name(var_name): return get_valid_identifier(var_name) +def modify_var_format(variable): + """ + Converts the variable definition from one format to other as below + + "input_var = CalmVariable.Simple('@@{runbook_var}@@', label='', is_mandatory=True, is_hidden=False, runtime=False, description='')" + + "CalmVariable.Simple('@@{runbook_var}@@', label='', is_mandatory=True, is_hidden=False, runtime=False, description='', name=input_var)" + """ + arr = variable.split("=") + ret = "=".join(arr[1:]).strip() + ret = ret[:-1] + ", name='{}')".format(arr[0].strip()) + return ret + + class IndentHelper: def generate_indents( self, special_tasks_data, task, base_indent, depth, if_needed, else_needed diff --git a/calm/dsl/decompile/decompile_render.py b/calm/dsl/decompile/decompile_render.py index 1415068a5..68c60d8ea 100644 --- a/calm/dsl/decompile/decompile_render.py +++ b/calm/dsl/decompile/decompile_render.py @@ -4,8 +4,12 @@ from calm.dsl.log import get_logging_handle from calm.dsl.decompile.bp_file_helper import render_bp_file_template from calm.dsl.decompile.runbook import render_runbook_template +from calm.dsl.decompile.cloud_provider_file_helper import ( + render_cloud_provider_file_template, +) from calm.dsl.decompile.file_handler import ( init_bp_dir, + init_provider_dir, init_runbook_dir, init_environment_dir, init_project_dir, @@ -23,6 +27,13 @@ def create_bp_file(dir_name, bp_data): fd.write(bp_data) +def create_provider_file(dir_name, provider_data): + + provider_path = os.path.join(dir_name, "provider.py") + with open(provider_path, "w") as fd: + fd.write(provider_data) + + def create_runbook_file(dir_name, runbook_data): runbook_path = os.path.join(dir_name, "runbook.py") @@ -70,6 +81,30 @@ def create_bp_dir( create_bp_file(bp_dir, bp_data) +def create_provider_dir( + provider_cls=None, + provider_dir=None, + with_secrets=False, + contains_encrypted_secrets=False, +): + + if not provider_dir: + provider_dir = os.path.join(os.getcwd(), provider_cls.__name__) + + LOG.info("Creating provider directory") + _, _, _ = init_provider_dir(provider_dir) + LOG.info("Rendering provider file template") + provider_data = render_cloud_provider_file_template( + cls=provider_cls, + with_secrets=with_secrets, + contains_encrypted_secrets=contains_encrypted_secrets, + ) + LOG.info("Formatting provider file using black") + provider_data = format_str(provider_data, mode=FileMode()) + LOG.info("Creating provider file") + create_provider_file(provider_dir, provider_data) + + def create_runbook_dir( runbook_cls=None, runbook_dir=None, diff --git a/calm/dsl/decompile/endpoint.py b/calm/dsl/decompile/endpoint.py index 2d6a188d6..10a2d8ad9 100644 --- a/calm/dsl/decompile/endpoint.py +++ b/calm/dsl/decompile/endpoint.py @@ -1,9 +1,13 @@ from calm.dsl.decompile.render import render_template from .decompile_helpers import process_variable_name +from calm.dsl.decompile.ref_dependency import get_endpoint_name def render_endpoint(ep): name = ep.name - usr_attrs = {"var_name": process_variable_name(name), "name": name} + usr_attrs = { + "var_name": get_endpoint_name(name) or process_variable_name(name), + "name": name, + } text = render_template(schema_file="endpoint.py.jinja2", obj=usr_attrs) return text.strip() diff --git a/calm/dsl/decompile/file_handler.py b/calm/dsl/decompile/file_handler.py index a70492aeb..93809bbd7 100644 --- a/calm/dsl/decompile/file_handler.py +++ b/calm/dsl/decompile/file_handler.py @@ -4,6 +4,7 @@ SCRIPTS_DIR = None SPECS_DIR = None BP_DIR = None +PROVIDER_DIR = None LOCAL_DIR_KEY = ".local" SCRIPTS_DIR_KEY = "scripts" @@ -30,6 +31,22 @@ def make_bp_dirs(bp_dir): return (bp_dir, local_dir, spec_dir, scripts_dir) +def make_provider_dirs(provider_dir): + + if not os.path.isdir(provider_dir): + os.makedirs(provider_dir) + + local_dir = os.path.join(provider_dir, LOCAL_DIR_KEY) + if not os.path.isdir(local_dir): + os.makedirs(local_dir) + + scripts_dir = os.path.join(provider_dir, SCRIPTS_DIR_KEY) + if not os.path.isdir(scripts_dir): + os.makedirs(scripts_dir) + + return (provider_dir, local_dir, scripts_dir) + + def make_runbook_dirs(runbook_dir): if not os.path.isdir(runbook_dir): @@ -94,6 +111,14 @@ def init_bp_dir(bp_dir): return (BP_DIR, LOCAL_DIR, SPECS_DIR, SCRIPTS_DIR) +def init_provider_dir(provider_dir): + + global LOCAL_DIR, SCRIPTS_DIR, PROVIDER_DIR + PROVIDER_DIR, LOCAL_DIR, SCRIPTS_DIR = make_provider_dirs(provider_dir) + + return (PROVIDER_DIR, LOCAL_DIR, SCRIPTS_DIR) + + def init_runbook_dir(runbook_dir): global LOCAL_DIR, SCRIPTS_DIR, RUNBOOK_DIR @@ -124,6 +149,10 @@ def get_bp_dir(): return BP_DIR +def get_provider_dir(): + return PROVIDER_DIR + + def get_runbook_dir(): return RUNBOOK_DIR @@ -165,8 +194,9 @@ def get_scripts_dir_key(): def init_file_globals(): - global LOCAL_DIR, SPECS_DIR, SCRIPTS_DIR, BP_DIR + global LOCAL_DIR, SPECS_DIR, SCRIPTS_DIR, BP_DIR, PROVIDER_DIR LOCAL_DIR = None SCRIPTS_DIR = None SPECS_DIR = None BP_DIR = None + PROVIDER_DIR = None diff --git a/calm/dsl/decompile/ndb.py b/calm/dsl/decompile/ndb.py index cde98ba36..a724a76f4 100644 --- a/calm/dsl/decompile/ndb.py +++ b/calm/dsl/decompile/ndb.py @@ -2,7 +2,7 @@ import json import os -from calm.dsl.db.table_config import ResourceTypeCache +from calm.dsl.db.table_config import ResourceTypeCache, AccountCache from calm.dsl.builtins.models.ndb import ( DatabaseServer, Database, @@ -15,10 +15,12 @@ HIDDEN_SUFFIX, ) from calm.dsl.builtins.models.helper import common as common_helper +from calm.dsl.builtins.models.variable import VariableType from calm.dsl.constants import CACHE from calm.dsl.store import Cache from calm.dsl.tools import get_escaped_quotes_string from calm.dsl.decompile.file_handler import get_local_dir +from calm.dsl.decompile.decompile_helpers import modify_var_format from calm.dsl.log import get_logging_handle @@ -404,54 +406,120 @@ def get_schema_file_and_user_attrs_for_postgres_create_snapshot( } -def get_schema_file_and_user_attrs(task_name, attrs, account_name): +def get_schema_file_and_user_attrs_for_rtop_task( + task_name, + resource_type_name, + account_name, + action_name, + provider_name, + inarg_list, + output_variable_map, + credentials_list=[], + rendered_credential_list=[], +): + input_variables = [] + from .variable import render_variable_template + + for variable in inarg_list: + var_cls = VariableType.decompile(variable) + var_template = render_variable_template( + var_cls, + "Task_" + task_name, + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, + ignore_cred_dereference_error=True, # CALM-47256 + ) + input_variables.append(modify_var_format(var_template)) - resource_type_name = attrs.get("resource_type_reference", {}).get("name", "") - action_name = attrs.get("action_reference", {}).get("name", "") - resource_type_cached_data = ResourceTypeCache.get_entity_data( - name=resource_type_name, provider_name="NDB" + user_attrs = { + "name": task_name, + "resource_type_name": resource_type_name, + "account_name": account_name, + "action_name": action_name, + "provider_name": provider_name, + "inarg_list": input_variables, + "output_variables": output_variable_map, + } + return "task_resource_type_action.py.jinja2", user_attrs + + +def get_schema_file_and_user_attrs( + task_name, attrs, credentials_list=[], rendered_credential_list=[] +): + account_ref_dict = attrs.get("account_reference", {}) + account_uuid = account_ref_dict.get("uuid", "") + account_name = account_ref_dict.get("name", "") + account_data = AccountCache.get_entity_data_using_uuid(account_uuid) + + # NOTE: Commenting out the following condition as of today, + # decompile for runbooks not in "ACTIVE" state is also supported + # if not account_data: + # msg = "Account with name: '{}', uuid: '{}' not found".format( + # account_name, account_uuid + # ) + # LOG.error(msg) + # sys.exit(msg) + + rt_ref_dict = attrs.get("resource_type_reference", {}) + resource_type_uuid = rt_ref_dict.get("uuid", "") + resource_type_name = rt_ref_dict.get("name", "") + resource_type_cached_data = ResourceTypeCache.get_entity_data_using_uuid( + resource_type_uuid ) - if not resource_type_cached_data: - LOG.error("resource_type not found in NDB provider") - sys.exit( - "resource_type {} not found in NDB provider".format(resource_type_name) - ) + # NOTE: Commenting out the following condition as of today, + # decompile for runbooks not in "ACTIVE" state is also supported + # if not resource_type_cached_data: + # msg = "Resource Type with name: '{}', uuid: '{}' not found for account '{}'".format( + # resource_type_name, resource_type_uuid, account_data['name'] + # ) + # LOG.error(msg) + # sys.exit(msg) - for action in resource_type_cached_data["action_list"]: + provider_name = account_data.get("provider_type", "") + action_name = attrs.get("action_reference", {}).get("name", "") + for action in resource_type_cached_data.get("action_list", []): if action_name == action["name"]: - rt_task = action["runbook"]["task_definition_list"][1]["name"] - modified_rt_task = "-".join(rt_task.lower().split()) - if ( - resource_type_cached_data["name"], - action["name"], - ) not in rt_action_class_map.keys(): - LOG.error( - "decompile for resource type {} and action {} not in rt_action_class_map".format( - resource_type_cached_data["name"], action["name"] + if provider_name == "NDB": + rt_task = action["runbook"]["task_definition_list"][1]["name"] + modified_rt_task = "-".join(rt_task.lower().split()) + if ( + resource_type_cached_data["name"], + action["name"], + ) not in rt_action_class_map.keys(): + LOG.error( + "decompile for resource type {} and action {} not in rt_action_class_map".format( + resource_type_cached_data["name"], action["name"] + ) ) + sys.exit("Decompile failed for RT_Operation task") + return rt_action_class_map[ + (resource_type_cached_data["name"], action["name"]) + ]( + task_name=task_name, + account_name=account_name, + rt_task=modified_rt_task, + inarg_list=attrs["inarg_list"], + output_vars=attrs["output_variables"], + ) + else: + return get_schema_file_and_user_attrs_for_rtop_task( + task_name, + resource_type_name, + account_name, + action_name, + provider_name, + attrs.get("inarg_list", []), + attrs.get("output_variables", None), + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, ) - sys.exit("Decompile failed for RT_Operation task") - return rt_action_class_map[ - (resource_type_cached_data["name"], action["name"]) - ]( - task_name=task_name, - account_name=account_name, - rt_task=modified_rt_task, - inarg_list=attrs["inarg_list"], - output_vars=attrs["output_variables"], - ) - LOG.error( - "No action {} found in resource type {}".format( - action_name, resource_type_cached_data["name"] - ) - ) - sys.exit( - "No action {} found in resource type {}".format( - action_name, resource_type_cached_data["name"] - ) + msg = "Action '{}' not found on resource type '{}'".format( + action_name, resource_type_name ) + LOG.error(msg) + sys.exit(msg) def create_file_from_file_name(file_name): diff --git a/calm/dsl/decompile/profile.py b/calm/dsl/decompile/profile.py index 89fa9416d..27e3f2e0c 100644 --- a/calm/dsl/decompile/profile.py +++ b/calm/dsl/decompile/profile.py @@ -96,7 +96,12 @@ def render_profile_template( for entity in user_attrs.get("variables", []): variable_list.append( render_variable_template( - entity, entity_context, secrets_dict=secrets_dict, context=context + entity, + entity_context, + secrets_dict=secrets_dict, + context=context, + endpoints=endpoints, + ep_list=ep_list, ) ) diff --git a/calm/dsl/decompile/ref.py b/calm/dsl/decompile/ref.py index 78605e4c5..dc97fd265 100644 --- a/calm/dsl/decompile/ref.py +++ b/calm/dsl/decompile/ref.py @@ -1,11 +1,12 @@ from calm.dsl.decompile.render import render_template -from calm.dsl.builtins import RefType +from calm.dsl.builtins import RefType, get_valid_identifier from calm.dsl.log import get_logging_handle from calm.dsl.decompile.ref_dependency import ( get_service_name, get_endpoint_name, get_profile_name, get_entity_gui_dsl_name, + update_endpoint_name, ) from calm.dsl.decompile.ref_dependency import get_package_name, get_deployment_name @@ -47,12 +48,16 @@ def render_ref_template(cls): if cls_name: user_attrs["name"] = cls_name elif kind == "app_endpoint": + gui_display_name = user_attrs["name"] + update_endpoint_name(gui_display_name, get_valid_identifier(user_attrs["name"])) cls_name = get_endpoint_name(user_attrs["name"]) if cls_name: user_attrs["name"] = cls_name # Updating name attribute of class - cls.name = user_attrs["name"] + # Skip updating endpoint name as already existing endpoint names should be used as it is. + if kind != "app_endpoint": + cls.name = user_attrs["name"] text = render_template(schema_file=schema_file, obj=user_attrs) return text.strip() diff --git a/calm/dsl/decompile/ref_dependency.py b/calm/dsl/decompile/ref_dependency.py index 8e58b361f..4d77ea9de 100644 --- a/calm/dsl/decompile/ref_dependency.py +++ b/calm/dsl/decompile/ref_dependency.py @@ -7,6 +7,8 @@ PACKAGE_NAME_MAP = {} DEPLOYMENT_NAME_MAP = {} POWER_ACTION_SUBSTRATE_MAP = {} +RESOURCE_TYPE_NAME_MAP = {} +ENDPOINT_NAME_MAP = {} def get_service_name(name): @@ -36,6 +38,19 @@ def update_profile_name(ui_name, dsl_name): PROFILE_NAME_MAP[ui_name] = dsl_name +def get_resource_type_name(name): + """returns the class name used for entity ref""" + + global RESOURCE_TYPE_NAME_MAP + return RESOURCE_TYPE_NAME_MAP.get(name, None) + + +def update_resource_type_name(ui_name, dsl_name): + + global RESOURCE_TYPE_NAME_MAP + RESOURCE_TYPE_NAME_MAP[ui_name] = dsl_name + + def get_entity_gui_dsl_name(ui_name): global ENTITY_GUI_DSL_NAME_MAP @@ -61,8 +76,14 @@ def update_package_name(ui_name, dsl_name): PACKAGE_NAME_MAP[ui_name] = dsl_name +def update_endpoint_name(ui_name, dsl_name): + global ENDPOINT_NAME_MAP + ENDPOINT_NAME_MAP[ui_name] = dsl_name + + def get_endpoint_name(name): - return process_variable_name(name) + global ENDPOINT_NAME_MAP + return ENDPOINT_NAME_MAP.get(name, None) def get_deployment_name(name): @@ -101,7 +122,8 @@ def update_power_action_target_substrate(runbook_name, substrate_name): def init_ref_dependency_globals(): - global SERVICE_NAME_MAP, PROFILE_NAME_MAP, ENTITY_GUI_DSL_NAME_MAP, PACKAGE_NAME_MAP, DEPLOYMENT_NAME_MAP, POWER_ACTION_SUBSTRATE_MAP + global SERVICE_NAME_MAP, PROFILE_NAME_MAP, ENTITY_GUI_DSL_NAME_MAP, ENDPOINT_NAME_MAP + global PACKAGE_NAME_MAP, DEPLOYMENT_NAME_MAP, POWER_ACTION_SUBSTRATE_MAP, RESOURCE_TYPE_NAME_MAP SERVICE_NAME_MAP = {} PROFILE_NAME_MAP = {} @@ -109,3 +131,5 @@ def init_ref_dependency_globals(): PACKAGE_NAME_MAP = {} DEPLOYMENT_NAME_MAP = {} POWER_ACTION_SUBSTRATE_MAP = {} + RESOURCE_TYPE_NAME_MAP = {} + ENDPOINT_NAME_MAP = {} diff --git a/calm/dsl/decompile/resource_type.py b/calm/dsl/decompile/resource_type.py new file mode 100644 index 000000000..fc13acd4b --- /dev/null +++ b/calm/dsl/decompile/resource_type.py @@ -0,0 +1,81 @@ +from calm.dsl.builtins.models.resource_type import ResourceTypeEntity +from calm.dsl.decompile.action import render_action_template +from calm.dsl.decompile.decompile_helpers import modify_var_format +from calm.dsl.decompile.render import render_template +from calm.dsl.decompile.ref_dependency import update_resource_type_name +from calm.dsl.decompile.variable import render_variable_template +from calm.dsl.log import get_logging_handle + +LOG = get_logging_handle(__name__) + + +def render_resource_type_template( + cls, secrets_dict, credential_list=[], rendered_credential_list=[] +): + + LOG.debug("Rendering {} resource_type template".format(cls.__name__)) + if not isinstance(cls, ResourceTypeEntity): + raise TypeError("{} is not of type {}".format(cls, ResourceTypeEntity)) + + # Entity context + entity_context = "ResourceType_" + cls.__name__ + context = "resource_type_list." + (getattr(cls, "name", "") or cls.__name__) + "." + + user_attrs = cls.get_user_attrs() + user_attrs["name"] = cls.__name__ + user_attrs["description"] = cls.__doc__ or "" + + gui_display_name = getattr(cls, "name", "") or cls.__name__ + if gui_display_name != cls.__name__: + user_attrs[ + "gui_display_name" + ] = gui_display_name # Update RT name map and gui name + + update_resource_type_name( + gui_display_name, cls.__name__ + ) # updating ui and dsl name mapping + + action_list = [] + for action in user_attrs.get("actions", []): + action_list.append( + render_action_template( + action, + entity_context, + secrets_dict=secrets_dict, + context=context, + credential_list=credential_list, + rendered_credential_list=rendered_credential_list, + ) + ) + + variable_list = [] + for entity in user_attrs.get("variables", []): + var_template = render_variable_template( + entity, + entity_context, + secrets_dict=secrets_dict, + context=context, + credentials_list=credential_list, + rendered_credential_list=rendered_credential_list, + ) + variable_list.append(modify_var_format(var_template)) + + schema_list = [] + for entity in user_attrs.get("schemas", []): + var_template = render_variable_template( + entity, + entity_context, + secrets_dict=secrets_dict, + context=context, + variable_context="schema", + credentials_list=credential_list, + rendered_credential_list=rendered_credential_list, + ) + schema_list.append(modify_var_format(var_template)) + + user_attrs["variables"] = variable_list + user_attrs["schemas"] = schema_list + user_attrs["actions"] = action_list + + text = render_template("resource_type.py.jinja2", obj=user_attrs) + return text.strip() diff --git a/calm/dsl/decompile/runbook.py b/calm/dsl/decompile/runbook.py index 8f55d7b8d..ec4ea5371 100644 --- a/calm/dsl/decompile/runbook.py +++ b/calm/dsl/decompile/runbook.py @@ -14,7 +14,10 @@ ) from calm.dsl.decompile.ndb import get_NDB_files -from calm.dsl.decompile.decompile_helpers import process_variable_name +from calm.dsl.decompile.decompile_helpers import ( + process_variable_name, + modify_var_format, +) from calm.dsl.builtins import CalmEndpoint as Endpoint from calm.dsl.builtins.models.runbook import RunbookType, runbook from calm.dsl.log import get_logging_handle @@ -65,9 +68,10 @@ def render_runbook_template( rendered_credential_list = [] credentials_list = [] + credential_names = [] for cred in credentials: rendered_credential_list.append(render_credential_template(cred)) - credentials_list.append(get_cred_var_name(cred.name)) + credential_names.append(get_cred_var_name(cred.name)) # get mapping used for rendering task_tree template root_node, task_child_map, decision_tasks, while_loop_tasks = get_task_order( @@ -83,6 +87,8 @@ def render_runbook_template( CONFIG_SPEC_MAP, decision_tasks, while_tasks=while_loop_tasks, + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, ) ) variables = [] @@ -93,6 +99,8 @@ def render_runbook_template( entity_context, credentials_list=credentials_list, rendered_credential_list=rendered_credential_list, + endpoints=endpoints, + ep_list=ep_list, ) ) secret_files = get_secret_variable_files() @@ -104,6 +112,8 @@ def render_runbook_template( if while_loop_tasks: import_status = True + credential_names.extend([cred["name_in_file"] for cred in credentials_list]) + # runbook project reference project_name = metadata_obj.project["name"] user_attrs = { @@ -111,7 +121,7 @@ def render_runbook_template( "description": runbook_cls.__doc__ or "", "secret_files": secret_files, "endpoints": endpoints, - "credentials_list": credentials_list, + "credentials_list": credential_names, "credentials": rendered_credential_list, "tasks": tasks, "variables": variables, @@ -120,6 +130,18 @@ def render_runbook_template( "default_endpoint_name": default_endpoint_name, } + runbook_outputs = getattr(runbook_cls, "outputs", []) + if runbook_outputs: + outputs = [] + for output in runbook_cls.outputs: + var_template = render_variable_template( + output, + entity_context, + variable_context="output_variable", + ) + outputs.append(modify_var_format(var_template)) + user_attrs["outputs"] = outputs + gui_display_name = getattr(runbook_cls, "name", "") or runbook_cls.__name__ if gui_display_name != runbook_cls.__name__: user_attrs["gui_display_name"] = gui_display_name diff --git a/calm/dsl/decompile/schemas/action.py.jinja2 b/calm/dsl/decompile/schemas/action.py.jinja2 index e1d6ac770..d62c8cf76 100644 --- a/calm/dsl/decompile/schemas/action.py.jinja2 +++ b/calm/dsl/decompile/schemas/action.py.jinja2 @@ -1,11 +1,18 @@ {%- macro action(obj) -%} @action -def {{obj.name}}({% if obj.gui_display_name -%}name="{{obj.gui_display_name}}"{%- endif %}): +def {{obj.name}}({% if obj.gui_display_name -%}name="{{obj.gui_display_name}}", {%- endif %}{% if obj.type -%}type="{{obj.type}}"{%- endif %}): {% if obj.description -%}"""{{obj.description}}"""{%- endif %} -{% if obj.variables or obj.tasks %} +{% if obj.variables or obj.outputs or obj.tasks %} {%- for variable in obj.variables %} {{variable | indent( width=4, first=True)}} {%- endfor %} +{% if obj.outputs %} + outputs=[ + {%- for output in obj.outputs %} + {{output}}, + {%- endfor %} + ] +{% endif %} {%- for task in obj.tasks %} {{task | indent( width=4, first=True)}} {%- endfor %} diff --git a/calm/dsl/decompile/schemas/bp_file_helper.py.jinja2 b/calm/dsl/decompile/schemas/bp_file_helper.py.jinja2 index eb55337f8..5eed75b9c 100644 --- a/calm/dsl/decompile/schemas/bp_file_helper.py.jinja2 +++ b/calm/dsl/decompile/schemas/bp_file_helper.py.jinja2 @@ -8,6 +8,7 @@ Generated blueprint DSL (.py) import json #no_qa import os #no_qa +from calm.dsl.builtins import CalmTask as CalmVarTask from calm.dsl.builtins import * #no_qa from calm.dsl.runbooks import CalmEndpoint as Endpoint diff --git a/calm/dsl/decompile/schemas/cloud_provider.py.jinja2 b/calm/dsl/decompile/schemas/cloud_provider.py.jinja2 new file mode 100644 index 000000000..ea977383c --- /dev/null +++ b/calm/dsl/decompile/schemas/cloud_provider.py.jinja2 @@ -0,0 +1,57 @@ +{% macro cloud_provider(obj) %} +class {{obj.name}}(CloudProvider): + {% if obj.description -%}"""{{obj.description}}"""{% endif %} + + infra_type = "{{obj.infra_type}}" + + auth_schema_variables=[ + {%- for variable in obj.auth_schema_variables %} + {{variable}}, + {%- endfor %} + ] + + variables=[ + {%- for variable in obj.variables %} + {{variable}}, + {%- endfor %} + ] + + {% if obj.endpoint_schema %} + endpoint_schema = ProviderEndpointSchema( + type="{{obj.endpoint_schema.type}}", + variables=[ + {%- for variable in obj.endpoint_schema_variables %} + {{variable}}, + {%- endfor %} + ] + ) + {% endif %} + + {% if obj.test_account %} + test_account = ProviderTestAccount( + {% if obj.test_account.name -%} + name="{{obj.test_account.name}}", + {% endif %} + {% if obj.test_account.description -%} + description="{{obj.test_account.description}}", + {% endif %} + variables=[ + {%- for variable in obj.test_account_variables %} + {{variable}}, + {%- endfor %} + ] + ) + {% endif %} + + {% if obj.credentials %}credentials = [{{obj.credentials}}]{% endif %} + + {% if obj.resource_types %}resource_types = [{{obj.resource_types}}]{% endif %} + +{% for action in obj.actions %} +{{action | indent( width=4, first=True)}} +{%- endfor %} + +{% endmacro %} + +{{ cloud_provider(obj) }} + \ No newline at end of file diff --git a/calm/dsl/decompile/schemas/cloud_provider_file_helper.py.jinja2 b/calm/dsl/decompile/schemas/cloud_provider_file_helper.py.jinja2 new file mode 100644 index 000000000..ed1e3407e --- /dev/null +++ b/calm/dsl/decompile/schemas/cloud_provider_file_helper.py.jinja2 @@ -0,0 +1,38 @@ +{% macro cloud_provider_file_helper(obj) %} +# THIS FILE IS AUTOMATICALLY GENERATED. +# Disclaimer: Please test this file before using in production. +""" +Generated provider DSL (.py) +""" + +import json #no_qa +import os #no_qa + +from calm.dsl.builtins import * #no_qa +from calm.dsl.builtins import CalmTask as CalmVarTask +from calm.dsl.builtins.models.task import Status, ProviderTask as CalmTask +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER, RESOURCE_TYPE +from calm.dsl.runbooks import parallel, branch + +# Secret Variables +{% if obj.contains_encrypted_secrets %} # Note: Don't modify file data, as it is the encoded secrets fetched from the server +{%- endif %} +{%- for var_file in obj.secret_files %} +{{var_file}} = read_local_file('{{var_file}}') +{%- endfor %} + +# Credentials +{%- for cred in obj.credentials %} +{{cred}} +{%- endfor %} + +# ResourceTypes +{% for entity in obj.dependent_entities %} +{{entity}} +{% endfor %} + +{{obj.provider}} + +{% endmacro %} + +{{cloud_provider_file_helper(obj)}} diff --git a/calm/dsl/decompile/schemas/http_response_handle.py.jinja2 b/calm/dsl/decompile/schemas/http_response_handle.py.jinja2 new file mode 100644 index 000000000..ef35a75aa --- /dev/null +++ b/calm/dsl/decompile/schemas/http_response_handle.py.jinja2 @@ -0,0 +1,17 @@ +{% macro success() -%} +HTTPResponseHandle.TASK_STATUS.Success +{%- endmacro %} + +{% macro failure() -%} +HTTPResponseHandle.TASK_STATUS.Failure +{%- endmacro %} + +{% macro warning() -%} +HTTPResponseHandle.TASK_STATUS.Warning +{%- endmacro %} + +{% macro response_code(obj) -%} +HTTPResponseHandle.ResponseCode( + status = {%- if obj.status == "SUCCESS" %} {{success()}}{%- endif %} {%- if obj.status == "FAILURE" %}{{failure()}}{%- endif %} {%- if obj.status == "WARNING" %}{{warning()}}{%- endif %} {% if obj.code_range_list %}, code_ranges = {{obj.code_range_list}} {%- endif %} {% if obj.code %}, code = {{obj.code}} {%- endif %} +), +{%- endmacro %} \ No newline at end of file diff --git a/calm/dsl/decompile/schemas/resource_type.py.jinja2 b/calm/dsl/decompile/schemas/resource_type.py.jinja2 new file mode 100644 index 000000000..df868c493 --- /dev/null +++ b/calm/dsl/decompile/schemas/resource_type.py.jinja2 @@ -0,0 +1,29 @@ +{%- macro resource_type(obj) %} +class {{obj.name}}(ResourceType): + {% if obj.description %}"""{{obj.description}}"""{% endif %} + + {% if obj.gui_display_name %}name = "{{obj.gui_display_name}}"{% endif %} + + {% if obj.resource_kind %}resource_kind = "{{obj.resource_kind}}"{% endif %} + + {% if obj.icon_name %}icon_name = "{{obj.icon_name}}"{% endif %} + + schemas = [ + {%- for variable in obj.schemas %} + {{variable}}, + {%- endfor %} + ] + + variables = [ + {%- for variable in obj.variables %} + {{variable}}, + {%- endfor %} + ] + +{% for action in obj.actions %} +{{action | indent( width=4, first=True)}} +{%- endfor %} + +{%- endmacro %} + +{{ resource_type(obj) }} diff --git a/calm/dsl/decompile/schemas/runbook.py.jinja2 b/calm/dsl/decompile/schemas/runbook.py.jinja2 index 7219ff128..cfaadc813 100644 --- a/calm/dsl/decompile/schemas/runbook.py.jinja2 +++ b/calm/dsl/decompile/schemas/runbook.py.jinja2 @@ -34,10 +34,17 @@ def {{obj.name}}({% if obj.credentials_list %}credentials={{obj.credentials_list {% if obj.description %} """{{obj.description}}""" {% endif %} -{%- if obj.variables or obj.tasks %} +{%- if obj.variables or obj.outputs or obj.tasks %} {%- for variable in obj.variables %} {{variable | indent( width=4, first=True)}} #noqa {%- endfor %} +{% if obj.outputs %} + outputs=[ + {%- for output in obj.outputs %} + {{output}}, + {%- endfor %} + ] +{% endif %} {%- for task in obj.tasks %} {{task | indent( width=4, first=True)}} {%- endfor %} diff --git a/calm/dsl/decompile/schemas/status_handle.py.jinja2 b/calm/dsl/decompile/schemas/status_handle.py.jinja2 new file mode 100644 index 000000000..be7c335a3 --- /dev/null +++ b/calm/dsl/decompile/schemas/status_handle.py.jinja2 @@ -0,0 +1,27 @@ +{% macro warning() -%} +StatusHandle.Result.Warning +{%- endmacro %} + +{% macro failure() -%} +StatusHandle.Status.Failure +{%- endmacro %} + +{% macro task_failure() -%} +StatusHandle.Status.TaskFailure +{%- endmacro %} + +{% macro task_status_value(value) -%} +{%- if value == "FAILURE" %}{{ failure() }}{% endif %} {%- if value == "TASK_FAILURE" %}{{ task_failure() }}{% endif %} +{%- endmacro %} + +{% macro exit_code(obj) -%} +StatusHandle.Mapping.exit_code( + values = {{obj.match_values}} {% if obj.result_status %}, result = {%- if obj.result_status == "WARNING" %}{{warning()}}{%- endif %}{%- endif %} +), +{%- endmacro %} + +{% macro task_status(obj) -%} +StatusHandle.Mapping.task_status( + values = [{%- for value in obj.match_values %}{{task_status_value(value)}}{%- endfor %}] {% if obj.result_status %}, result = {%- if obj.result_status == "WARNING" %}{{warning()}}{%- endif %}{%- endif %} +), +{%- endmacro %} \ No newline at end of file diff --git a/calm/dsl/decompile/schemas/task_decision_escript.py.jinja2 b/calm/dsl/decompile/schemas/task_decision_escript.py.jinja2 index cc7bffb00..e0c1e4a24 100644 --- a/calm/dsl/decompile/schemas/task_decision_escript.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_decision_escript.py.jinja2 @@ -1,12 +1,18 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro decision_escript_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Decision.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.Decision.escript.py2({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Decision.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.Decision.escript.py2({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Decision.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.Decision.escript.py2({{ common_details(obj) }}, cred={{obj.cred}} ) {%- else %} -CalmTask.Decision.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.Decision.escript.py2({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_decision_escript_py3.py.jinja2 b/calm/dsl/decompile/schemas/task_decision_escript_py3.py.jinja2 index faec12f11..d4031c06e 100644 --- a/calm/dsl/decompile/schemas/task_decision_escript_py3.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_decision_escript_py3.py.jinja2 @@ -1,12 +1,18 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro decision_escript_py3_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Decision.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.Decision.escript.py3({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Decision.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.Decision.escript.py3({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Decision.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.Decision.escript.py3({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Decision.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.Decision.escript.py3({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_decision_powershell.py.jinja2 b/calm/dsl/decompile/schemas/task_decision_powershell.py.jinja2 index 0f994e11b..bd3d1d6e0 100644 --- a/calm/dsl/decompile/schemas/task_decision_powershell.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_decision_powershell.py.jinja2 @@ -1,12 +1,18 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.attrs.connection_protocol %}, connection_protocol='{{obj.attrs.connection_protocol}}'{%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro decision_powershell_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Decision.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}) +CalmTask.Decision.powershell({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Decision.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}}) +CalmTask.Decision.powershell({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Decision.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}) +CalmTask.Decision.powershell({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Decision.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}}) +CalmTask.Decision.powershell({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_decision_python.py.jinja2 b/calm/dsl/decompile/schemas/task_decision_python.py.jinja2 index 13c4b7e59..f64e97a81 100644 --- a/calm/dsl/decompile/schemas/task_decision_python.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_decision_python.py.jinja2 @@ -1,12 +1,18 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.attrs.connection_protocol %}, connection_protocol='{{obj.attrs.connection_protocol}}'{%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro decision_python_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Decision.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}) +CalmTask.Decision.python({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Decision.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}}) +CalmTask.Decision.python({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Decision.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}) +CalmTask.Decision.python({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Decision.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}}) +CalmTask.Decision.python({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_decision_ssh.py.jinja2 b/calm/dsl/decompile/schemas/task_decision_ssh.py.jinja2 index 539e4a525..16d273c63 100644 --- a/calm/dsl/decompile/schemas/task_decision_ssh.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_decision_ssh.py.jinja2 @@ -1,12 +1,19 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + + {%- macro decision_ssh_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Decision.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}) +CalmTask.Decision.ssh({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Decision.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}}) +CalmTask.Decision.ssh({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Decision.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}) +CalmTask.Decision.ssh({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Decision.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}}) +CalmTask.Decision.ssh({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_exec_escript.py.jinja2 b/calm/dsl/decompile/schemas/task_exec_escript.py.jinja2 index 1c63c17e1..cd0984a3d 100644 --- a/calm/dsl/decompile/schemas/task_exec_escript.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_exec_escript.py.jinja2 @@ -1,12 +1,21 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + +{%- macro task_class(obj) -%} +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %} + {%- macro exec_escript_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Exec.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +{{ task_class(obj) }}.Exec.escript.py2({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Exec.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +{{ task_class(obj) }}.Exec.escript.py2({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Exec.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +{{ task_class(obj) }}.Exec.escript.py2({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Exec.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +{{ task_class(obj) }}.Exec.escript.py2({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_exec_escript_py3.py.jinja2 b/calm/dsl/decompile/schemas/task_exec_escript_py3.py.jinja2 index 08d6de9ef..4b854fe48 100644 --- a/calm/dsl/decompile/schemas/task_exec_escript_py3.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_exec_escript_py3.py.jinja2 @@ -1,12 +1,22 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro task_class(obj) -%} +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %} +{%- endmacro %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro exec_escript_py3_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Exec.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +{{ task_class(obj) }}.Exec.escript.py3({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Exec.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +{{ task_class(obj) }}.Exec.escript.py3({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Exec.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +{{ task_class(obj) }}.Exec.escript.py3({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Exec.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +{{ task_class(obj) }}.Exec.escript.py3({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_exec_powershell.py.jinja2 b/calm/dsl/decompile/schemas/task_exec_powershell.py.jinja2 index 0de7de085..e52156067 100644 --- a/calm/dsl/decompile/schemas/task_exec_powershell.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_exec_powershell.py.jinja2 @@ -1,12 +1,18 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.attrs.connection_protocol %}, connection_protocol='{{obj.attrs.connection_protocol}}'{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro exec_powershell_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Exec.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.powershell({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Exec.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.powershell({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Exec.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.powershell({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Exec.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.powershell({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_exec_python.py.jinja2 b/calm/dsl/decompile/schemas/task_exec_python.py.jinja2 index 63e1a40a2..788ede054 100644 --- a/calm/dsl/decompile/schemas/task_exec_python.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_exec_python.py.jinja2 @@ -1,12 +1,18 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.attrs.connection_protocol %}, connection_protocol='{{obj.attrs.connection_protocol}}'{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro exec_python_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Exec.python(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.python({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Exec.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.python({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Exec.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.python({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Exec.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.python({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_exec_ssh.py.jinja2 b/calm/dsl/decompile/schemas/task_exec_ssh.py.jinja2 index c8ecbce7a..97d7e832c 100644 --- a/calm/dsl/decompile/schemas/task_exec_ssh.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_exec_ssh.py.jinja2 @@ -1,12 +1,18 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro exec_ssh_task(obj) -%} {%- if obj.cred is not defined and obj.target is not defined %} -CalmTask.Exec.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} ) +CalmTask.Exec.ssh({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.Exec.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.ssh({{ common_details(obj) }}, target={{obj.target}}) {%- elif obj.target is not defined %} -CalmTask.Exec.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.ssh({{ common_details(obj) }}, cred={{obj.cred}}) {%- else %} -CalmTask.Exec.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.Exec.ssh({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_http_delete.py.jinja2 b/calm/dsl/decompile/schemas/task_http_delete.py.jinja2 index 2d9c60f01..fe240ffed 100644 --- a/calm/dsl/decompile/schemas/task_http_delete.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_http_delete.py.jinja2 @@ -1,12 +1,21 @@ +{% import "http_response_handle.py.jinja2" as http_response_handle %} +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +{%- if obj.relative_url %}relative_url={%- endif %} {%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}} {%- if obj.response_code_status_map %}, response_code_status_map=[{%- for status_map in obj.response_code_status_map %}{{ http_response_handle.response_code(status_map) }}{%- endfor %}] {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %}, response_paths={{obj.response_paths}}, name='{{obj.name}}' {%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference %}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} +{%- endmacro %} + +{% import "http_response_handle.py.jinja2" as http_response_handle %} + {%- macro http_delete_task(obj) -%} {%- if obj.target is not defined and obj.attrs.request_body is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.delete({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}'{%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.delete({{ common_details(obj) }}) {%- elif obj.target is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.delete({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}body=json.dumps({{obj.attrs.request_body}}), headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}'{%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.delete({{ common_details(obj) }}, body={{obj.attrs.request_body}}) {%- elif obj.attrs.request_body is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.delete({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}', target={{obj.target}}{%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.delete({{ common_details(obj) }}, target={{obj.target}}{%- if obj.cred %}, cred={{obj.cred}}{%- endif %}) {%- else %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.delete({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}body=json.dumps({{obj.attrs.request_body}}), headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}', target={{obj.target}}{%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.delete({{ common_details(obj) }}, body={{obj.attrs.request_body}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_http_get.py.jinja2 b/calm/dsl/decompile/schemas/task_http_get.py.jinja2 index a391069f0..16882ef64 100644 --- a/calm/dsl/decompile/schemas/task_http_get.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_http_get.py.jinja2 @@ -1,8 +1,15 @@ +{% import "http_response_handle.py.jinja2" as http_response_handle %} +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +{%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}} {%- if obj.response_code_status_map %}, response_code_status_map=[{%- for status_map in obj.response_code_status_map %}{{ http_response_handle.response_code(status_map) }}{%- endfor %}] {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %}, response_paths={{obj.response_paths}}, name='{{obj.name}}' {%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference %}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} +{%- endmacro %} + {%- macro http_get_task(obj) -%} {%- if obj.target is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.get({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}'{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference %}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.get({{ common_details(obj) }}) {%- else %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.get({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}', target={{obj.target}}{%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference %}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.get({{ common_details(obj) }}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_http_post.py.jinja2 b/calm/dsl/decompile/schemas/task_http_post.py.jinja2 index 79ac52962..6afac0f8d 100644 --- a/calm/dsl/decompile/schemas/task_http_post.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_http_post.py.jinja2 @@ -1,12 +1,22 @@ +{% import "http_response_handle.py.jinja2" as http_response_handle %} +{% import "status_handle.py.jinja2" as status_handle %} + + +{%- macro common_details(obj) -%} +{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}} {%- if obj.response_code_status_map %}, response_code_status_map=[{%- for status_map in obj.response_code_status_map %}{{ http_response_handle.response_code(status_map) }}{%- endfor %}] {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %}, response_paths={{obj.response_paths}}, name='{{obj.name}}' {%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference %}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} +{%- endmacro %} + +{% import "http_response_handle.py.jinja2" as http_response_handle %} + {%- macro http_post_task(obj) -%} {%- if obj.target is not defined and obj.attrs.request_body is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.post({%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}'{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.post({{ common_details(obj) }}) {%- elif obj.target is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.post({%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}body=json.dumps({{obj.attrs.request_body}}), headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}'{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.post({{ common_details(obj) }}, body={{obj.attrs.request_body}}) {%- elif obj.attrs.request_body is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.post({%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}', target={{obj.target}}{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.post({{ common_details(obj) }}, target={{obj.target}}) {%- else %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.post({%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}body=json.dumps({{obj.attrs.request_body}}), headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}', target={{obj.target}}{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.post({{ common_details(obj) }}, body={{obj.attrs.request_body}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_http_put.py.jinja2 b/calm/dsl/decompile/schemas/task_http_put.py.jinja2 index c414408cc..89da34504 100644 --- a/calm/dsl/decompile/schemas/task_http_put.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_http_put.py.jinja2 @@ -1,12 +1,19 @@ +{% import "http_response_handle.py.jinja2" as http_response_handle %} +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +{%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}} {%- if obj.response_code_status_map %}, response_code_status_map=[{%- for status_map in obj.response_code_status_map %}{{ http_response_handle.response_code(status_map) }}{%- endfor %}] {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %}, response_paths={{obj.response_paths}}, name='{{obj.name}}' {%- if obj.cred %}, cred={{obj.cred}}{%- endif %}{% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference %}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} +{%- endmacro %} + {%- macro http_put_task(obj) -%} {%- if obj.target is not defined and obj.attrs.request_body is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.put({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}'{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.put({{ common_details(obj) }}) {%- elif obj.target is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.put({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}body=json.dumps({{obj.attrs.request_body}}), headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}'{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.put({{ common_details(obj) }}, body={{obj.attrs.request_body}}) {%- elif obj.attrs.request_body is not defined %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.put({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}', target={{obj.target}}{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.put({{ common_details(obj) }}, target={{obj.target}}) {%- else %} -{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.put({%- if obj.relative_url %}relative_url={%- endif %}{%- if obj.attrs.url %}'{{obj.attrs.url}}', {%- endif %}body=json.dumps({{obj.attrs.request_body}}), headers={{obj.headers}}, secret_headers={{obj.secret_headers}} , content_type='{{obj.attrs.content_type}}', verify={{obj.attrs.tls_verify}}, status_mapping={{obj.status_mapping}}, response_paths={{obj.response_paths}}, name='{{obj.name}}', target={{obj.target}}{%- if obj.cred %}, cred={{obj.cred}}{%- endif %} {% if obj.credentials_list %}, credential={{obj.credentials_list | replace("'","")}}{% endif %} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.attrs.relative_url %}, relative_url='{{obj.attrs.relative_url}}'{%- endif %}) +{%- if obj.calm_var_task %}CalmVarTask{%- else %}CalmTask{%- endif %}.HTTP.put({{ common_details(obj) }}, body={{obj.attrs.request_body}}, target={{obj.target}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_resource_type_action.py.jinja2 b/calm/dsl/decompile/schemas/task_resource_type_action.py.jinja2 new file mode 100644 index 000000000..9ab6a5956 --- /dev/null +++ b/calm/dsl/decompile/schemas/task_resource_type_action.py.jinja2 @@ -0,0 +1,18 @@ +{%- macro task_resource_type_action(obj) -%} +CalmTask.ResourceTypeAction( + '{{obj.name}}', Ref.Resource_Type(name='{{obj.resource_type_name}}', provider_name='{{obj.provider_name}}'), + Ref.ResourceTypeAction( + name='{{obj.action_name}}', resource_type_name='{{obj.resource_type_name}}', + provider_name='{{obj.provider_name}}', + ), + account_ref=Ref.Account(name='{{obj.account_name}}'), + inarg_list=[ + {%- for variable in obj.inarg_list %} + {{variable}}, + {%- endfor %} + ], + output_variables={{obj.output_variables}}, +) +{%- endmacro %} + +{{ task_resource_type_action(obj) }} diff --git a/calm/dsl/decompile/schemas/task_setvariable_escript.py.jinja2 b/calm/dsl/decompile/schemas/task_setvariable_escript.py.jinja2 index 8784fac26..d448e10e4 100644 --- a/calm/dsl/decompile/schemas/task_setvariable_escript.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_setvariable_escript.py.jinja2 @@ -1,14 +1,20 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro setvariable_escript_task(obj) -%} {%- if obj.cred is not defined and obj.target and obj.variables is not defined %} -CalmTask.SetVariable.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py2({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.SetVariable.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py2({{ common_details(obj) }} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}}) {%- elif obj.target is not defined %} -CalmTask.SetVariable.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, variables={{obj.variables}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py2({{ common_details(obj) }}, cred={{obj.cred}}, variables={{obj.variables}}) {%- elif obj.variables is not defined %} -CalmTask.SetVariable.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py2({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- else %} -CalmTask.SetVariable.escript.py2(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py2({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_setvariable_escript_py3.py.jinja2 b/calm/dsl/decompile/schemas/task_setvariable_escript_py3.py.jinja2 index b4f93e5c0..d15d614a8 100644 --- a/calm/dsl/decompile/schemas/task_setvariable_escript_py3.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_setvariable_escript_py3.py.jinja2 @@ -1,14 +1,21 @@ +{% import "status_handle.py.jinja2" as status_handle %} + + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %} {%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro setvariable_escript_py3_task(obj) -%} {%- if obj.cred is not defined and obj.target and obj.variables is not defined %} -CalmTask.SetVariable.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py3({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.SetVariable.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py3({{ common_details(obj) }} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}} ) {%- elif obj.target is not defined %} -CalmTask.SetVariable.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, variables={{obj.variables}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py3({{ common_details(obj) }}, cred={{obj.cred}}, variables={{obj.variables}}) {%- elif obj.variables is not defined %} -CalmTask.SetVariable.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py3({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- else %} -CalmTask.SetVariable.escript.py3(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}} {%- if obj.attrs.tunnel_reference%}, tunnel=Ref.Tunnel(name="{{ obj.attrs.tunnel_reference.name }}"){%- endif %}) +CalmTask.SetVariable.escript.py3({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_setvariable_powershell.py.jinja2 b/calm/dsl/decompile/schemas/task_setvariable_powershell.py.jinja2 index 44b8e6a49..e63bbe903 100644 --- a/calm/dsl/decompile/schemas/task_setvariable_powershell.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_setvariable_powershell.py.jinja2 @@ -1,14 +1,20 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.attrs.connection_protocol %}, connection_protocol='{{obj.attrs.connection_protocol}}'{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro setvariable_powershell_task(obj) -%} {%- if obj.cred is not defined and obj.target and obj.variables is not defined %} -CalmTask.SetVariable.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.powershell({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.SetVariable.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.powershell({{ common_details(obj) }} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}}) {%- elif obj.target is not defined %} -CalmTask.SetVariable.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.powershell({{ common_details(obj) }}, cred={{obj.cred}}, variables={{obj.variables}}) {%- elif obj.variables is not defined %} -CalmTask.SetVariable.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.powershell({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- else %} -CalmTask.SetVariable.powershell(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.powershell({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_setvariable_python.py.jinja2 b/calm/dsl/decompile/schemas/task_setvariable_python.py.jinja2 index 4125c0af5..94117486c 100644 --- a/calm/dsl/decompile/schemas/task_setvariable_python.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_setvariable_python.py.jinja2 @@ -1,14 +1,20 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.attrs.connection_protocol %}, connection_protocol='{{obj.attrs.connection_protocol}}'{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro setvariable_python_task(obj) -%} {%- if obj.cred is not defined and obj.target and obj.variables is not defined %} -CalmTask.SetVariable.python(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.python({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.SetVariable.python(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.python({{ common_details(obj) }} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}}) {%- elif obj.target is not defined %} -CalmTask.SetVariable.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.python({{ common_details(obj) }}, cred={{obj.cred}}, variables={{obj.variables}}) {%- elif obj.variables is not defined %} -CalmTask.SetVariable.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.python({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- else %} -CalmTask.SetVariable.python(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.python({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_setvariable_ssh.py.jinja2 b/calm/dsl/decompile/schemas/task_setvariable_ssh.py.jinja2 index 82fa89333..35c02c16e 100644 --- a/calm/dsl/decompile/schemas/task_setvariable_ssh.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_setvariable_ssh.py.jinja2 @@ -1,14 +1,20 @@ +{% import "status_handle.py.jinja2" as status_handle %} + +{%- macro common_details(obj) -%} +name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.attrs.ip %}, ip='{{obj.attrs.ip}}'{%- endif %} {%- if obj.attrs.port %}, port={{obj.attrs.port}}{%- endif %} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "exit_code" %}{{ status_handle.exit_code(status_map) }}{% endif %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %} +{%- endmacro %} + {%- macro setvariable_ssh_task(obj) -%} {%- if obj.cred is not defined and obj.target and obj.variables is not defined %} -CalmTask.SetVariable.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.ssh({{ common_details(obj) }}) {%- elif obj.cred is not defined %} -CalmTask.SetVariable.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.ssh({{ common_details(obj) }} {%- if obj.target %}, target={{obj.target}}{% endif %}, variables={{obj.variables}}) {%- elif obj.target is not defined %} -CalmTask.SetVariable.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.ssh({{ common_details(obj) }}, cred={{obj.cred}}, variables={{obj.variables}}) {%- elif obj.variables is not defined %} -CalmTask.SetVariable.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.ssh({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}) {%- else %} -CalmTask.SetVariable.ssh(name='{{obj.name}}', filename={{obj.attrs.script_file}}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}} {%- if obj.target_endpoint %}, target_endpoint={{obj.target_endpoint}} {%- endif %}) +CalmTask.SetVariable.ssh({{ common_details(obj) }}, cred={{obj.cred}}, target={{obj.target}}, variables={{obj.variables}}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/task_vm_power_off.py.jinja2 b/calm/dsl/decompile/schemas/task_vm_power_off.py.jinja2 index 2b6502ae3..ce0cfa550 100644 --- a/calm/dsl/decompile/schemas/task_vm_power_off.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_vm_power_off.py.jinja2 @@ -1,5 +1,7 @@ +{% import "status_handle.py.jinja2" as status_handle %} + {%- macro task_vm_power_off(obj) -%} -CalmTask.VMPowerOff(name='{{obj.name}}', target={{obj.target}}) +CalmTask.VMPowerOff(name='{{obj.name}}', target={{obj.target}} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %}) {%- endmacro %} {{ task_vm_power_off(obj) }} diff --git a/calm/dsl/decompile/schemas/task_vm_power_on.py.jinja2 b/calm/dsl/decompile/schemas/task_vm_power_on.py.jinja2 index c4f4c8f80..97f244e93 100644 --- a/calm/dsl/decompile/schemas/task_vm_power_on.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_vm_power_on.py.jinja2 @@ -1,5 +1,7 @@ +{% import "status_handle.py.jinja2" as status_handle %} + {%- macro task_vm_power_on(obj) -%} -CalmTask.VMPowerOn(name='{{obj.name}}', target={{obj.target}}) +CalmTask.VMPowerOn(name='{{obj.name}}', target={{obj.target}} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %}) {%- endmacro %} {{ task_vm_power_on(obj) }} diff --git a/calm/dsl/decompile/schemas/task_vm_restart.py.jinja2 b/calm/dsl/decompile/schemas/task_vm_restart.py.jinja2 index a0eb21345..f54f44448 100644 --- a/calm/dsl/decompile/schemas/task_vm_restart.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_vm_restart.py.jinja2 @@ -1,5 +1,7 @@ +{% import "status_handle.py.jinja2" as status_handle %} + {%- macro task_vm_restart(obj) -%} -CalmTask.VMRestart(name='{{obj.name}}', target={{obj.target}}) +CalmTask.VMRestart(name='{{obj.name}}', target={{obj.target}} {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %}) {%- endmacro %} {{ task_vm_restart(obj) }} diff --git a/calm/dsl/decompile/schemas/task_while_loop.py.jinja2 b/calm/dsl/decompile/schemas/task_while_loop.py.jinja2 index 5a2de13cc..d2825ceff 100644 --- a/calm/dsl/decompile/schemas/task_while_loop.py.jinja2 +++ b/calm/dsl/decompile/schemas/task_while_loop.py.jinja2 @@ -1,5 +1,7 @@ +{% import "status_handle.py.jinja2" as status_handle %} + {%- macro while_task(obj) -%} -CalmTask.Loop(name='{{obj.name}}', iterations="{{obj.attrs.iterations}}", exit_condition={{obj.attrs.exit_condition_type}}, loop_variable='{{obj.attrs.loop_variable}}') +CalmTask.Loop(name='{{obj.name}}', iterations={{obj.attrs.iterations}}, exit_condition={{obj.attrs.exit_condition_type}}, loop_variable='{{obj.attrs.loop_variable}}' {%- if obj.status_map_list %}, status_map_list=[{%- for status_map in obj.status_map_list %}{%- if status_map.type == "status" %}{{ status_handle.task_status(status_map) }}{% endif %}{%- endfor %}] {%- endif %}) {%- endmacro %} {{ while_task(obj) }} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_date.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_date.py.jinja2 index fcd0e64a7..8200e7fdc 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_date.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_date.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_array_date_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.date({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.date({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.date({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.date({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_datetime.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_datetime.py.jinja2 index 423deed0e..4ed8131e4 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_datetime.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_datetime.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_array_datetime_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.datetime({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.datetime({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.datetime({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.datetime({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_int.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_int.py.jinja2 index 804de100d..987876c1b 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_int.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_int.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_array_int_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.int({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.int({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.int({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.int({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_multiline.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_multiline.py.jinja2 index b668b4647..17747e29d 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_multiline.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_multiline.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value={{obj.default_value}}{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_array_multiline_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.multiline({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.multiline({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.multiline({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.multiline({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_string.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_string.py.jinja2 index 9cc54ed6e..a4b44bee4 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_string.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_string.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_array_string_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_time.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_time.py.jinja2 index f659ff114..b1043f323 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_array_time.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_array_time.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_array_time_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.time({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.time({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.time({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.Array.time({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_date.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_date.py.jinja2 index 3094f4521..3a370e55d 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_date.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_date.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_date_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.date({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.date({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.date({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.date({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_datetime.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_datetime.py.jinja2 index b6e9faba8..a443389ea 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_datetime.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_datetime.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_datetime_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.datetime({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.datetime({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.datetime({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.datetime({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_int.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_int.py.jinja2 index d89e6183e..0e6bb623a 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_int.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_int.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_int_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.int({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.int({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.int({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.int({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_multiline.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_multiline.py.jinja2 index 33c077f5a..6eab90eb5 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_multiline.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_multiline.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value={{obj.default_value}}{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_multiline_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.multiline({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.multiline({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.multiline({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.multiline({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_string.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_string.py.jinja2 index edaffddfa..aa3dfd8c8 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_string.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_string.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_string_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/schemas/var_with_options_fromTask_time.py.jinja2 b/calm/dsl/decompile/schemas/var_with_options_fromTask_time.py.jinja2 index d127c67bf..f1c3d7daf 100644 --- a/calm/dsl/decompile/schemas/var_with_options_fromTask_time.py.jinja2 +++ b/calm/dsl/decompile/schemas/var_with_options_fromTask_time.py.jinja2 @@ -1,8 +1,12 @@ +{%- macro common_details(obj) -%} +{{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}'{%- if obj.default_value %}, value='{{obj.default_value}}'{%- endif %} +{%- endmacro %} + {%- macro with_options_from_task_time_var(obj) -%} {%- if obj.regex %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.time({{obj.value}}, label='{{obj.label}}', regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}, is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.time({{ common_details(obj) }}, regex='{{obj.regex}}', validate_regex={{obj.validate_regex}}) {%- else %} -{{obj.name}} = CalmVariable.WithOptions.FromTask.time({{obj.value}}, label='{{obj.label}}', is_mandatory={{obj.is_mandatory}}, is_hidden={{obj.is_hidden}}, description='{{obj.description}}') +{{obj.name}} = CalmVariable.WithOptions.FromTask.time({{ common_details(obj) }}) {%- endif %} {%- endmacro %} diff --git a/calm/dsl/decompile/service.py b/calm/dsl/decompile/service.py index f8575aff7..390a2b1aa 100644 --- a/calm/dsl/decompile/service.py +++ b/calm/dsl/decompile/service.py @@ -41,7 +41,12 @@ def render_service_template(cls, secrets_dict=[], endpoints=[], ep_list=[]): for entity in user_attrs.get("variables", []): variable_list.append( render_variable_template( - entity, entity_context, context=context, secrets_dict=secrets_dict + entity, + entity_context, + context=context, + secrets_dict=secrets_dict, + endpoints=endpoints, + ep_list=ep_list, ) ) diff --git a/calm/dsl/decompile/task.py b/calm/dsl/decompile/task.py index 66f7a6e08..955789a90 100644 --- a/calm/dsl/decompile/task.py +++ b/calm/dsl/decompile/task.py @@ -1,5 +1,6 @@ import os import sys +import uuid from calm.dsl.decompile.render import render_template from calm.dsl.decompile.ndb import get_schema_file_and_user_attrs @@ -33,6 +34,8 @@ def render_task_template( secrets_dict=[], credentials_list=[], rendered_credential_list=[], + use_calm_var_task=False, + ignore_cred_dereference_error=False, ): LOG.debug("Rendering {} task template".format(cls.name)) if not isinstance(cls, TaskType): @@ -55,11 +58,21 @@ def render_task_template( cred = cls.attrs.get("login_credential_local_reference", None) if cred: - user_attrs["cred"] = "ref({})".format( - get_cred_var_name(getattr(cred, "name", "") or cred.__name__) - ) + try: + user_attrs["cred"] = "ref({})".format( + get_cred_var_name(getattr(cred, "name", "") or cred.__name__) + ) + except ValueError as ve: + if ignore_cred_dereference_error: + LOG.debug("Ignoring cred not found error for RTOpTask") + else: + raise Exception(ve) + status_map_list = getattr(cls, "status_map_list", []) + if status_map_list: + user_attrs["status_map_list"] = status_map_list if cls.type == "EXEC": + user_attrs["calm_var_task"] = use_calm_var_task script_type = cls.attrs["script_type"] cls.attrs["script_file"] = create_script_file( script_type, cls.attrs["script"], entity_context @@ -120,7 +133,7 @@ def render_task_template( elif scaling_type == "SCALEIN": schema_file = "task_scaling_scalein.py.jinja2" elif cls.type == "HTTP": - user_attrs["calm_var_task"] = False + user_attrs["calm_var_task"] = use_calm_var_task if "Runbook" in entity_context: if user_attrs.get("attrs", {}).get("url", ""): @@ -132,7 +145,9 @@ def render_task_template( attrs = cls.attrs user_attrs["headers"] = {} user_attrs["secret_headers"] = {} - user_attrs["status_mapping"] = {} + user_attrs["response_code_status_map"] = attrs.get( + "expected_response_params", [] + ) for var in attrs.get("headers", []): var_type = var["type"] @@ -149,33 +164,46 @@ def render_task_template( } ) - for status in attrs.get("expected_response_params", []): - user_attrs["status_mapping"][status["code"]] = ( - True if status["status"] == "SUCCESS" else False - ) - # Store auth objects auth_obj = attrs.get("authentication", {}) auth_type = auth_obj.get("type", "") if auth_type == "basic_with_cred": auth_cred = auth_obj.get("credential_local_reference", None) if auth_cred: - user_attrs["cred"] = "ref({})".format( - get_cred_var_name( - getattr(auth_cred, "name", "") or auth_cred.__name__ + try: + user_attrs["cred"] = "ref({})".format( + get_cred_var_name( + getattr(auth_cred, "name", "") or auth_cred.__name__ + ) ) - ) + except ValueError as ve: + if ignore_cred_dereference_error: + LOG.debug("Ignoring cred not found error for RTOpTask") + else: + raise Exception(ve) elif auth_type == "basic": cred_dict = { "username": auth_obj["username"], "password": auth_obj["password"], "type": "PASSWORD", + "name": "Credential" + str(uuid.uuid4())[:8], } cred = CredentialType.decompile(cred_dict) - rendered_credential_list.append(render_credential_template(cred)) + rendered_credential_list.append( + render_credential_template( + cred, + context="PROVIDER" + if ( + entity_context.startswith("CloudProvider") + or entity_context.startswith("ResourceType") + ) + else "BP", + ) + ) cred = get_cred_var_name(cred.name) user_attrs["credentials_list"] = cred - credentials_list.append(cred) + cred_dict["name_in_file"] = cred + credentials_list.append(cred_dict) user_attrs["response_paths"] = attrs.get("response_paths", {}) method = attrs["method"] @@ -193,11 +221,11 @@ def render_task_template( schema_file = "task_http_put.py.jinja2" elif method == "DELETE": - # TODO remove it from here - if not cls.attrs["request_body"]: - cls.attrs["request_body"] = {} schema_file = "task_http_delete.py.jinja2" + if cls.attrs["request_body"] != None: + cls.attrs["request_body"] = repr(cls.attrs["request_body"]) + elif cls.type == "CALL_RUNBOOK": is_power_action = False runbook = cls.attrs["runbook_reference"] @@ -242,15 +270,12 @@ def render_task_template( } schema_file = "task_call_config.py.jinja2" elif cls.type == "RT_OPERATION": - acc_name = cls.attrs["account_reference"]["name"] - tag = cls.attrs.get("tag", "") - if tag == "Database": - schema_file, user_attrs = get_schema_file_and_user_attrs( - cls.name, cls.attrs, acc_name - ) - else: - LOG.error("Tag {} not supported for RT operation".format(tag)) - sys.exit("Tag {} not supported for RT operation".format(tag)) + schema_file, user_attrs = get_schema_file_and_user_attrs( + cls.name, + cls.attrs, + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, + ) elif cls.type == "DECISION": script_type = cls.attrs["script_type"] cls.attrs["script_file"] = create_script_file( diff --git a/calm/dsl/decompile/task_tree.py b/calm/dsl/decompile/task_tree.py index 4892eebae..3855c5708 100644 --- a/calm/dsl/decompile/task_tree.py +++ b/calm/dsl/decompile/task_tree.py @@ -13,6 +13,8 @@ def render_task_tree_template( while_tasks=None, context="", secrets_dict=[], + credentials_list=[], + rendered_credential_list=[], ): """render task tree template""" @@ -26,6 +28,8 @@ def render_task_tree_template( CONFIG_SPEC_MAP, context, secrets_dict, + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, ) for tasks in ["success_tasks", "failure_tasks"]: for task in decision_tasks[decision_task][tasks]: @@ -36,6 +40,8 @@ def render_task_tree_template( CONFIG_SPEC_MAP, context, secrets_dict, + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, ) while_loop_rendered_tasks = {} @@ -48,6 +54,8 @@ def render_task_tree_template( CONFIG_SPEC_MAP, context, secrets_dict, + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, ) for child_task in while_tasks[base_task]["child_tasks"]: while_loop_rendered_tasks[child_task.name] = render_task_template( @@ -57,6 +65,8 @@ def render_task_tree_template( CONFIG_SPEC_MAP, context, secrets_dict, + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, ) special_task_data = {"decision_tasks": decision_tasks, "while_tasks": while_tasks} rendered_tasks = {} @@ -84,6 +94,8 @@ def render_task_tree_template( CONFIG_SPEC_MAP, context, secrets_dict, + credentials_list=credentials_list, + rendered_credential_list=rendered_credential_list, ) task_indent_tree = [] visited = [] diff --git a/calm/dsl/decompile/variable.py b/calm/dsl/decompile/variable.py index 58b64ee68..154ddb48c 100644 --- a/calm/dsl/decompile/variable.py +++ b/calm/dsl/decompile/variable.py @@ -1,10 +1,12 @@ import os from calm.dsl.decompile.render import render_template +from calm.dsl.decompile.endpoint import render_endpoint from calm.dsl.decompile.task import render_task_template -from calm.dsl.builtins import VariableType, TaskType +from calm.dsl.builtins import VariableType, TaskType, CalmEndpoint as Endpoint from calm.dsl.decompile.file_handler import get_local_dir from calm.dsl.log import get_logging_handle +from calm.dsl.constants import VARIABLE LOG = get_logging_handle(__name__) SECRET_VAR_FILES = [] @@ -14,18 +16,40 @@ def render_variable_template( cls, entity_context, context="", + variable_context="variable", # In some case, the spec field might not be variable_list secrets_dict=[], credentials_list=[], rendered_credential_list=[], + endpoints=[], + ep_list=[], + ignore_cred_dereference_error=False, ): LOG.debug("Rendering {} variable template".format(cls.__name__)) if not isinstance(cls, VariableType): raise TypeError("{} is not of type {}".format(cls, VariableType)) + if cls.options: + options = cls.options.get_dict() + + if cls.type in [ + VARIABLE.TYPE.EXEC_LOCAL, + VARIABLE.TYPE.HTTP_LOCAL, + VARIABLE.TYPE.EXEC_SECRET, + VARIABLE.TYPE.HTTP_SECRET, + ]: + ep = cls.options.get("exec_target_reference", None) + if ep: + endpoint_name = ep.get("name", "") + if endpoint_name not in ep_list: + endpoints.append(render_endpoint(Endpoint.use_existing(endpoint_name))) + ep_list.append(endpoint_name) + else: + options.pop("exec_target_reference") + # Updating the context of variables - entity_context = entity_context + "_variable_" + cls.__name__ - context = context + "variable_list." + cls.__name__ + entity_context = entity_context + "_" + variable_context + "_" + cls.__name__ + context = context + variable_context + "_list." + cls.__name__ user_attrs = cls.get_user_attrs() user_attrs["description"] = cls.__doc__ or "" @@ -41,7 +65,6 @@ def render_variable_template( var_type = "simple" else: - options = cls.options.get_dict() choices = options.get("choices", []) option_type = options.get("type", "") @@ -111,7 +134,6 @@ def render_variable_template( else: data_type = cls.data_type - options = cls.options.get_dict() option_type = options.get("type", "PREDEFINED") if option_type == "PREDEFINED": @@ -155,11 +177,15 @@ def render_variable_template( options.pop("choices", None) task = TaskType.decompile(options) task.__name__ = "SampleTask" + if user_attrs["value"]: # CALM-45352 + user_attrs["default_value"] = user_attrs.pop("value") user_attrs["value"] = render_task_template( task, entity_context=entity_context, credentials_list=credentials_list, rendered_credential_list=rendered_credential_list, + use_calm_var_task=True, + ignore_cred_dereference_error=ignore_cred_dereference_error, ) if data_type == "BASE": @@ -174,6 +200,8 @@ def render_variable_template( elif var_val_type == "DATE_TIME": schema_file = "var_with_options_fromTask_datetime.py.jinja2" elif var_val_type == "MULTILINE_STRING": + if user_attrs.get("default_value"): + user_attrs["default_value"] = repr(user_attrs["default_value"]) schema_file = "var_with_options_fromTask_multiline.py.jinja2" else: if var_val_type == "STRING": @@ -187,6 +215,8 @@ def render_variable_template( elif var_val_type == "DATE_TIME": schema_file = "var_with_options_fromTask_array_datetime.py.jinja2" elif var_val_type == "MULTILINE_STRING": + if user_attrs.get("default_value"): + user_attrs["default_value"] = repr(user_attrs["default_value"]) schema_file = "var_with_options_fromTask_array_multiline.py.jinja2" if not schema_file: diff --git a/calm/dsl/init/__init__.py b/calm/dsl/init/__init__.py index 2ac5e9839..1be96b793 100644 --- a/calm/dsl/init/__init__.py +++ b/calm/dsl/init/__init__.py @@ -1,5 +1,6 @@ from .blueprint import init_bp from .runbook import init_runbook +from .provider import init_provider -__all__ = ["init_bp", "init_runbook"] +__all__ = ["init_bp", "init_runbook", "init_provider"] diff --git a/calm/dsl/init/blueprint/ahv_blueprint.py.jinja2 b/calm/dsl/init/blueprint/ahv_blueprint.py.jinja2 index ad86ef3d5..9b06b925c 100644 --- a/calm/dsl/init/blueprint/ahv_blueprint.py.jinja2 +++ b/calm/dsl/init/blueprint/ahv_blueprint.py.jinja2 @@ -1,14 +1,14 @@ {% macro BlueprintTemplate(bp_name, subnet_name, cluster_name, vpc_name) -%} # THIS FILE IS AUTOMATICALLY GENERATED. """ -Sample Calm DSL for {{bp_name}} blueprint +Sample NCM Self-Service DSL for {{bp_name}} blueprint The top-level folder contains the following files: HelloBlueprint/ ├── .local │ └── keys -│ ├── centos -│ └── centos_pub +│ ├── ssh_key +│ └── ssh_key_pub ├── blueprint.py └── scripts ├── pkg_install_task.sh @@ -16,9 +16,9 @@ HelloBlueprint/ On launch, this blueprint does the following: 1. Creates AHV VM (2 vCPUs, 4G Mem, 1 core) - 2. Installs CentOS 7 by downloading image from http://download.nutanix.com. + 2. Creates placeholder (VM_DISK_IMAGE) to supply VM Disk Image 3. Injects SSH public key in the VM using cloud init. - 4. Creates calm credential using the SSH private key to run tasks on the VM. + 4. Creates NCM Self-Service credential using the SSH private key to run tasks on the VM. Order of execution for every deployment during blueprint launch: 1. Substrate.__pre_create__() (Only http and escript tasks are allowed here) @@ -38,6 +38,10 @@ Useful commands (execute from top-level directory): 8. calm delete app 9. calm delete bp +Remark: +1. Provided SSH Creds in this blueprint works only with VM Disk Images supporting cloud init guest customization. +2. For all other VM Disk Images, supply valid creds for blueprint to work properly. + """ import os @@ -54,19 +58,28 @@ from calm.dsl.builtins import AhvVmGC, AhvVmResources, AhvVm, Ref # SSH Credentials -CENTOS_USER = "centos" -CENTOS_KEY = read_local_file(os.path.join("keys", "centos")) -CENTOS_PUBLIC_KEY = read_local_file(os.path.join("keys", "centos_pub")) -CentosCred = basic_cred( - CENTOS_USER, CENTOS_KEY, name="Centos", type="KEY", default=True, +USERNAME = "admin" +PRIVATE_KEY = read_local_file(os.path.join("keys", "ssh_key")) +PUBLIC_KEY = read_local_file(os.path.join("keys", "ssh_key_pub")) +CredObject = basic_cred( + USERNAME, PRIVATE_KEY, name="admin", type="KEY", default=True, ) -# OS Image details for VM -CENTOS_IMAGE_SOURCE = "http://download.nutanix.com/calm/CentOS-7-x86_64-1810.qcow2" -CentosPackage = vm_disk_package( - name="centos_disk", config={"image": {"source": CENTOS_IMAGE_SOURCE}}, -) +# Use either of the following methods to provide VM Disk Image +# Case 1: Add VM Disk Image name here (For case when valid image is known from server) +VM_DISK_IMAGE = "" + +# Case 2: OS Image details for VM (Uncomment following block and use for case when URI of image is known) +""" +IMAGE_URI = "" +DISK_NAME = "" + +# Creating disk image object to be used later +DiskPackage = vm_disk_package( + name=DISK_NAME, config={"image": {"source": IMAGE_URI}}, +) +""" class {{bp_name}}Service(Service): """Sample Service""" @@ -153,10 +166,10 @@ class {{bp_name}}VmResources(AhvVmResources): vCPUs = 2 cores_per_vCPU = 1 disks = [ - AhvVmDisk.Disk.Scsi.cloneFromVMDiskPackage(CentosPackage, bootable=True), + AhvVmDisk.Disk.Scsi.cloneFromImageService(VM_DISK_IMAGE, bootable=True), # use cloneFromVMDiskPackage(DiskPackage, bootable=True) method if using with DISK IMAGE URI ] {% if vpc_name != "" %}nics = [AhvVmNic.NormalNic.ingress(subnet="{{subnet_name}}", vpc="{{vpc_name}}")] - {% else %}nics = [AhvVmNic.DirectNic.ingress(subnet="{{subnet_name}}", cluster="{{cluster_name}}")] + {% else %}nics = [AhvVmNic.NormalNic.ingress(subnet="{{subnet_name}}", cluster="{{cluster_name}}")] {% endif %} @@ -164,8 +177,8 @@ class {{bp_name}}VmResources(AhvVmResources): config={ "users": [ { - "name": CENTOS_USER, - "ssh-authorized-keys": [CENTOS_PUBLIC_KEY], + "name": USERNAME, + "ssh-authorized-keys": [PUBLIC_KEY], "sudo": ["ALL=(ALL) NOPASSWD:ALL"], } ] @@ -241,9 +254,9 @@ class {{bp_name}}Profile(Profile): class {{bp_name}}(Blueprint): """ Sample blueprint for {{bp_name}} app using AHV VM""" - credentials = [CentosCred] + credentials = [CredObject] services = [{{bp_name}}Service] - packages = [{{bp_name}}Package, CentosPackage] + packages = [{{bp_name}}Package] # Add disk image object (if created above) in this package list (e.g. DiskPackage) substrates = [{{bp_name}}Substrate] profiles = [{{bp_name}}Profile] diff --git a/calm/dsl/init/blueprint/ahv_single_vm_blueprint.py.jinja2 b/calm/dsl/init/blueprint/ahv_single_vm_blueprint.py.jinja2 index f6c6490fd..288174c84 100644 --- a/calm/dsl/init/blueprint/ahv_single_vm_blueprint.py.jinja2 +++ b/calm/dsl/init/blueprint/ahv_single_vm_blueprint.py.jinja2 @@ -1,14 +1,14 @@ {% macro BlueprintTemplate(bp_name, subnet_name, cluster_name, vm_image, vpc_name) -%} # THIS FILE IS AUTOMATICALLY GENERATED. """ -Sample Calm DSL for {{bp_name}} blueprint +Sample NCM Self-Service DSL for {{bp_name}} blueprint The top-level folder contains the following files: HelloBlueprint/ ├── .local │ └── keys -│ ├── centos -│ └── centos_pub +│ ├── ssh_key +│ └── ssh_key_pub ├── blueprint.py └── scripts ├── pkg_install_task.sh @@ -16,9 +16,9 @@ HelloBlueprint/ On launch, this blueprint does the following: 1. Creates AHV VM (2 vCPUs, 4G Mem, 1 core) - 2. Installs {{vm_image}} image on vm. + 2. Creates placeholder (VM_DISK_IMAGE) to supply VM Disk Image 3. Injects SSH public key in the VM using cloud init. - 4. Creates calm credential using the SSH private key to run tasks on the VM. + 4. Creates NCM Self-Service credential using the SSH private key to run tasks on the VM. Order of execution for every deployment during blueprint launch: 1. Substrate.__pre_create__() (Only http and escript tasks are allowed here) @@ -38,6 +38,10 @@ Useful commands (execute from top-level directory): 8. calm delete app 9. calm delete bp +Remark: +1. Provided SSH Creds in this blueprint works only with VM Disk Images supporting cloud init guest customization. +2. For all other VM Disk Images, supply valid creds for blueprint to work properly. + """ import os @@ -54,13 +58,15 @@ from calm.dsl.builtins import Ref # SSH Credentials -CENTOS_USER = "centos" -CENTOS_KEY = read_local_file(os.path.join("keys", "centos")) -CENTOS_PUBLIC_KEY = read_local_file(os.path.join("keys", "centos_pub")) -CentosCred = basic_cred( - CENTOS_USER, CENTOS_KEY, name="Centos", type="KEY", default=True, +USERNAME = "admin" +PRIVATE_KEY = read_local_file(os.path.join("keys", "ssh_key")) +PUBLIC_KEY = read_local_file(os.path.join("keys", "ssh_key_pub")) +CredObject = basic_cred( + USERNAME, PRIVATE_KEY, name="admin", type="KEY", default=True, ) +VM_DISK_IMAGE = "" # Add VM Disk Image name here + class {{bp_name}}VmResources(AhvVmResources): @@ -68,18 +74,18 @@ class {{bp_name}}VmResources(AhvVmResources): vCPUs = 2 cores_per_vCPU = 1 disks = [ - AhvVmDisk.Disk.Scsi.cloneFromImageService("{{vm_image}}", bootable=True), + AhvVmDisk.Disk.Scsi.cloneFromImageService(VM_DISK_IMAGE, bootable=True), ] {% if vpc_name != "" %}nics = [AhvVmNic.NormalNic.ingress(subnet="{{subnet_name}}", vpc="{{vpc_name}}")] - {% else %}nics = [AhvVmNic.DirectNic.ingress(subnet="{{subnet_name}}", cluster="{{cluster_name}}")] + {% else %}nics = [AhvVmNic.NormalNic.ingress(subnet="{{subnet_name}}", cluster="{{cluster_name}}")] {% endif %} guest_customization = AhvVmGC.CloudInit( config={ "users": [ { - "name": CENTOS_USER, - "ssh-authorized-keys": [CENTOS_PUBLIC_KEY], + "name": USERNAME, + "ssh-authorized-keys": [PUBLIC_KEY], "sudo": ["ALL=(ALL) NOPASSWD:ALL"], } ] @@ -140,7 +146,7 @@ class {{bp_name}}Profile(VmProfile): class {{bp_name}}(VmBlueprint): # Credentials for blueprint - credentials = [CentosCred] + credentials = [CredObject] profiles = [{{bp_name}}Profile] diff --git a/calm/dsl/init/blueprint/render.py b/calm/dsl/init/blueprint/render.py index 57a0c0846..16068b675 100644 --- a/calm/dsl/init/blueprint/render.py +++ b/calm/dsl/init/blueprint/render.py @@ -297,26 +297,27 @@ def create_bp_file(dir_name, bp_name, provider_type, bp_type): bp_path = os.path.join(dir_name, "blueprint.py") LOG.info("Writing bp file to {}".format(bp_path)) + LOG.warning("Please fill required vm disk image in bp before using.") with open(bp_path, "w") as fd: fd.write(bp_text) def create_cred_keys(dir_name): - # Will create key via name centos/centos_pub + # Will create key via name ssh_key/ssh_key_pub key = RSA.generate(2048) # Write private key private_key = key.export_key("PEM") - private_key_filename = os.path.join(dir_name, "centos") + private_key_filename = os.path.join(dir_name, "ssh_key") with open(private_key_filename, "wb") as fd: fd.write(private_key) os.chmod(private_key_filename, 0o600) # Write public key public_key = key.publickey().export_key("OpenSSH") - public_key_filename = os.path.join(dir_name, "centos_pub") + public_key_filename = os.path.join(dir_name, "ssh_key_pub") with open(public_key_filename, "wb") as fd: fd.write(public_key) os.chmod(public_key_filename, 0o600) @@ -362,6 +363,9 @@ def init_bp(bp_name, dir_name, provider_type, bp_type): # Creating keys LOG.info("Generating keys for credentials") + LOG.info( + "These keys are valid for VM Disk Image supporting cloud-init guest customization" + ) create_cred_keys(key_dir) # create scripts diff --git a/calm/dsl/init/blueprint/scripts/pkg_install_task.sh b/calm/dsl/init/blueprint/scripts/pkg_install_task.sh index 8c9a76ff9..44b372144 100644 --- a/calm/dsl/init/blueprint/scripts/pkg_install_task.sh +++ b/calm/dsl/init/blueprint/scripts/pkg_install_task.sh @@ -2,9 +2,4 @@ set -ex -echo "@@{sample_pkg_var}@@" - -sudo yum install epel-release -y -sudo yum update -y - echo "Package installation steps go here ..." diff --git a/calm/dsl/init/provider/__init__.py b/calm/dsl/init/provider/__init__.py new file mode 100644 index 000000000..79093a731 --- /dev/null +++ b/calm/dsl/init/provider/__init__.py @@ -0,0 +1,3 @@ +from .render import init_provider + +__all__ = ["init_provider"] diff --git a/calm/dsl/init/provider/provider.py.jinja2 b/calm/dsl/init/provider/provider.py.jinja2 new file mode 100644 index 000000000..bb2fc4942 --- /dev/null +++ b/calm/dsl/init/provider/provider.py.jinja2 @@ -0,0 +1,184 @@ +{% macro ProviderTemplate(provider_name, pc_ip, pc_port) -%} +# THIS FILE IS AUTOMATICALLY GENERATED. +""" +Sample Calm DSL for {{provider_name}} provider + +The top-level folder contains the following files: +{{provider_name}}Provider/ +├── provider.py +└── scripts + └── verify_script.py + +On run, this provider does the following: + 1. Creates variables for Provider's authentication schema + 2. Creates variables for Provider's endpoint schema + 3. Creates variables for Provider attributes + 4. Creates credentials that can be leveraged in shell & powershell tasks + 5. Creates an action which contains the script to verify accounts created for the current provider + 6. Creates a test account which can be used for performing test executions on provider actions + 7. Creates Resource types that can be managed as part of the Provider + +Useful commands (execute from top-level directory): + 1. calm compile provider --file {{provider_name}}Provider/provider.py + 2. calm create provider --file {{provider_name}}Provider/provider.py --name + 3. calm get providers --name + 4. calm describe provider + 5. calm delete providers + 6. calm test provider-verify + 7. calm watch provider-verify-execution + 8. calm abort provider-verify-execution + 9. calm test resource-type-action + 10. calm watch resource-type-action-execution + 11. calm abort resource-type-action-execution + +""" +from calm.dsl.builtins import ( + CalmVariable, CloudProvider, action, ResourceType, + ProviderEndpointSchema, ProviderTestAccount, + basic_cred, read_local_file, +) +from calm.dsl.builtins.models.task import ProviderTask as CalmTask +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER, RESOURCE_TYPE + +# from calm.dsl.builtins import ( +# NutanixEndpointSchema, VmwareEndpointSchema, GCPEndpointSchema, +# AWSEndpointSchema, AzureEndpointSchema, NoneEndpointSchema, +# ) + +LINUX_IP = "10.10.10.10" +LINUX_CREDENTIAL = basic_cred( + "nutanix", read_local_file("linux_credential_key"), + name="LinuxJumpboxCred", type="KEY" +) + +WINDOWS_IP = "10.10.10.12" +WINDOWS_CREDENTIAL = basic_cred( + "administrator", name="WindowsJumpboxCred", password="anothersecret" +) + +class {{provider_name}}ResourceType(ResourceType): + """Sample ResourceType""" + + resource_kind = "Compute" + + # Icon + # icon_file = '/path_to_icon/icon.jpg' + # icon_name = 'myIcon' + + # Variables + variables = [ + CalmVariable.Simple.string(name="resource_type_var", value="value"), + ] + + # Schemas + schemas = [ + CalmVariable.Simple.string(name="resource_kind", value="compute"), + ] + + """ + Actions + - Only one action can be of type RESOURCE_TYPE.ACTION_TYPE.CREATE + - Only one action can be of type RESOURCE_TYPE.ACTION_TYPE.DELETE + - Only one action can be of type RESOURCE_TYPE.ACTION_TYPE.LIST + - All other actions should be of type RESOURCE_TYPE.ACTION_TYPE.GENERIC + """ + @action + def Create(type=RESOURCE_TYPE.ACTION_TYPE.CREATE): + """Create Action for {{provider_name}}ResourceType""" + CalmTask.Exec.ssh(name="ShellTask", script="pwd\nls\nwhoami\nsleep 5", cred=LINUX_CREDENTIAL, ip=LINUX_IP, port=22) + CalmTask.Exec.escript(name="Create Resource", script="print ('Creating an instance of {{provider_name}}ResourceType')") + + @action + def Delete(type=RESOURCE_TYPE.ACTION_TYPE.DELETE): + """Delete Action for {{provider_name}}ResourceType""" + CalmTask.Exec.escript(name="Delete Resource", script="print ('Deleting an instance of {{provider_name}}ResourceType')") + + @action + def List(type=RESOURCE_TYPE.ACTION_TYPE.LIST): + """List Action for {{provider_name}}ResourceType""" + outputs = [CalmVariable.Simple.string(name="resource_ids", value="")] + CalmTask.SetVariable.escript( + name="List Resources", variables=['resource_ids'], + script="print (\"resource_ids = ['resource1', 'resource2']\")" + ) + + @action + def Clone(): + """Clone Action for {{provider_name}}ResourceType""" + outputs = [CalmVariable.Simple.string(name="cloned_resource_id", value="")] + existing_resource_id = CalmVariable.Simple.string("") + CalmTask.Exec.powershell(name="PowershellTask", script="dir", cred=WINDOWS_CREDENTIAL, ip=WINDOWS_IP, port=5985, connection_protocol='http') + CalmTask.SetVariable.escript( + name="Clone existing Resources", variables=['cloned_resource_id'], + script="""print("Cloning existing HelloResourceType with id: @@{existing_resource_id}@@");print ("cloned_resource_id = new_resource")""" + ) + + +class {{provider_name}}Provider(CloudProvider): + """Sample provider for {{provider_name}}""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining Resource Types to be managed under this Provider + resource_types = [{{provider_name}}ResourceType] + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value="", is_mandatory=True, runtime=True), + CalmVariable.Simple.Secret.string(name="password", value="", is_mandatory=True, runtime=True), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string(name="server_ip", value="1.1.1.1", is_mandatory=True, runtime=True), + CalmVariable.Simple.int(name="port_number", value="443", is_mandatory=True, runtime=True) + ] + ) + + # Standard endpoint types can also be used the following way (Uncomment the corresponding import too) + # endpoint_schema = NutanixEndpointSchema() + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + CalmVariable.WithOptions.FromTask.string( + CalmTask.Exec.ssh(script="echo option1,option2,option3", cred=LINUX_CREDENTIAL, ip=LINUX_IP, port=22), + name="provider_dynamic_var", label="Dynamic Var", + ) + ] + + # Defining credentials to be consumed in shell & powershell tasks + credentials = [LINUX_CREDENTIAL, WINDOWS_CREDENTIAL] + + # Defining an account for performing test executions on Provider & ResourceType actions + # - Variable list of test account should be atleast the combination of + # (auth_schema_variables + endpoint_schema_variables), with just the values modified + test_account = ProviderTestAccount( + name='Test{{provider_name}}Account', description='Used for test executions', + variables=[ + CalmVariable.Simple.string(name="server_ip", value="10.10.10.10", is_mandatory=True, runtime=True), + CalmVariable.Simple.int(name="port_number", value="9440", is_mandatory=True, runtime=True), + CalmVariable.Simple.string(name="username", value="root", is_mandatory=True, runtime=True), + CalmVariable.Simple.Secret.string(name="password", value="iamasecret", is_mandatory=True, runtime=True), + CalmVariable.WithOptions.FromTask.string( + CalmTask.Exec.ssh(script="echo option1,option2,option3", cred=LINUX_CREDENTIAL, ip=LINUX_IP, port=22), + name="provider_dynamic_var", label="Dynamic Var", value="option2" + ) + ], + ) + + @action + def Verify(): # Name of the action must be 'Verify' + + """Verify action for Provider""" + + CalmTask.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py") + CalmTask.Exec.escript(name="PrintSuccessMessage", script="print ('Successfully Authenticated')") + + +{%- endmacro %} + + +{{ProviderTemplate(provider_name, pc_ip, pc_port)}} diff --git a/calm/dsl/init/provider/render.py b/calm/dsl/init/provider/render.py new file mode 100644 index 000000000..57b6b453c --- /dev/null +++ b/calm/dsl/init/provider/render.py @@ -0,0 +1,77 @@ +import os +from jinja2 import Environment, PackageLoader + +from calm.dsl.builtins import read_file +from calm.dsl.log import get_logging_handle +from calm.dsl.config import get_context + +LOG = get_logging_handle(__name__) + + +def render_provider_template(provider_name): + + schema_file = "provider.py.jinja2" + + loader = PackageLoader(__name__, "") + env = Environment(loader=loader) + template = env.get_template(schema_file) + LOG.info("Rendering provider template") + + ContextObj = get_context() + server_config = ContextObj.get_server_config() + pc_ip = server_config["pc_ip"] + pc_port = server_config["pc_port"] + text = template.render( + provider_name=provider_name, + pc_ip=pc_ip, + pc_port=pc_port, + ) + LOG.info("Success") + + return text.strip() + os.linesep + + +def create_provider_file(dir_name, provider_name): + + rb_text = render_provider_template(provider_name) + rb_path = os.path.join(dir_name, "provider.py") + + LOG.info("Writing provider file to {}".format(rb_path)) + with open(rb_path, "w") as fd: + fd.write(rb_text) + LOG.info("Success") + + +def create_scripts(dir_name): + + dir_path = os.path.dirname(os.path.realpath(__file__)) + scripts_dir = os.path.join(dir_path, "scripts") + for script_file in os.listdir(scripts_dir): + script_path = os.path.join(scripts_dir, script_file) + data = read_file(script_path) + + with open(os.path.join(dir_name, script_file), "w+") as fd: + fd.write(data) + + +def make_provider_dirs(dir_name, provider_name): + + provider_dir = "{}Provider".format(os.path.join(dir_name, provider_name)) + if not os.path.isdir(provider_dir): + os.makedirs(provider_dir) + + script_dir = os.path.join(provider_dir, "scripts") + if not os.path.isdir(script_dir): + os.makedirs(script_dir) + + return (provider_dir, script_dir) + + +def init_provider(provider_name, dir_name): + + provider_name = provider_name.strip().split()[0].title() + provider_dir, script_dir = make_provider_dirs(dir_name, provider_name) + + create_scripts(script_dir) + + create_provider_file(provider_dir, provider_name) diff --git a/calm/dsl/init/provider/scripts/verify_script.py b/calm/dsl/init/provider/scripts/verify_script.py new file mode 100644 index 000000000..133da5af2 --- /dev/null +++ b/calm/dsl/init/provider/scripts/verify_script.py @@ -0,0 +1,6 @@ +# Dummy script to verify credentials of custom provider accounts + +print("Account credentials being validated:") +print("Username: @@{username}@@, Password: @@{password}@@") +print("Making a dummy request to authenticate to the below provider endpoint") +print("Server IP: @@{server_ip}@@, Port: @@{port_number}@@") diff --git a/calm/dsl/init/runbook/runbook.py.jinja2 b/calm/dsl/init/runbook/runbook.py.jinja2 index deccf0eac..124f19765 100644 --- a/calm/dsl/init/runbook/runbook.py.jinja2 +++ b/calm/dsl/init/runbook/runbook.py.jinja2 @@ -54,6 +54,11 @@ def {{runbook_name}}(endpoints=[PCEndpoint, IPEndpoint]): runbooks_count = Variable.Simple.int("0", runtime=True) # noqa apps_count = Variable.Simple.int("0", runtime=True) # noqa + outputs = [ + Variable.Simple.string(name="blueprints_count", value=""), + Variable.Simple.string(name="runbooks_count", value=""), + ] + # HTTP Tasks to get CALM Entity Counts # default target is set as endpoints[0] = PCEndpoint, therefore target not required for http tasks Task.HTTP.post( diff --git a/calm/dsl/runbooks/__init__.py b/calm/dsl/runbooks/__init__.py index b4cfe3996..4076993a1 100644 --- a/calm/dsl/runbooks/__init__.py +++ b/calm/dsl/runbooks/__init__.py @@ -26,7 +26,12 @@ ) from calm.dsl.builtins.models.variable import RunbookVariable -from calm.dsl.builtins.models.task import RunbookTask, Status +from calm.dsl.builtins.models.task import ( + RunbookTask, + Status, + StatusHandle, + HTTPResponseHandle, +) from calm.dsl.builtins.models.runbook import ( Runbook, RunbookType, @@ -59,6 +64,8 @@ "RunbookVariable", "RunbookTask", "Status", + "StatusHandle", + "HTTPResponseHandle", "Runbook", "RunbookType", "runbook", diff --git a/examples/NutanixPaaS_provider/provider.py b/examples/NutanixPaaS_provider/provider.py new file mode 100644 index 000000000..c316a1fb8 --- /dev/null +++ b/examples/NutanixPaaS_provider/provider.py @@ -0,0 +1,5724 @@ +# THIS FILE IS AUTOMATICALLY GENERATED. +# Disclaimer: Please test this file before using in production. +""" +Generated provider DSL (.py) +""" + +import json # no_qa +import os # no_qa + +from calm.dsl.builtins import * # no_qa +from calm.dsl.builtins import CalmTask as CalmVarTask +from calm.dsl.builtins.models.task import Status, ProviderTask as CalmTask +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER, RESOURCE_TYPE +from calm.dsl.runbooks import parallel, branch + +# Secret Variables +# Note: Don't modify file data, as it is the encoded secrets fetched from the server +CloudProvider_NutanixPaaS_auth_schema_password = read_local_file( + "CloudProvider_NutanixPaaS_auth_schema_password" +) + +# Credentials + +# ResourceTypes + + +class VM(ResourceType): + """Resource Type to manage Nutanix Virtual Machines. Built using new V4 APIs. + + API version that start with prefix "a" are experimental and not meant for production use""" + + resource_kind = "Compute" + + icon_name = "VM" + + schemas = [] + + variables = [ + CalmVariable.Simple( + "v4.0.b1", + label="", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="vm_api_version", + ), + CalmVariable.Simple( + "v4.0.b2", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="cluster_mgt_api_version", + ), + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="networking_api_version", + ), + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="prism_api_version", + ), + ] + + @action + def Create(type="resource_type_create"): + """Action to create a VM""" + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete", + ) + categories = CalmVariable.Simple.multiline( + "", + label="Categories", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Comma separated names of categories ", + ) + cloud_init = CalmVariable.Simple.multiline( + "", + label="Cloud init", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Cloud init scripts used when creating the VM", + ) + sys_prep = CalmVariable.Simple.multiline( + "", + label="Sys Prep", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Sys prep scripts used when creating the VM", + ) + subnet = CalmVariable.Simple( + "", + label="Subnet", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the subnet to create the VM", + ) + iso_image = CalmVariable.Simple( + "", + label="ISO Image", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the iso image needed to create the CD-ROM", + ) + disk_image = CalmVariable.Simple( + "", + label="Disk Image", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the disk image needed to create the disk", + ) + storage_container = CalmVariable.Simple( + "", + label="Storage Container", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="This reference is for disk level storage container preference. This preference specifies the storage container to which this disk belongs", + ) + disk_size_in_bytes = CalmVariable.Simple.int( + "", + label="Size of the disk in Bytes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Size of the disk in Bytes\n\n", + ) + num_of_sockets = CalmVariable.Simple.int( + "1", + label="Number of vCPU sockets", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Number of vCPU sockets\n\n", + ) + num_cores_per_socket = CalmVariable.Simple.int( + "2", + label="Number of cores per socket", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Number of cores per socket", + ) + num_threads_per_core = CalmVariable.Simple.int( + "1", + label="Number of threads per core", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Number of threads per core", + ) + memory_size_bytes = CalmVariable.Simple.int( + "2000000000", + label="Memory size in bytes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Memory size in bytes", + ) + cluster = CalmVariable.Simple( + "", + label="Cluster", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Reference to a cluster\n\n", + ) + description = CalmVariable.Simple( + "", + label="VM description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="VM description\n\n", + ) + vm_name = CalmVariable.Simple( + "", + label="VM Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the VM to be created", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create VM", + filename=os.path.join( + "scripts", "ResourceType_VM_Action_Create_Task_CreateVM.py" + ), + variables=["task_uuid", "task_status"], + ) + + @action + def Delete(type="resource_type_delete"): + """Action to delete a VM""" + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete", + ) + vm_extId = CalmVariable.Simple( + "", + label="VM ExtID", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The globally unique identifier of a VM", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Delete VM", + filename=os.path.join( + "scripts", "ResourceType_VM_Action_Delete_Task_DeleteVM.py" + ), + variables=["task_uuid", "task_status"], + ) + + @action + def List(type="resource_type_list"): + """Action to List VMs""" + + page = CalmVariable.Simple.int( + "0", + label="Page", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results", + ) + limit = CalmVariable.Simple.int( + "50", + label="Limit", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set.", + ) + filter = CalmVariable.Simple( + "", + label="Filter", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'.", + ) + orderby = CalmVariable.Simple( + "name", + label="Order By", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in descending order", + ) + select = CalmVariable.Simple( + "", + label="Select", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. If a $select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="vms", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="List VMs", + filename=os.path.join( + "scripts", "ResourceType_VM_Action_List_Task_ListVMs.py" + ), + variables=["vms"], + ) + + @action + def PerformOperation(name="Perform Operation", type="resource_type_generic"): + """Action to perform a VM operation""" + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete", + ) + action = CalmVariable.WithOptions( + [ + "reboot", + "shutdown", + "guest-reboot", + "guest-shutdown", + "power-on", + "power-off", + "power-cycle", + "reset", + ], + label="Action", + default="power-on", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the action that needs to be performed", + ) + vm_extId = CalmVariable.Simple( + "", + label="VM ExtID", + regex="^.*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The globally unique identifier of a VM", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Perform VM operation", + filename=os.path.join( + "scripts", + "ResourceType_VM_Action_PerformOperation_Task_PerformVMoperation.py", + ), + variables=["task_uuid", "task_status"], + ) + + @action + def Clone(type="resource_type_generic"): + """Action to clone a VM""" + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete ", + ) + num_threads_per_core = CalmVariable.Simple.int( + "", + label="Number of threads per core", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Number of threads per core", + ) + num_cores_per_socket = CalmVariable.Simple.int( + "", + label="Number of cores per socket", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Number of cores per socket", + ) + num_of_sockets = CalmVariable.Simple.int( + "", + label="Number of vCPU sockets", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Number of vCPU sockets", + ) + memory_size_bytes = CalmVariable.Simple.int( + "", + label="Memory size in bytes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Memory size in bytes", + ) + clone_from_extId = CalmVariable.Simple( + "", + label="Clone From ExtId", + regex="^.*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The globally unique identifier of a VM", + ) + cloned_vm_name = CalmVariable.Simple( + "", + label="Cloned VM Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the cloned vm", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Clone VM", + filename=os.path.join( + "scripts", "ResourceType_VM_Action_Clone_Task_CloneVM.py" + ), + variables=["task_uuid", "task_status"], + ) + + @action + def Get(type="resource_type_generic"): + """Action to fetch a VM by name""" + + vm_extId = CalmVariable.Simple( + "", + label="VM ExtID", + regex="^.*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The globally unique identifier of a VM", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="vm", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Get VM", + filename=os.path.join( + "scripts", "ResourceType_VM_Action_Get_Task_GetVM.py" + ), + variables=["vm"], + ) + + @action + def Update(type="resource_type_generic"): + """Action to update a VM""" + + description = CalmVariable.Simple( + "", + label="VM description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="VM description", + ) + categories = CalmVariable.Simple.multiline( + "", + label="Categories", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Comma seperated names of categories", + ) + num_of_sockets = CalmVariable.Simple.int( + "", + label="Number of vCPU sockets", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Number of vCPU sockets", + ) + num_cores_per_socket = CalmVariable.Simple.int( + "", + label="Number of cores per socket", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Number of cores per socket", + ) + num_threads_per_core = CalmVariable.Simple.int( + "", + label="Number of threads per core", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Number of threads per core", + ) + memory_size_bytes = CalmVariable.Simple.int( + "", + label="Memory size in bytes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Memory size in bytes", + ) + cluster = CalmVariable.Simple( + "", + label="Cluster", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Reference to a cluster\n", + ) + subnet = CalmVariable.Simple( + "", + label="Subnet", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the subnet to create the VM", + ) + iso_image = CalmVariable.Simple( + "", + label="ISO Image", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the iso image needed to create the CD-ROM", + ) + disk_image = CalmVariable.Simple( + "", + label="Disk Image", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the disk image needed to create the disk", + ) + disk_size_in_bytes = CalmVariable.Simple.int( + "", + label="Size of the disk in Bytes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Size of the disk in Bytes", + ) + storage_container = CalmVariable.Simple( + "", + label="Storage Container", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="This reference is for disk level storage container preference. This preference specifies the storage container to which this disk belongs", + ) + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete ", + ) + cloud_init = CalmVariable.Simple.multiline( + "", + label="Cloud init", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Cloud init scripts used when creating the VM", + ) + sys_prep = CalmVariable.Simple.multiline( + "", + label="Sys Prep", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Sys prep scripts used when creating the VM", + ) + vm_extId = CalmVariable.Simple( + "", + label="VM ExtID", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The globally unique identifier of a VM", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Update VM", + filename=os.path.join( + "scripts", "ResourceType_VM_Action_Update_Task_UpdateVM.py" + ), + variables=["task_uuid", "task_status"], + ) + + +class Subnet(ResourceType): + """Resource Type to manage Nutanix Subnets""" + + resource_kind = "Network" + + icon_name = "Subnet" + + schemas = [] + + variables = [] + + @action + def CreateNutanixIPAMSubnet( + name="Create Nutanix IPAM Subnet", type="resource_type_create" + ): + """Action to create Nutanix IPAM""" + + description = CalmVariable.Simple( + "", + label="Description (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + boot_file_name = CalmVariable.Simple( + "", + label="Boot File Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + ) + tftp_server_name = CalmVariable.Simple( + "", + label="TFTP Server Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + domain_search_list = CalmVariable.Simple( + "", + label="Domain Search List CSV", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter domain search list - ,", + ) + domain_name = CalmVariable.Simple( + "", + label="Domain Name (Optional)", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + domain_name_server_list = CalmVariable.Simple( + "10.40.64.15,10.40.64.16", + label="DNS List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter comma separated dns servers eg , ", + ) + dhcp_server_address = CalmVariable.Simple( + "10.44.19.126", + label="DHCP Server Address (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + pool_list = CalmVariable.Simple( + '"10.44.19.66 10.44.19.125"', + label="IP Pool List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description='Enter pool list in this format eg." ", " " ', + ) + default_gateway_ip = CalmVariable.Simple( + "10.44.19.65", + label="Default Gateway IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + subnet_ip = CalmVariable.Simple( + "10.44.19.64/26", + label="Subnet IP Prefix", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter subnet IP with prefix eg 10.10.10.10/24", + ) + virtual_switch_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py", + ), + ), + label="Virtual Switch UUID", + is_mandatory=True, + is_hidden=False, + description="", + ) + vlan_id = CalmVariable.Simple( + "", + label="VLAN ID", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Enter vlan_id", + ) + subnet_name = CalmVariable.Simple( + "", + label="Subet Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Enter name of subnet", + ) + pe_cluster_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py", + ), + ), + label="PE Cluster UUID", + is_mandatory=True, + is_hidden=False, + description="Please select cluster", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create Nutanix IPAM", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_Task_CreateNutanixIPAM.py", + ), + variables=["task_uuid", "task_status"], + ) + + @action + def Delete(type="resource_type_delete"): + """Action to delete a subnet""" + + subnet_list = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_Delete_variable_subnet_list_Task_SampleTask.py", + ), + ), + label="", + is_mandatory=True, + is_hidden=False, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Delete Subnet", + filename=os.path.join( + "scripts", "ResourceType_Subnet_Action_Delete_Task_DeleteSubnet.py" + ), + variables=["task_uuid", "task_status"], + ) + + @action + def List(type="resource_type_list"): + """Action to list subnets""" + + length = CalmVariable.Simple.int( + "50", + label="", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The number of records to retrieve relative to the offset", + ) + sort_order = CalmVariable.WithOptions( + ["ASCENDING", "DESCENDING"], + label="", + default="ASCENDING", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + offset = CalmVariable.Simple.int( + "0", + label="", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Offset from the start of the entity list", + ) + sort_attribute = CalmVariable.Simple( + "name", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The attribute to perform sort on", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="subnet_list", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="SubnetList", + filename=os.path.join( + "scripts", "ResourceType_Subnet_Action_List_Task_SubnetList.py" + ), + variables=["subnet_list"], + ) + + @action + def CreateExternalIPAMSubnet( + name="Create External IPAM Subnet", type="resource_type_generic" + ): + """Action to create Nutanix IPAM""" + + description = CalmVariable.Simple( + "", + label="Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="optional", + ) + enable_nat = CalmVariable.WithOptions( + ["True", "False"], + label="Enable NAT?", + default="True", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Default - True", + ) + pool_list = CalmVariable.Simple( + "", + label="IP Pool List", + is_mandatory=True, + is_hidden=False, + runtime=True, + description='Enter pool list in this format eg." ", " " ', + ) + default_gateway_ip = CalmVariable.Simple( + "", + label="Default Gateway IP", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + subnet_ip = CalmVariable.Simple( + "", + label="Subnet IP Prefix", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Enter subnet ip with prefix eg 10.10.10.10/24", + ) + vlan_id = CalmVariable.Simple( + "", + label="VLAN ID", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + virtual_switch_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_CreateExternalIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py", + ), + ), + label="Virtual Switch UUID", + is_mandatory=True, + is_hidden=False, + description="", + ) + pe_cluster_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_CreateExternalIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py", + ), + ), + label="PE Cluster UUID", + is_mandatory=True, + is_hidden=False, + description="", + ) + subnet_name = CalmVariable.Simple( + "", + label="Subnet Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create External IPAM Subnet", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_CreateExternalIPAMSubnet_Task_CreateExternalIPAMSubnet.py", + ), + variables=["task_uuid", "task_status"], + ) + + @action + def CreateOverlaySubnet(name="Create Overlay Subnet", type="resource_type_generic"): + """Action to create an overlay subnet""" + + description = CalmVariable.Simple( + "", + label="Description (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + boot_file_name = CalmVariable.Simple( + "", + label="Boot File Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + tftp_server_name = CalmVariable.Simple( + "", + label="TFTP Server Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + domain_search_list = CalmVariable.Simple( + "", + label="Domain Search List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter domain search list - ,", + ) + domain_name_server_list = CalmVariable.Simple( + "10.40.64.15,10.40.64.16", + label="DNS List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter comma separated dns servers (optional)", + ) + domain_name = CalmVariable.Simple( + "", + label="Domain Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Optional", + ) + pool_list = CalmVariable.Simple( + "", + label="IP Pool List(Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description='Enter pool list in this format eg." ", " " ', + ) + default_gateway_ip = CalmVariable.Simple( + "", + label="Default Gateway IP", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + subnet_ip = CalmVariable.Simple( + "", + label="Subnet IP Prefix", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Enter subnet ip with prefix eg 10.10.10.10/24", + ) + vpc_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_CreateOverlaySubnet_variable_vpc_uuid_Task_SampleTask.py", + ), + ), + label="VPC UUID", + is_mandatory=True, + is_hidden=False, + description="", + regex="^.*$", + validate_regex=False, + ) + subnet_name = CalmVariable.Simple( + "", + label="Subnet Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create Overlay Subnet", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_CreateOverlaySubnet_Task_CreateOverlaySubnet.py", + ), + variables=["task_uuid", "task_status"], + ) + + @action + def Get(type="resource_type_generic"): + """Action to fetch a subnet""" + + subnet_name = CalmVariable.Simple( + "", + label="Subnet Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Enter name or FIQL format to search eg. [.*name.*]", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="subnets", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Get Subnet", + filename=os.path.join( + "scripts", "ResourceType_Subnet_Action_Get_Task_GetSubnet.py" + ), + variables=["subnets"], + ) + + @action + def UpdateNutanixIPAMSubnet( + name="Update Nutanix IPAM Subnet", type="resource_type_generic" + ): + """Action to update a Nutanix IPAM subnet""" + + description = CalmVariable.Simple( + "", + label="Description (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + boot_file_name = CalmVariable.Simple( + "", + label="Boot File Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + ) + tftp_server_name = CalmVariable.Simple( + "", + label="TFTP Server Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + ) + domain_search_list = CalmVariable.Simple( + "", + label="Domain Search List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter domain search list - ,", + ) + domain_name = CalmVariable.Simple( + "", + label="Domain Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + domain_name_server_list = CalmVariable.Simple( + "", + label="DNS List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter comma separated dns servers eg , ", + ) + dhcp_server_address = CalmVariable.Simple( + "", + label="DHCP Server Address (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="DHCP Server Address (Optional)", + ) + pool_list = CalmVariable.Simple( + "", + label="IP Pool List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description='Enter pool list in this format eg." ", " " ', + ) + default_gateway_ip = CalmVariable.Simple( + "", + label="Default Gateway IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If subnet_ip_prefix entered then please add def gateway ip too.", + ) + subnet_ip = CalmVariable.Simple( + "", + label="Subnet IP Prefix(Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + virtual_switch_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py", + ), + ), + label="Virtual Switch UUID", + is_mandatory=False, + is_hidden=False, + description="", + ) + vlan_id = CalmVariable.Simple( + "", + label="VLAN ID", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + subnet_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_subnet_name_Task_SampleTask.py", + ), + ), + label="Subnet Name", + is_mandatory=False, + is_hidden=False, + description="Select subnet to update.", + ) + pe_cluster_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py", + ), + ), + label="PE Cluster UUID", + is_mandatory=True, + is_hidden=False, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Update Nutanix IPAM Subnet", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_Task_UpdateNutanixIPAMSubnet.py", + ), + variables=["task_uuid", "task_status"], + ) + + @action + def UpdateExternalIPAMSubnet( + name="Update External IPAM Subnet", type="resource_type_generic" + ): + """Action to update an external IPAM subnet""" + + description = CalmVariable.Simple( + "", + label="Description (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + pool_list = CalmVariable.Simple( + "", + label="Add IP Pool List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description='Enter pool list in this format eg." ", " " ', + ) + default_gateway_ip = CalmVariable.Simple( + "", + label="Default Gateway IP (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + virtual_switch_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py", + ), + ), + label="Virtual Switch UUID", + is_mandatory=True, + is_hidden=False, + description="", + ) + subnet_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_subnet_name_Task_SampleTask.py", + ), + ), + label="Subnet Name", + is_mandatory=True, + is_hidden=False, + description="", + ) + pe_cluster_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py", + ), + ), + label="PE Cluster UUID", + is_mandatory=True, + is_hidden=False, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Update External Subnet", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_Task_UpdateExternalSubnet.py", + ), + variables=["task_uuid", "task_status"], + ) + + @action + def UpdateOverlaySubnet(name="Update Overlay Subnet", type="resource_type_generic"): + """Action to update an overlay subnet""" + + description = CalmVariable.Simple( + "", + label="Description (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + boot_file_name = CalmVariable.Simple( + "", + label="Boot File Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + tftp_server_name = CalmVariable.Simple( + "", + label="TFTP Server Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + domain_search_list = CalmVariable.Simple( + "", + label="Domain Search List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter domain search list - ,", + ) + domain_name_server_list = CalmVariable.Simple( + "", + label="DNS List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter comma separated dns servers (optional)", + ) + domain_name = CalmVariable.Simple( + "", + label="Domain Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + pool_list = CalmVariable.Simple( + "", + label="IP Pool List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description='Enter pool list in this format eg." ", " " ', + ) + subnet_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateOverlaySubnet_variable_subnet_name_Task_SampleTask.py", + ), + ), + label="Subnet Name", + is_mandatory=True, + is_hidden=False, + description="", + ) + pe_cluster_uuid = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateOverlaySubnet_variable_pe_cluster_uuid_Task_SampleTask.py", + ), + ), + label="PE Cluster UUID", + is_mandatory=True, + is_hidden=False, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="update_overlay_subnet", + filename=os.path.join( + "scripts", + "ResourceType_Subnet_Action_UpdateOverlaySubnet_Task_update_overlay_subnet.py", + ), + variables=["task_uuid", "task_status"], + ) + + +class VPC(ResourceType): + """Resource Type to manage Nutanix Virtual Private Clouds""" + + resource_kind = "Network" + + icon_name = "VPC" + + schemas = [] + + variables = [] + + @action + def Create(type="resource_type_create"): + """Action to create a VPC""" + + Description = CalmVariable.Simple( + "", + label="Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter Description (optional)", + ) + common_domain_name_server_ip_list = CalmVariable.Simple( + "", + label="DNS IPs (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter comma separated IP address (optional)", + ) + externally_routable_prefix_list = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter externally_routable_prefix_list with comma separated e.g 10.10.10.10/24(Optional)", + ) + external_subnet_reference = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VPC_Action_Create_variable_external_subnet_reference_Task_SampleTask.py", + ), + ), + label="External Subnet", + is_mandatory=True, + is_hidden=False, + description="", + ) + vpc_name = CalmVariable.Simple( + "", + label="VPC Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create VPC", + filename=os.path.join( + "scripts", "ResourceType_VPC_Action_Create_Task_CreateVPC.py" + ), + variables=["task_uuid", "task_status"], + ) + + @action + def Delete(type="resource_type_delete"): + """Action to delete a VPC""" + + subnet_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VPC_Action_Delete_variable_subnet_name_Task_SampleTask.py", + ), + ), + label="FYI: The following subnets will be deleted. Avoid executing the action if you don't want to delete them", + is_mandatory=False, + is_hidden=False, + description="The below subnet(s) will get deleted automatically, do not select/unselect. \nDo not execute if you don't want to delete them.", + ) + vpc_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VPC_Action_Delete_variable_vpc_name_Task_SampleTask.py", + ), + ), + label="VPC Name", + is_mandatory=True, + is_hidden=False, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="subnet_task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="subnet_task_status", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="vpc_task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="vpc_task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Delete Subnet", + filename=os.path.join( + "scripts", "ResourceType_VPC_Action_Delete_Task_DeleteSubnet.py" + ), + variables=["subnet_task_uuid", "subnet_task_status"], + ) + + CalmTask.SetVariable.escript.py3( + name="Delete VPC", + filename=os.path.join( + "scripts", "ResourceType_VPC_Action_Delete_Task_DeleteVPC.py" + ), + variables=["vpc_task_uuid", "vpc_task_status"], + ) + + @action + def List(type="resource_type_list"): + """Action to list VPCs""" + + offset = CalmVariable.Simple.int( + "0", + label="", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Offset from the start of the entity list", + ) + length = CalmVariable.Simple.int( + "50", + label="", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The number of records to retrieve relative to the offset", + ) + sort_order = CalmVariable.WithOptions( + ["ASCENDING", "DESCENDING"], + label="", + default="ASCENDING", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + sort_attribute = CalmVariable.Simple( + "name", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The attribute to perform sort on", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="vpc_list", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="List VPCs", + filename=os.path.join( + "scripts", "ResourceType_VPC_Action_List_Task_ListVPCs.py" + ), + variables=["vpc_list"], + ) + + @action + def Update(type="resource_type_generic"): + """Action to update a VPC""" + + Description = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + common_domain_name_server_ip_list = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + externally_routable_prefix_list = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter externally_routable_prefix_list with comma separated e.g 10.10.10.10/24 (Optional)", + ) + external_subnet_reference = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VPC_Action_Update_variable_external_subnet_reference_Task_SampleTask.py", + ), + ), + label="External Subnet", + is_mandatory=False, + is_hidden=False, + description="", + ) + vpc_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VPC_Action_Update_variable_vpc_name_Task_SampleTask.py", + ), + ), + label="VPC name", + is_mandatory=True, + is_hidden=False, + description="", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Update VPC", + filename=os.path.join( + "scripts", "ResourceType_VPC_Action_Update_Task_UpdateVPC.py" + ), + variables=["task_uuid", "task_status"], + ) + + +class SecurityPolicies(ResourceType): + """A Network Security Policy is a set of rules configured to protect entities across the network. You can use these APIs to create different types of policies such as Application, Isolation, and Quarantine with Inbound/Outbound as categories, IP Addresses or ranges, CIDR blocks, subnets and/or Address Groups/Service Groups.""" + + name = "Security Policies" + + resource_kind = "Network" + + icon_name = "FlowPolicies" + + schemas = [] + + variables = [ + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="flow_api_version", + ), + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="prism_api_version", + ), + ] + + @action + def CreateIsolationPolicy( + name="Create Isolation Policy", type="resource_type_create" + ): + + vpcReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_vpcReferences_Task_SampleTask.py", + ), + ), + label="VPC References", + is_mandatory=False, + is_hidden=False, + description="Select vpc values If scope is selected as VPC_LIST", + ) + isHitlogEnabled = CalmVariable.WithOptions( + ["False", "True"], + label="Is Hitlog Enabled", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + isIpv6TrafficAllowed = CalmVariable.WithOptions( + ["False", "True"], + label="Is Ipv6 Traffic Allowed", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + scope = CalmVariable.WithOptions( + ["ALL_VLAN", "VPC_LIST"], + label="Scope", + default="ALL_VLAN", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="ALL_VLAN : Policy is scoped to all the VLANs.\nVPC_LIST : Policy is scoped to the list of VPCs specified in vpcReferences", + ) + secondIsolationGroup = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_secondIsolationGroup_Task_SampleTask.py", + ), + ), + label="Second Isolation Group", + is_mandatory=False, + is_hidden=False, + description="", + ) + firstIsolationGroup = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_firstIsolationGroup_Task_SampleTask.py", + ), + ), + label="First Isolation Group", + is_mandatory=False, + is_hidden=False, + description="", + ) + policy_state = CalmVariable.WithOptions( + ["SAVE", "MONITOR", "ENFORCE"], + label="", + default="SAVE", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + description = CalmVariable.Simple( + "", + label="Description(Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_name = CalmVariable.Simple( + "", + label="Enter Isolation Policy Name", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="create_isolation_policy", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_Task_create_isolation_policy.py", + ), + variables=["task_uuid"], + ) + + @action + def Delete(type="resource_type_delete"): + + policy_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_Delete_variable_policy_name_Task_SampleTask.py", + ), + ), + label="Select policy to delete", + is_mandatory=True, + is_hidden=False, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Delete", + filename=os.path.join( + "scripts", "ResourceType_SecurityPolicies_Action_Delete_Task_Delete.py" + ), + variables=["task_uuid"], + ) + + @action + def List(type="resource_type_list"): + + orderby = CalmVariable.Simple( + "", + label="Order By", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in descending order", + ) + filter = CalmVariable.Simple( + "", + label="Filter", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'.", + ) + select = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + limit = CalmVariable.Simple.int( + "50", + label="Limit", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set.", + ) + page = CalmVariable.Simple.int( + "0", + label="Page", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results.\n\n", + ) + + CalmTask.SetVariable.escript.py3( + name="List", + filename=os.path.join( + "scripts", "ResourceType_SecurityPolicies_Action_List_Task_List.py" + ), + variables=["list_security_policies"], + ) + + @action + def Get(type="resource_type_generic"): + + extId = CalmVariable.Simple( + "", + label="ExtId", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Get", + filename=os.path.join( + "scripts", "ResourceType_SecurityPolicies_Action_Get_Task_Get.py" + ), + variables=["get_policies"], + ) + + @action + def CreateApplicationPolicyGeneric( + name="Create Application Policy Generic", type="resource_type_generic" + ): + + vpcReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_vpcReferences_Task_SampleTask.py", + ), + ), + label="Select VPC Reference", + is_mandatory=False, + is_hidden=False, + description="Select vpc when scope selected as VPC_LIST", + ) + dest_serviceGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_dest_serviceGroupReferences_Task_SampleTask.py", + ), + ), + label="3.9 Destination ServiceGroup References", + is_mandatory=False, + is_hidden=False, + description="This is a protocol type along with TCP, UDP, ICMP and will be added for w.r.t source selected", + ) + dest_AllGroupProtocolType = CalmVariable.Simple( + "", + label="3.9 All Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_AddressProtocolType = CalmVariable.Simple( + "", + label="3.7. Address Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_SubnetProtocolType = CalmVariable.Simple( + "", + label="3.6. Subnet Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_CategoryProtocolType = CalmVariable.Simple( + "", + label="3.5 Category Destination Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + destAllGroupType = CalmVariable.WithOptions( + ["No", "ALL"], + label="3.4 Destination AllGroupT ype", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + destAddressGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_destAddressGroupReferences_Task_SampleTask.py", + ), + ), + label="3.3 Destination Address Group References", + is_mandatory=False, + is_hidden=False, + description="", + ) + destSubnet = CalmVariable.Simple( + "", + label="3.2 Destination Subnet", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + destCategoryReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_destCategoryReferences_Task_SampleTask.py", + ), + ), + label="3.1 Destination Category References", + is_mandatory=False, + is_hidden=False, + description="List of categories that define a set of network endpoints as outbound.", + ) + src_serviceGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_src_serviceGroupReferences_Task_SampleTask.py", + ), + ), + label="2.9 Source ServiceGroup References", + is_mandatory=False, + is_hidden=False, + description="This is a protocol type along with TCP, UDP, ICMP and will be added for w.r.t source selected", + ) + src_AllGroupProtocolType = CalmVariable.Simple( + "", + label="2.8 All Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_AddressProtocolType = CalmVariable.Simple( + "", + label="2.6. Address Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_SubnetProtocolType = CalmVariable.Simple( + "", + label="2.7. Subnet Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_CategoryProtocolType = CalmVariable.Simple( + "", + label="2.5. Category Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + srcAllGroupType = CalmVariable.WithOptions( + ["No", "ALL"], + label="2.4 Source AllGroupType", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + srcAddressGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_srcAddressGroupReferences_Task_SampleTask.py", + ), + ), + label="2.3 Source AddressGroup References", + is_mandatory=False, + is_hidden=False, + description="", + ) + srcSubnet = CalmVariable.Simple( + "", + label="2.2 Source Subnet", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="ev IP/prefix", + ) + srcCategoryReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_srcCategoryReferences_Task_SampleTask.py", + ), + ), + label="2..1 Source Category References", + is_mandatory=False, + is_hidden=False, + description="List of categories that define a set of network endpoints as inbound", + ) + intra_groupAction = CalmVariable.WithOptions( + ["ALLOW", "DENY"], + label="1.1 Internal Entity Group Action", + default="ALLOW", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Traffic between Intra(secured) Entity Groups.", + ) + securedGroup = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_securedGroup_Task_SampleTask.py", + ), + ), + label="1. Select Secured Group", + is_mandatory=True, + is_hidden=False, + description="Select Secured Group to apply policy and a rule for specifying allowed traffic inside of a secured entity group.\nDo not select existing secured group.", + ) + isHitlogEnabled = CalmVariable.WithOptions( + ["False", "True"], + label="Is Hitlog Enabled", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + isIpv6TrafficAllowed = CalmVariable.WithOptions( + ["False", "True"], + label="Is Ipv6 Traffic Allowed", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + scope = CalmVariable.WithOptions( + ["ALL_VLAN", "VPC_LIST"], + label="Scope", + default="ALL_VLAN", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="ALL_VLAN : Policy is scoped to all the VLANs.\nVPC_LIST : Policy is scoped to the list of VPCs specified in vpcReferences", + ) + policy_state = CalmVariable.WithOptions( + ["SAVE", "MONITOR", "ENFORCE"], + label="", + default="SAVE", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + description = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_name = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Create Application Policy", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_Task_CreateApplicationPolicy.py", + ), + variables=["task_uuid"], + ) + + @action + def UpdateIsolationPolicy( + name="Update Isolation Policy", type="resource_type_generic" + ): + + vpcReferences = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_vpcReferences_Task_SampleTask.py", + ), + ), + label="VPC References", + is_mandatory=False, + is_hidden=False, + description="Select vpc values If scope is selected as VPC_LIST", + ) + isHitlogEnabled = CalmVariable.WithOptions( + ["False", "True"], + label="", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + isIpv6TrafficAllowed = CalmVariable.WithOptions( + ["False", "True"], + label="", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + scope = CalmVariable.WithOptions( + ["ALL_VLAN", "VPC_LIST"], + label="Scope", + default="ALL_VLAN", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="ALL_VLAN : Policy is scoped to all the VLANs.\nVPC_LIST : Policy is scoped to the list of VPCs specified in vpcReferences", + ) + secondIsolationGroup = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_secondIsolationGroup_Task_SampleTask.py", + ), + ), + label="Second Isolation Group", + is_mandatory=False, + is_hidden=False, + description="", + ) + firstIsolationGroup = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_firstIsolationGroup_Task_SampleTask.py", + ), + ), + label="", + is_mandatory=False, + is_hidden=False, + description="", + ) + policy_state = CalmVariable.WithOptions( + ["SAVE", "MONITOR", "ENFORCE"], + label="", + default="SAVE", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + description = CalmVariable.Simple( + "", + label="Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_new_name = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_policy_name_Task_SampleTask.py", + ), + ), + label="Select Policy Name to Update", + is_mandatory=False, + is_hidden=False, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="update_isolation_policy", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_Task_update_isolation_policy.py", + ), + variables=["task_uuid"], + ) + + @action + def UpdateApplicationPolicy( + name="Update Application Policy", type="resource_type_generic" + ): + + vpcReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_vpcReferences_Task_SampleTask.py", + ), + ), + label="Select VPC Reference", + is_mandatory=False, + is_hidden=False, + description="Select VPC when scope selected as VPC_LIST", + ) + dest_serviceGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py", + ), + ), + label="3.9 Destination ServiceGroup References", + is_mandatory=False, + is_hidden=False, + description="This is a protocol type along with TCP, UDP, ICMP and will be added for w.r.t source selected", + ) + dest_AllGroupProtocolType = CalmVariable.Simple( + "", + label="3.8 All Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_AddressProtocolType = CalmVariable.Simple( + "", + label="3.7. Address Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_SubnetProtocolType = CalmVariable.Simple( + "", + label="3.6. Subnet Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_CategoryProtocolType = CalmVariable.Simple( + "", + label="3.5 Category Destination Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + destAllGroupType = CalmVariable.WithOptions( + ["No", "ALL"], + label="3.4 Destination AllGroupT ype", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + destAddressGroupReferences = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_destAddressGroupReferences_Task_SampleTask.py", + ), + ), + label="3.3 Destination Address Group References", + is_mandatory=False, + is_hidden=False, + description="", + ) + destSubnet = CalmVariable.Simple( + "", + label="3.2 Destination Subnet", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + destCategoryReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_destCategoryReferences_Task_SampleTask.py", + ), + ), + label="3.1 Destination Category References", + is_mandatory=False, + is_hidden=False, + description="List of categories that define a set of network endpoints as outbound.", + ) + src_serviceGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py", + ), + ), + label="2.9 Source ServiceGroup References", + is_mandatory=False, + is_hidden=False, + description="This is a protocol type along with TCP, UDP, ICMP and will be added for w.r.t source selected", + ) + src_AllGroupProtocolType = CalmVariable.Simple( + "", + label="2.8 All Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_SubnetProtocolType = CalmVariable.Simple( + "", + label="2.7. Subnet Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_AddressProtocolType = CalmVariable.Simple( + "", + label="2.6. Address Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_CategoryProtocolType = CalmVariable.Simple( + "", + label="2.5. Category Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + srcAllGroupType = CalmVariable.WithOptions( + ["No", "ALL"], + label="2.4 Source AllGroupType", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + srcAddressGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py", + ), + ), + label="2.3 Source AddressGroup Reference", + is_mandatory=False, + is_hidden=False, + description="", + ) + srcSubnet = CalmVariable.Simple( + "", + label="2.2 Source Subnet", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="eg. IP/prefix", + ) + srcCategoryReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_srcCategoryReferences_Task_SampleTask.py", + ), + ), + label="2..1 Source Category References", + is_mandatory=False, + is_hidden=False, + description="List of categories that define a set of network endpoints as inbound", + ) + intra_groupAction = CalmVariable.WithOptions( + ["ALLOW", "DENY"], + label="1.1 Internal Entity Group Action", + default="ALLOW", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Traffic between Intra(secured) Entity Groups.", + ) + securedGroup = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_securedGroup_Task_SampleTask.py", + ), + ), + label="1. Select Secured Group", + is_mandatory=True, + is_hidden=False, + description="Select Secured Group to apply policy and a rule for specifying allowed traffic inside of a secured entity group.\nDo not select existing secured group.", + ) + isHitlogEnabled = CalmVariable.WithOptions( + ["False", "True"], + label="", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + isIpv6TrafficAllowed = CalmVariable.WithOptions( + ["False", "True"], + label="", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + scope = CalmVariable.WithOptions( + ["ALL_VLAN", "VPC_LIST"], + label="", + default="ALL_VLAN", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_state = CalmVariable.WithOptions( + ["SAVE", "MONITOR", "ENFORCE"], + label="", + default="SAVE", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + description = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_new_name = CalmVariable.Simple( + "", + label="New Policy Name", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_policy_name_Task_SampleTask.py", + ), + ), + label="Select policy to update", + is_mandatory=False, + is_hidden=False, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Update Application Policy", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_Task_UpdateApplicationPolicy.py", + ), + variables=["task_uuid"], + ) + + @action + def UpdateQuarantineForensicPolicy( + name="Update Quarantine Forensic Policy", type="resource_type_generic" + ): + + dest_serviceGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py", + ), + ), + label="3.9 Destination ServiceGroup References", + is_mandatory=False, + is_hidden=False, + description="This is a protocol type along with TCP, UDP, ICMP and will be added for w.r.t source selected", + ) + dest_AllGroupProtocolType = CalmVariable.Simple( + "", + label="3.8 All Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_AddressProtocolType = CalmVariable.Simple( + "", + label="3.7. Address Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_SubnetProtocolType = CalmVariable.Simple( + "", + label="3.6. Subnet Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_CategoryProtocolType = CalmVariable.Simple( + "", + label="3.5 Category Destination Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + destAllGroupType = CalmVariable.WithOptions( + ["No", "ALL"], + label="", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + destAddressGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_destAddressGroupReferences_Task_SampleTask.py", + ), + ), + label="3.3 Destination Address Group References", + is_mandatory=False, + is_hidden=False, + description="", + ) + destSubnet = CalmVariable.Simple( + "", + label="3.2 Destination Subnet", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + destCategoryReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_destCategoryReferences_Task_SampleTask.py", + ), + ), + label="3.1 Destination Category References", + is_mandatory=False, + is_hidden=False, + description="List of categories that define a set of network endpoints as outbound.", + ) + src_serviceGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py", + ), + ), + label="2.9 Source ServiceGroup References", + is_mandatory=False, + is_hidden=False, + description="This is a protocol type along with TCP, UDP, ICMP and will be added for w.r.t source selected", + ) + src_AllGroupProtocolType = CalmVariable.Simple( + "", + label="2.8 All Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_SubnetProtocolType = CalmVariable.Simple( + "", + label="2.7. Subnet Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_AddressProtocolType = CalmVariable.Simple( + "", + label="2.6. Address Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_CategoryProtocolType = CalmVariable.Simple( + "", + label="2.5. Category Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + srcAllGroupType = CalmVariable.WithOptions( + ["No", "ALL"], + label="2.4 Source AllGroupType", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + srcAddressGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py", + ), + ), + label="2.3 Source AddressGroup Reference", + is_mandatory=False, + is_hidden=False, + description="", + ) + srcSubnet = CalmVariable.Simple( + "", + label="2.2 Source Subnet", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="eg. IP/prefix", + ) + srcCategoryReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_srcCategoryReferences_Task_SampleTask.py", + ), + ), + label="2..1 Source Category References", + is_mandatory=False, + is_hidden=False, + description="List of categories that define a set of network endpoints as inbound", + ) + isHitlogEnabled = CalmVariable.WithOptions( + ["False", "True"], + label="", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + isIpv6TrafficAllowed = CalmVariable.WithOptions( + ["False", "True"], + label="", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_state = CalmVariable.WithOptions( + ["SAVE", "MONITOR", "ENFORCE"], + label="", + default="SAVE", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_name = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_policy_name_Task_SampleTask.py", + ), + ), + label="Select policy to update", + is_mandatory=False, + is_hidden=False, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="update quarantine forensic policy", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_Task_updatequarantineforensicpolicy.py", + ), + variables=["task_uuid"], + ) + + @action + def CreateVDIPolicy(name="Create VDI Policy", type="resource_type_generic"): + + vpcReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_vpcReferences_Task_SampleTask.py", + ), + ), + label="", + is_mandatory=False, + is_hidden=False, + description="", + ) + dest_serviceGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py", + ), + ), + label="3.9 Destination ServiceGroup References", + is_mandatory=False, + is_hidden=False, + description="This is a protocol type along with TCP, UDP, ICMP and will be added for w.r.t source selected", + ) + dest_AllGroupProtocolType = CalmVariable.Simple( + "", + label="3.9 All Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_AddressProtocolType = CalmVariable.Simple( + "", + label="3.7. Address Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_SubnetProtocolType = CalmVariable.Simple( + "", + label="3.6. Subnet Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + dest_CategoryProtocolType = CalmVariable.Simple( + "", + label="33.5 Category Destination Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + destAllGroupType = CalmVariable.WithOptions( + ["No", "ALL"], + label="", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + destAddressGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_destAddressGroupReferences_Task_SampleTask.py", + ), + ), + label="3.3 Destination Address Group References", + is_mandatory=False, + is_hidden=False, + description="", + ) + destSubnet = CalmVariable.Simple( + "", + label="3.2 Destination Subnet", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + destCategoryReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_destCategoryReferences_Task_SampleTask.py", + ), + ), + label="3.1 Destination Category References", + is_mandatory=False, + is_hidden=False, + description="List of categories that define a set of network endpoints as outbound.", + ) + src_serviceGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py", + ), + ), + label="2.9 Source ServiceGroup References", + is_mandatory=False, + is_hidden=False, + description="This is a protocol type along with TCP, UDP, ICMP and will be added for w.r.t source selected", + ) + src_AllGroupProtocolType = CalmVariable.Simple( + "", + label="2.8 All Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_AddressProtocolType = CalmVariable.Simple( + "", + label="2.6. Address Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_SubnetProtocolType = CalmVariable.Simple( + "", + label="2.7. Subnet Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + src_CategoryProtocolType = CalmVariable.Simple( + "", + label="2.5. Category Source Protocol Type with values", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" : , ; ..\neg. TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT", + ) + srcAllGroupType = CalmVariable.WithOptions( + ["No", "ALL"], + label="2.4 Source AllGroupType", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + srcAddressGroupReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py", + ), + ), + label="2.3 Source AddressGroup References", + is_mandatory=False, + is_hidden=False, + description="", + ) + srcSubnet = CalmVariable.Simple( + "", + label="2.2 Source Subnet", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="eg IP/prefix", + ) + srcCategoryReferences = CalmVariable.WithOptions.FromTask.Array( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_srcCategoryReferences_Task_SampleTask.py", + ), + ), + label="2.1 Source Category References", + is_mandatory=False, + is_hidden=False, + description="List of categories that define a set of network endpoints as inbound", + ) + intra_groupAction = CalmVariable.WithOptions( + ["ALLOW", "DENY"], + label="1.2 Internal Entity Group Action", + default="ALLOW", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + securedGroup = CalmVariable.Simple( + "ADGroup:$Default", + label="1.1 Secured AD Group", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Default AD Group. Do not change", + ) + isHitlogEnabled = CalmVariable.WithOptions( + ["False", "True"], + label="", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + isIpv6TrafficAllowed = CalmVariable.WithOptions( + ["False", "True"], + label="", + default="False", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + scope = CalmVariable.WithOptions( + ["ALL_VLAN", "VPC_LIST"], + label="", + default="ALL_VLAN", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_state = CalmVariable.WithOptions( + ["SAVE", "MONITOR", "ENFORCE"], + label="", + default="SAVE", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + description = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_name = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="VDI policy is for only reference purpose, not tested.", + ) + + CalmTask.SetVariable.escript.py3( + name="Create VDI policy", + filename=os.path.join( + "scripts", + "ResourceType_SecurityPolicies_Action_CreateVDIPolicy_Task_CreateVDIpolicy.py", + ), + variables=["task_uuid"], + ) + + +class VirtualNetworkPolicies(ResourceType): + """The Network Controller uses policy based routing on VPC networks. A Routing Policy belongs to a VPC and is described by one or more match rules and actions.""" + + name = "Virtual Network Policies" + + resource_kind = "Network" + + icon_name = "FlowPolicies" + + schemas = [] + + variables = [ + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="flow_virtual_network_api_version", + ), + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="prism_api_version", + ), + ] + + @action + def Create(type="resource_type_create"): + + description = CalmVariable.Simple( + "", + label="Policy Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + rerouteFallbackAction = CalmVariable.WithOptions( + ["PASSTHROUGH", "NO_ACTION", "ALLOW", "DROP"], + label="Reroute Fallback Action", + default="PASSTHROUGH", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Type of fallback action in reroute case when service VM is down.", + ) + forwardIp = CalmVariable.Simple( + "", + label="Forward IP address", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="if ActionType = FORWARD", + ) + serviceIp = CalmVariable.Simple( + "", + label="Service IP ", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If separate_reroute selected NO then enter service IP.", + ) + ingressServiceIp = CalmVariable.Simple( + "", + label="Ingress Service IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="if separate_reroute = YES then Enter ingressServiceIp", + ) + egressServiceIp = CalmVariable.Simple( + "", + label="Egress Service IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If separate_reroute = YES then select egressServiceIp", + ) + separate_reroute = CalmVariable.WithOptions( + ["Yes", "No"], + label="Separate Reroute", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Select YES If the policy action type = REROUTE and require separate ingress and egress reroute IPs.", + ) + isBidirectional = CalmVariable.WithOptions( + ["No", "Yes"], + label="Bidirectional", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Default: false\nIf True, policies in the reverse direction will be installed with the same action but source and destination will be swapped", + ) + actionType = CalmVariable.WithOptions( + ["PERMIT", "DENY", "REROUTE", "FORWARD"], + label="Action Type", + default="PERMIT", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + ICMP_DATA = CalmVariable.Simple( + "", + label="Enter ICMP details", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="if protocol type in ICMP then enter icmp type and icmp code. \neg ANY or icmptype_no : [ ANY | icmpcode_no ]", + ) + protocolNumber = CalmVariable.Simple.int( + "", + label="Enter Protocol Number", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Add only If protocolType selected as protocolNumber,", + ) + TCP_UDP_DATA = CalmVariable.Simple( + "", + label="Enter TCP or UDP Port range", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If protocol type TCP or UDP. StartPort : EndPort \neg. ANY:ANY or ANY:EndPortRange or StartPortRange:ANY", + ) + protocolType = CalmVariable.WithOptions( + ["ANY", "PROTOCOL_NUMBER", "TCP", "UDP", "ICMP"], + label="Select Protocol Type", + default="ANY", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Routing Policy Protocol Type.", + ) + destination_subnet_ip = CalmVariable.Simple( + "", + label="Destination Subnet IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If destination_addressType selected as CUSTOM", + ) + source_subnet_ip = CalmVariable.Simple( + "", + label="Source Subnet IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If source_addressType selected as CUSTOM", + ) + destination_addressType = CalmVariable.WithOptions( + ["ANY", "EXTERNAL", "CUSTOM"], + label="Destination Address Type", + default="ANY", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + source_addressType = CalmVariable.WithOptions( + ["ANY", "EXTERNAL", "CUSTOM"], + label="Source Address Type", + default="ANY", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + vpcExtId = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_Create_variable_vpcExtId_Task_SampleTask.py", + ), + ), + label="Select VPC", + is_mandatory=True, + is_hidden=False, + description="Routing policy will be created for selected VPC.", + ) + policy_name = CalmVariable.Simple( + "", + label="Policy Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + priority = CalmVariable.Simple.int( + "", + label="Priority", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="integer [ 1 .. 1000 ]", + ) + + CalmTask.SetVariable.escript.py3( + name="Create", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_Create_Task_Create.py", + ), + variables=["task_uuid"], + ) + + @action + def Delete(type="resource_type_delete"): + + delete_policy = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_Delete_variable_delete_policy_Task_SampleTask.py", + ), + ), + label="Select routing policy to delete", + is_mandatory=True, + is_hidden=False, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Delete", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_Delete_Task_Delete.py", + ), + variables=["task_uuid"], + ) + + @action + def List(type="resource_type_list"): + + orderby = CalmVariable.Simple( + "", + label="Orderby", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in descending order.", + ) + filter = CalmVariable.Simple( + "", + label="Filter", + is_mandatory=False, + is_hidden=False, + runtime=True, + description=" \nstring\nA URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'.", + ) + select = CalmVariable.Simple( + "", + label="Select", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. If a $select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned", + ) + limit = CalmVariable.Simple.int( + "50", + label="Limit", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Default: 50\nA URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set.", + ) + page = CalmVariable.Simple.int( + "0", + label="Page", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Default: 0\nA URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results.", + ) + + CalmTask.SetVariable.escript.py3( + name="List policies", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_List_Task_Listpolicies.py", + ), + variables=["list_policies"], + ) + + @action + def Get(type="resource_type_generic"): + + extId = CalmVariable.Simple( + "", + label="", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Get", + filename=os.path.join( + "scripts", "ResourceType_VirtualNetworkPolicies_Action_Get_Task_Get.py" + ), + variables=["get_policies"], + ) + + @action + def Update(type="resource_type_generic"): + + description = CalmVariable.Simple( + "", + label="Policy Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + rerouteFallbackAction = CalmVariable.WithOptions( + ["PASSTHROUGH", "NO_ACTION", "ALLOW", "DROP"], + label="Reroute Fallback Action", + default="PASSTHROUGH", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Type of fallback action in reroute case when service VM is down.", + ) + forwardIp = CalmVariable.Simple( + "", + label="Forward IP address", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="if ActionType = FORWARD", + ) + serviceIp = CalmVariable.Simple( + "", + label="Service IP ", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If separate_reroute selected NO then enter service IP.", + ) + ingressServiceIp = CalmVariable.Simple( + "", + label="Ingress Service IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="if separate_reroute = YES then Enter ingressServiceIp", + ) + egressServiceIp = CalmVariable.Simple( + "", + label="Egress Service IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If separate_reroute = YES then select egressServiceIp", + ) + separate_reroute = CalmVariable.WithOptions( + ["No", "Yes"], + label="Separate Reroute", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Select YES If the policy action type = REROUTE and require separate ingress and egress reroute IPs.", + ) + isBidirectional = CalmVariable.WithOptions( + ["No", "Yes"], + label="Bidirectional", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Default: false\nIf True, policies in the reverse direction will be installed with the same action but source and destination will be swapped", + ) + actionType = CalmVariable.WithOptions( + ["PERMIT", "DENY", "REROUTE", "FORWARD"], + label="Action Type", + default="PERMIT", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Imp:- You can not change existing actionType for update activity.", + ) + ICMP_DATA = CalmVariable.Simple( + "", + label="Enter ICMP details", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="if protocol type in ICMP then enter icmp type and icmp code. \neg ANY or icmptype_no : [ ANY | icmpcode_no ]", + ) + protocolNumber = CalmVariable.Simple( + "", + label="Enter Protocol Number", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Add only If protocolType selected as protocolNumber,", + ) + TCP_UDP_DATA = CalmVariable.Simple( + "", + label="Enter TCP or UDP Port range", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If protocol type TCP or UDP. \neg. ANY:ANY or ANY:EndPortRange or StartPortRange:ANY", + ) + protocolType = CalmVariable.WithOptions( + ["ANY", "PROTOCOL_NUMBER", "TCP", "UDP", "ICMP"], + label="Select Protocol Type", + default="ANY", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Routing Policy Protocol Type.", + ) + destination_subnet_ip = CalmVariable.Simple( + "", + label="Destination Subnet IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If destination_addressType selected as CUSTOM", + ) + source_subnet_ip = CalmVariable.Simple( + "", + label="Source Subnet IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="If source_addressType selected as CUSTOM", + ) + destination_addressType = CalmVariable.WithOptions( + ["ANY", "EXTERNAL", "CUSTOM"], + label="", + default="ANY", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + source_addressType = CalmVariable.WithOptions( + ["ANY", "EXTERNAL", "CUSTOM"], + label="", + default="ANY", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + policy_name = CalmVariable.Simple( + "", + label="Policy Name", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + vpcname = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_Update_variable_vpcname_Task_SampleTask.py", + ), + ), + label="Select Policy to update", + is_mandatory=True, + is_hidden=False, + description="", + ) + priority = CalmVariable.Simple.int( + "", + label="Priority", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Update", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_Update_Task_Update.py", + ), + variables=["task_uuid"], + ) + + @action + def ClearCounter(name="Clear Counter", type="resource_type_generic"): + + clear_counter = CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_ClearCounter_variable_clear_counter_Task_SampleTask.py", + ), + ), + label="", + is_mandatory=True, + is_hidden=False, + description="", + ) + select_all_policies = CalmVariable.WithOptions( + ["No", "Yes"], + label="Do you want to clear counter for all the policies under vpc.", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Clear_Counter", + filename=os.path.join( + "scripts", + "ResourceType_VirtualNetworkPolicies_Action_ClearCounter_Task_Clear_Counter.py", + ), + variables=["task_uuid"], + ) + + +class FileServer(ResourceType): + """Resource Type to manage Nutanix File Servers. Built using new V4 APIs. + + API version that start with prefix "a" are experimental and not meant for production use""" + + name = "File Server" + + resource_kind = "Storage" + + icon_name = "FileServer" + + schemas = [] + + variables = [ + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="prism_api_version", + ), + CalmVariable.Simple( + "v4.0.b2", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="cluster_mgt_api_version", + ), + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="networking_api_version", + ), + CalmVariable.Simple( + "v4.0.a5", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="files_api_version", + ), + ] + + @action + def Create(type="resource_type_create"): + + dns_servers = CalmVariable.Simple( + "", + label="DNS servers", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="List of DNS servers associated with the file server, accepts comma seperated values", + ) + ntp_servers = CalmVariable.Simple( + "", + label="NTP servers", + regex="^.*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The list of IP or FQDN of the NTP servers associated with the file server, accepts comma separated values", + ) + vm_count = CalmVariable.Simple.int( + "1", + label="VM Count", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Total number of file server VMs. This cannot exceed the number of available CVMs in the associated cluster.\n", + ) + type_of_deployment = CalmVariable.Simple( + "", + label="Deployment type", + regex="^.*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description='File server deployment type\n\nEnum Description\n"PRISM_CENTRAL" File server is deployed from prism central.\n"PRISM_ELEMENT" File server is deployed from prism element.\n', + ) + internal_network_vlan = CalmVariable.Simple( + "", + label="Internal network VLAN ", + regex="^.*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="File server network VLAN.", + ) + internal_network_subnet_mask = CalmVariable.Simple( + "", + label="Internal network subnet mask", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + internal_network_default_gateway = CalmVariable.Simple( + "", + label="Internal network default gateway", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + internal_network_ip_address = CalmVariable.Simple( + "", + label="Internal network ip address", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + external_network_vlan = CalmVariable.Simple( + "", + label="External network VLAN ", + regex="^.*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="File server network VLAN.", + ) + external_network_subnet_mask = CalmVariable.Simple( + "", + label="External network subnet mask", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + external_network_default_gateway = CalmVariable.Simple( + "", + label="External network default gateway", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + internal_network_is_managed = CalmVariable.WithOptions( + ["Yes", "No"], + label="Internal network is managed", + default="No", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Indicates whether the current file server network is managed or unmanaged.\n\n", + ) + external_network_ip_address = CalmVariable.Simple( + "", + label="External network ip address", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + ) + external_network_is_managed = CalmVariable.WithOptions( + ["No", "Yes"], + label="External network is managed", + default="No", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Indicates whether the current file server network is managed or unmanaged.\n\n", + ) + dns_domain_name = CalmVariable.Simple( + "", + label="DNS domain name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Fully qualified domain name (file server namespace). This, along with the file server name, constitutes the namespace of the file server. Example: fileserver_name.corp.companyname.com. This is also used to create file server DNS entries on the nameservers so that clients can access the file server using its name.", + ) + file_blocking_extensions = CalmVariable.Simple( + "", + label="File blocking extensions", + is_mandatory=False, + is_hidden=False, + runtime=True, + description='List of file blocking extensions or patterns. For Ex: [".db",".txt",".mp3"].\n\n', + ) + compression_enabled = CalmVariable.WithOptions( + ["Yes", "No"], + label="Compression enabled", + default="Yes", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Filesystem compression value. For file servers greater than 3.6 version, the filesystem compression is enabled by default.\n\n", + ) + rebalance_enabled = CalmVariable.WithOptions( + ["Yes", "No"], + label="Rebalance enabled", + default="No", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="This flag indicates if file server recommendations are available and the user has to perform actions.\n", + ) + file_server_version = CalmVariable.Simple( + "", + label="File server version", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="File server version.", + ) + file_server_size_in_gb = CalmVariable.Simple.int( + "", + label="File server size", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="File server size in Gib.\n\n", + ) + storage_container = CalmVariable.Simple( + "", + label="Storage Container", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The container associated with the file server.\n\n", + ) + file_server_name = CalmVariable.Simple( + "", + label="Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="File server name.\n\n", + ) + cluster_name = CalmVariable.Simple( + "", + label="Cluster name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Cluster name for on prem deployment", + ) + vm_memory_gib = CalmVariable.Simple.int( + "12", + label="VM Memory in GB", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="VM Memory in GB", + ) + vm_vcpu = CalmVariable.Simple.int( + "4", + label="VM VCPUS", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="VM VCPUS", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create file server", + filename=os.path.join( + "scripts", + "ResourceType_FileServer_Action_Create_Task_Createfileserver.py", + ), + variables=["task_uuid"], + ) + + @action + def List(type="resource_type_list"): + + select = CalmVariable.Simple( + "extId, name, sizeInGib", + label="Select", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. If a $select expression consists of a single select item that is an asterisk (i.e. *), then all properties on the matching resource will be returned.", + ) + order_by = CalmVariable.Simple( + "name", + label="Order by", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in desc order. The orderby can be applied to the following fields:\n\ncompressionEnabled\ncontainerExtId\ndnsDomainName\nisCompressionEnabled\nname\nsizeGiBytes\nsizeInGib\nversion", + ) + filter = CalmVariable.Simple( + "", + label="Filter", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter result on cluster name karbon-ntnx1.0, filter '$filter=startswith(name, 'C')' would filter on cluster name starting with C. The filter can be applied on the following fields:\n\ncompressionEnabled\ncontainerExtId\ndnsDomainName\nisCompressionEnabled\nname\nsizeGiBytes\nsizeInGib\nversion", + ) + limit = CalmVariable.Simple.int( + "50", + label="Limit", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided a default value of 50 records will be returned in the result set.", + ) + page = CalmVariable.Simple.int( + "0", + label="Page", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the page number of the result set. Must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range will lead to no results being returned.", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="file_servers", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="List file servers", + filename=os.path.join( + "scripts", "ResourceType_FileServer_Action_List_Task_Listfileservers.py" + ), + variables=["file_servers"], + ) + + @action + def Get(type="resource_type_generic"): + + file_server_name = CalmVariable.Simple( + "", + label="File server name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The name of the file server", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="file_server", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Get file server", + filename=os.path.join( + "scripts", "ResourceType_FileServer_Action_Get_Task_Getfileserver.py" + ), + variables=["file_server"], + ) + + @action + def Update(type="resource_type_generic"): + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait for the operation to complete", + default="Yes", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + dns_servers = CalmVariable.Simple( + "", + label="DNS servers", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="List of DNS servers associated with the file server, accepts comma seperated values", + ) + ntp_servers = CalmVariable.Simple( + "", + label="NTP servers", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The list of IP or FQDN of the NTP servers associated with the file server, accepts comma separated values", + ) + vm_count = CalmVariable.Simple.int( + "", + label="VM Count", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Total number of file server VMs. This cannot exceed the number of available CVMs in the associated cluster.\n", + ) + dns_domain_name = CalmVariable.Simple( + "", + label="DNS domain name", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Fully qualified domain name (file server namespace). This, along with the file server name, constitutes the namespace of the file server. Example: fileserver_name.corp.companyname.com. This is also used to create file server DNS entries on the nameservers so that clients can access the file server using its name.", + ) + file_blocking_extensions = CalmVariable.Simple( + "", + label="File blocking extensions", + is_mandatory=False, + is_hidden=False, + runtime=True, + description='List of file blocking extensions or patterns. For Ex: [".db",".txt",".mp3"].\n\n', + ) + compression_enabled = CalmVariable.WithOptions( + ["Yes", "No", "-"], + label="Compression enabled", + default="-", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Filesystem compression value. For file servers greater than 3.6 version, the filesystem compression is enabled by default.\n\n", + ) + rebalance_enabled = CalmVariable.WithOptions( + ["Yes", "No", "-"], + label="Rebalance enabled", + default="-", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="This flag indicates if file server recommendations are available and the user has to perform actions.\n", + ) + file_server_size_in_gb = CalmVariable.Simple.int( + "", + label="File server size", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="File server size in Gib.\n\n", + ) + vm_vcpu = CalmVariable.Simple.int( + "", + label="VM VCPUS", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="VM VCPUS", + ) + vm_memory_gib = CalmVariable.Simple.int( + "", + label="VM Memory in GB", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="VM Memory in GB", + ) + file_server_name = CalmVariable.Simple( + "", + label="Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="File server name.\n\n", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Update file server", + filename=os.path.join( + "scripts", + "ResourceType_FileServer_Action_Update_Task_Updatefileserver.py", + ), + variables=["task_uuid"], + ) + + @action + def Listmounttargets(name="List mount targets", type="resource_type_generic"): + + select = CalmVariable.Simple( + "name,extId,maxSizeGiBytes,path,protocol", + label="Select", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. If a $select expression consists of a single select item that is an asterisk (i.e. *), then all properties on the matching resource will be returned.", + ) + order_by = CalmVariable.Simple( + "name", + label="Order by", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in desc order. The orderby can be applied to the following fields:", + ) + filter = CalmVariable.Simple( + "", + label="Filter", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter result on cluster name karbon-ntnx1.0, filter '$filter=startswith(name, 'C')' would filter on cluster name starting with C", + ) + limit = CalmVariable.Simple.int( + "50", + label="Limit", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided a default value of 50 records will be returned in the result set.", + ) + page = CalmVariable.Simple.int( + "0", + label="Page", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the page number of the result set. Must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range will lead to no results being returned.", + ) + file_server_name = CalmVariable.Simple( + "", + label="File server name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the file server to get the mounts for", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="mount_targets", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="List mount targets", + filename=os.path.join( + "scripts", + "ResourceType_FileServer_Action_Listmounttargets_Task_Listmounttargets.py", + ), + variables=["mount_targets"], + ) + + @action + def CreateSMBmount(name="Create SMB mount", type="resource_type_generic"): + + description = CalmVariable.Simple( + "", + label="Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Mount target description.\n\n", + ) + type = CalmVariable.WithOptions( + ["GENERAL", "DISTRIBUTED", "STANDARD", "HOMES"], + label="Type", + default="GENERAL", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Supported mount target types.\n\n", + ) + access_based_enumeration_enabled = CalmVariable.WithOptions( + ["No", "Yes"], + label="Access Based Enumeration Enbaled", + default="No", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Flag to enable access based enumeration.\n\n", + ) + encryption_enabled = CalmVariable.WithOptions( + ["No", "Yes"], + label="Encryption Enabled", + default="No", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Flag to enable SMB3 encryption.\n\n", + ) + ca_enabled = CalmVariable.WithOptions( + ["No", "Yes"], + label="CA Enabled", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Flag to enable continuous availability feature for SMB mount targets.", + ) + share_acl = CalmVariable.Simple.multiline( + "", + label="Share ACL", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Access control list(ACL) for SMB share\n\nAccepts command seperated values in format\n::, ...", + ) + compression_enabled = CalmVariable.WithOptions( + ["No", "Yes"], + label="Compression Enabled", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Flag to enable compression.\n\n", + ) + max_size_in_gb = CalmVariable.Simple.int( + "", + label="Max size in GB", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Maximum size of mount target in GiB.\n\n", + ) + path = CalmVariable.Simple( + "", + label="Path", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Path of the nested mount target.\n\n", + ) + mount_target_name = CalmVariable.Simple( + "", + label="Mount target name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Mount target name.\n\n", + ) + file_server_name = CalmVariable.Simple( + "", + label="File server name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the file server to create mount target ", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create SMB mount", + filename=os.path.join( + "scripts", + "ResourceType_FileServer_Action_CreateSMBmount_Task_CreateSMBmount.py", + ), + variables=["task_uuid"], + ) + + @action + def Getmounttarget(name="Get mount target", type="resource_type_generic"): + + file_server_name = CalmVariable.Simple( + "", + label="File server name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the file server", + ) + mount_target_name = CalmVariable.Simple( + "", + label="Mount target name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Mount target name", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="mount_target", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Get mount target", + filename=os.path.join( + "scripts", + "ResourceType_FileServer_Action_Getmounttarget_Task_Getmounttarget.py", + ), + variables=["mount_target"], + ) + + @action + def UpdateSMBmount(name="Update SMB mount", type="resource_type_generic"): + + description = CalmVariable.Simple( + "", + label="Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Mount target description.\n\n", + ) + type = CalmVariable.WithOptions( + ["GENERAL", "DISTRIBUTED", "STANDARD", "HOMES"], + label="Type", + default="GENERAL", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Supported mount target types.\n\n", + ) + access_based_enumeration_enabled = CalmVariable.WithOptions( + ["No", "Yes", "-"], + label="Access Based Enumeration Enbaled", + default="-", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Flag to enable access based enumeration.\n\n", + ) + encryption_enabled = CalmVariable.WithOptions( + ["No", "Yes", "-"], + label="Encryption Enabled", + default="-", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Flag to enable SMB3 encryption.\n\n", + ) + ca_enabled = CalmVariable.WithOptions( + ["No", "Yes", "-"], + label="CA Enabled", + default="-", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Flag to enable continuous availability feature for SMB mount targets.", + ) + share_acl = CalmVariable.Simple.multiline( + "", + label="Share ACL", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Access control list(ACL) for SMB share\n\nAccepts command seperated values in format\n::, ...", + ) + compression_enabled = CalmVariable.WithOptions( + ["No", "Yes", "-"], + label="Compression Enabled", + default="-", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Flag to enable compression.\n\n", + ) + max_size_in_gb = CalmVariable.Simple.int( + "", + label="Max size in GB", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Maximum size of mount target in GiB.\n\n", + ) + path = CalmVariable.Simple( + "", + label="Path", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Path of the nested mount target.\n\n", + ) + mount_target_name = CalmVariable.Simple( + "", + label="Mount target name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Mount target name.\n\n", + ) + file_server_name = CalmVariable.Simple( + "", + label="File server name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the file server to create mount target ", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Update SMB mount", + filename=os.path.join( + "scripts", + "ResourceType_FileServer_Action_UpdateSMBmount_Task_UpdateSMBmount.py", + ), + variables=["task_uuid"], + ) + + +class ObjectStore(ResourceType): + """Resource Type to manage Nutanix Object Stores. + + API version that start with prefix "a" are experimental and not meant for production use""" + + name = "Object Store" + + resource_kind = "Storage" + + icon_name = "Object Store" + + schemas = [] + + variables = [ + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="networking_api_version", + ), + CalmVariable.Simple( + "v4.0.b2", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="cluster_mgt_api_version", + ), + CalmVariable.Simple( + "v4.0.b1", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="prism_api_version", + ), + CalmVariable.Simple( + "v4.0.a2", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="objects_api_version", + ), + ] + + @action + def Create(type="resource_type_create"): + """Action to create an object store""" + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete ", + ) + total_capacity_gib = CalmVariable.Simple.int( + "", + label="Total Capacity in GB", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Size of the Object store in GiB.\n\n", + ) + public_network_ips = CalmVariable.Simple( + "", + label="Public Network IPs", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A list of static IP addresses used as public IPs to access the Object store. (comma separated)\n\n", + ) + public_network = CalmVariable.Simple( + "", + label="Public Network", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Public network reference of the Object store. This is the subnet for AHV cluster or the IPAM name for an ESXi cluster.", + ) + storage_network_dns_ip = CalmVariable.Simple( + "", + label="Storage Network DNS IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="An unique address that identifies a device on the internet or a local network in IPv4.", + ) + storage_network_vip = CalmVariable.Simple( + "", + label="Storage Network VIP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="An unique address that identifies a device on the internet or a local network in IPv4.", + ) + storage_network = CalmVariable.Simple( + "", + label="Storage Network", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Reference to the Storage Network of the Object store. This is the subnet for an AHV cluster or the IPAM name for an ESXi cluster.\n\n", + ) + cluster = CalmVariable.Simple( + "", + label="Cluster", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the cluster ", + ) + number_of_worker_nodes = CalmVariable.Simple.int( + "", + label="Number of worker nodes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The number of worker nodes (VMs) to be created for the Object store. Each worker node requires 10 vCPUs and 32 GiB of memory.", + ) + region = CalmVariable.Simple( + "", + label="Region", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The region in which the Object store is deployed", + ) + domain = CalmVariable.Simple( + "", + label="Domain", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The DNS domain/subdomain the Object store belongs to. All the Object stores under one Prism Central must have the same domain name. The domain name must consist of at least 2 parts separated by a '.'. Each part can contain upper and lower case letters, digits, hyphens, or underscores. Each part can be up to 63 characters long. The domain must begin and end with an alphanumeric character. For example - 'objects-0.pc_nutanix.com'", + ) + deployment_version = CalmVariable.Simple( + "", + label="Deployment Version", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Object store deployment version", + ) + description = CalmVariable.Simple( + "", + label="Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A brief description of the Object store", + ) + object_store_name = CalmVariable.Simple( + "", + label="Object Store Name", + regex="^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9]))*([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])$", + validate_regex=True, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The name of the Object store.\n\n", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create Object Storage", + filename=os.path.join( + "scripts", + "ResourceType_ObjectStore_Action_Create_Task_CreateObjectStorage.py", + ), + variables=["task_uuid", "task_status"], + ) + + @action + def Delete(type="resource_type_delete"): + """Action to delete an object store""" + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete", + ) + object_store_name = CalmVariable.Simple( + "", + label="", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The name of the object store that needs to be deleted", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Delete object store", + filename=os.path.join( + "scripts", + "ResourceType_ObjectStore_Action_Delete_Task_Deleteobjectstore.py", + ), + variables=["task_uuid", "task_status"], + ) + + @action + def List(type="resource_type_list"): + """Action to list object stores""" + + select = CalmVariable.Simple( + "*", + label="Select", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. If a $select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned.", + ) + expand = CalmVariable.Simple( + "", + label="Expand", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to request related resources when a resource that satisfies a particular request is retrieved. Each expanded item is evaluated relative to the entity containing the property being expanded. Other query options can be applied to an expanded property by appending a semicolon-separated list of query options, enclosed in parentheses, to the property name. Permissible system query options are $filter, $select and $orderby.", + ) + orderby = CalmVariable.Simple( + "name", + label="Order by", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in descending order", + ) + filter = CalmVariable.Simple( + "", + label="Filter", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'.", + ) + limit = CalmVariable.Simple.int( + "50", + label="Limit", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set.", + ) + page = CalmVariable.Simple.int( + "0", + label="Page", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results.\n\n", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="object_stores", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="List Object Store", + filename=os.path.join( + "scripts", + "ResourceType_ObjectStore_Action_List_Task_ListObjectStore.py", + ), + variables=["object_stores"], + ) + + @action + def Get(type="resource_type_generic"): + """Action to fetch a specified object store""" + + object_store_name = CalmVariable.Simple( + "", + label="Object Store Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The name of the object store", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="object_store", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Get an object store", + filename=os.path.join( + "scripts", + "ResourceType_ObjectStore_Action_Get_Task_Getanobjectstore.py", + ), + variables=["object_store"], + ) + + @action + def Update(type="resource_type_generic"): + """Action to update an object store""" + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete ", + ) + total_capacity_gib = CalmVariable.Simple.int( + "", + label="Total Capacity in GB", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Size of the Object store in GiB.\n\n", + ) + public_network_ips = CalmVariable.Simple( + "", + label="Public Network IPs", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A list of static IP addresses used as public IPs to access the Object store.\n\n", + ) + public_network = CalmVariable.Simple( + "", + label="Public Network", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Public network reference of the Object store. This is the subnet for AHV cluster or the IPAM name for an ESXi cluster.", + ) + storage_network_dns_ip = CalmVariable.Simple( + "", + label="Storage Network DNS IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="An unique address that identifies a device on the internet or a local network in IPv4.", + ) + storage_network_vip = CalmVariable.Simple( + "", + label="Storage Network VIP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="An unique address that identifies a device on the internet or a local network in IPv4.", + ) + storage_network = CalmVariable.Simple( + "", + label="Storage Network", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Reference to the Storage Network of the Object store. This is the subnet for an AHV cluster or the IPAM name for an ESXi cluster.\n\n", + ) + cluster = CalmVariable.Simple( + "", + label="Cluster", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the cluster ", + ) + number_of_worker_nodes = CalmVariable.Simple.int( + "", + label="Number of worker nodes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The number of worker nodes (VMs) to be created for the Object store. Each worker node requires 10 vCPUs and 32 GiB of memory.", + ) + region = CalmVariable.Simple( + "", + label="Region", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The region in which the Object store is deployed", + ) + domain = CalmVariable.Simple( + "", + label="Domain", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="The DNS domain/subdomain the Object store belongs to. All the Object stores under one Prism Central must have the same domain name. The domain name must consist of at least 2 parts separated by a '.'. Each part can contain upper and lower case letters, digits, hyphens, or underscores. Each part can be up to 63 characters long. The domain must begin and end with an alphanumeric character. For example - 'objects-0.pc_nutanix.com'", + ) + deployment_version = CalmVariable.Simple( + "", + label="Deployment Version", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Object store deployment version", + ) + description = CalmVariable.Simple( + "", + label="Description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A brief description of the Object store", + ) + object_store_name = CalmVariable.Simple( + "", + label="Object Store Name", + regex="^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9]))*([A-Za-z]|[A-Za-z][A-Za-z0-9-]*[A-Za-z0-9])$", + validate_regex=True, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The name of the Object store that needs to be updated\n\n", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_uuid", + ), + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Update Object Storage", + filename=os.path.join( + "scripts", + "ResourceType_ObjectStore_Action_Update_Task_UpdateObjectStorage.py", + ), + variables=["task_uuid", "task_status"], + ) + + +class Bucket(ResourceType): + """Resource Type to manage Nutanix Buckets. + + API version that start with prefix "a" are experimental and not meant for production use""" + + resource_kind = "Storage" + + icon_name = "Bucket" + + schemas = [] + + variables = [ + CalmVariable.Simple( + "v4.0.a2", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="objects_api_version", + ), + ] + + @action + def Create(type="resource_type_create"): + """Action to create a Bucket in specified object store""" + + wait = CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="whether to wait for the task to completed", + ) + non_current_version_expiration_days = CalmVariable.Simple.int( + "", + label="Non current version expiration", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Non current version expiration", + ) + expiration_days = CalmVariable.Simple.int( + "", + label="Expiration Days", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Expiration Days", + ) + enable_versioning = CalmVariable.WithOptions( + ["Yes", "No"], + label="Enable Versioning", + default="No", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enable or disable versioning of bucket", + ) + bucket_name = CalmVariable.Simple( + "", + label="Bucket Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the bucket to be created", + ) + object_store_name = CalmVariable.Simple( + "", + label="Object store name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the object store", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="task_status", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create Bucket", + filename=os.path.join( + "scripts", "ResourceType_Bucket_Action_Create_Task_CreateBucket.py" + ), + variables=["task_status"], + ) + + @action + def Delete(type="resource_type_delete"): + """Action to delete a Bucket in specified object store""" + + bucket_name = CalmVariable.Simple( + "", + label="Bucket Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Bucket name", + ) + object_store_name = CalmVariable.Simple( + "", + label="Object Store Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the object store", + ) + + CalmTask.Exec.escript.py3( + name="Delete Bucket", + filename=os.path.join( + "scripts", "ResourceType_Bucket_Action_Delete_Task_DeleteBucket.py" + ), + ) + + @action + def List(type="resource_type_list"): + """Action to list buckets in a specified object store""" + + object_store_name = CalmVariable.Simple( + "", + label="Object Store Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Object Store Name", + ) + + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="buckets", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="List Buckets", + filename=os.path.join( + "scripts", "ResourceType_Bucket_Action_List_Task_ListBuckets.py" + ), + variables=["buckets"], + ) + + @action + def ModifyAccess(name="Modify Access", type="resource_type_generic"): + """Action to modify acess of a Bucket""" + + permissions = CalmVariable.WithOptions.Predefined.Array( + ["READ", "WRITE", "NONE"], + label="Permissions", + defaults=["READ"], + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Permissions", + ) + username = CalmVariable.Simple( + "", + label="Username", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Username in name_prefix@domain format", + ) + bucket_name = CalmVariable.Simple( + "", + label="Bucket", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Bucket", + ) + object_store_name = CalmVariable.Simple( + "", + label="Object Store Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Object Store Name", + ) + + CalmTask.Exec.escript.py3( + name="Modify Access", + filename=os.path.join( + "scripts", + "ResourceType_Bucket_Action_ModifyAccess_Task_ModifyAccess.py", + ), + ) + + +class NutanixPaaS(CloudProvider): + """Provider to manage complete Nutanix infrastructure including compute, storage, network & other platform services. + + NOTE: This example provider is included for informational purposes only. Admins will need to clone and customize this provider for their own purposes when using it with any NCM entities. This example provider will not be maintained and may be deprecated in the future.""" + + infra_type = "cloud" + + auth_schema_variables = [ + CalmVariable.Simple( + "", + label="Username", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + name="username", + ), + CalmVariable.Simple.Secret( + CloudProvider_NutanixPaaS_auth_schema_password, + label="Password", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + name="password", + ), + ] + + variables = [] + + endpoint_schema = ProviderEndpointSchema(type="NUTANIX_PC", variables=[]) + + resource_types = [ + VM, + Subnet, + VPC, + SecurityPolicies, + VirtualNetworkPolicies, + FileServer, + ObjectStore, + Bucket, + ] + + @action + def Verify(type="provider"): + + CalmTask.Exec.escript.py3( + name="Task 1", + filename=os.path.join( + "scripts", "CloudProvider_NutanixPaaS_Action_Verify_Task_Task1.py" + ), + ) diff --git a/examples/NutanixPaaS_provider/scripts/CloudProvider_NutanixPaaS_Action_Verify_Task_Task1.py b/examples/NutanixPaaS_provider/scripts/CloudProvider_NutanixPaaS_Action_Verify_Task_Task1.py new file mode 100644 index 000000000..84964f2a1 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/CloudProvider_NutanixPaaS_Action_Verify_Task_Task1.py @@ -0,0 +1,40 @@ +""" +Note: The provided methods in this script are generic and meant to serve as a starting point for credential verification. They can be customized to fit specific use cases and requirements +""" +""" +Script Verify Nutanix credentials by sending a POST request to the Nutanix API. It takes following parameters as macros defined in test-account/ provider. +Parameters: +\`pc_server\`: The Nutanix pc server. +\`pc_port\`: The Nutanix pc port. +\`username\`: The username for Nutanix authentication. +\`password\`: The password for Nutanix authentication. +Process: +Sends a POST request with the credentials to the Nutanix API. Checks the response status code to determine the validity of the credentials. +""" +import requests +import base64 + +url = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/clusters/list" +credentials = "@@{username}@@:@@{password}@@" +encoded_credentials = base64.b64encode(credentials.encode("utf-8")).decode("utf-8") +payload = json.dumps({}) +headers = { + "Content-Type": "application/json", + "Authorization": f"Basic {encoded_credentials}", +} +try: + response = requests.post(url, headers=headers, data=payload, verify=False) + if response.status_code == 200: + print("Nutanix credentials are valid.") + exit(0) + elif response.status_code == 401: + print("Unauthorized: Invalid Nutanix credentials.") + elif response.status_code == 403: + print("Forbidden: Insufficient privileges for Nutanix.") + else: + print( + f"Nutanix credentials are invalid. Response code: {response.status_code}, Response: {response.text}" + ) +except Exception as e: + print("Script execution failed with error: {}".format(e)) +exit(1) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_Create_Task_CreateBucket.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_Create_Task_CreateBucket.py new file mode 100644 index 000000000..67645dee6 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_Create_Task_CreateBucket.py @@ -0,0 +1,95 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = { + "api_version": "3.0", + "metadata": {"kind": "bucket"}, + "spec": {"description": "", "name": "", "resources": {"features": []}}, +} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def add_versioning(): + payload["spec"]["resources"]["features"].append("VERSIONING") + if "@@{non_current_version_expiration_days}@@" or "@@{expiration_days}@@": + payload["spec"]["resources"]["lifecycle_configuration"] = { + "Rule": [ + { + "Filter": {}, + "ID": "ntnx-frontend-emptyprefix-rule", + "Status": "Enabled", + } + ] + } + if "@@{non_current_version_expiration_days}@@": + payload["spec"]["resources"]["lifecycle_configuration"]["Rule"][0][ + "NoncurrentVersionExpiration" + ] = {"NoncurrentDays": int("@@{non_current_version_expiration_days}@@")} + if "@@{expiration_days}@@": + payload["spec"]["resources"]["lifecycle_configuration"]["Rule"][0][ + "Expiration" + ] = {"Days": int("@@{expiration_days}@@")} + + +def get_creation_status(url): + response = session.get(url) + print(f"get bucket response = {response.json()}") + response.raise_for_status() + return response.json()["status"]["state"] + + +def create(bucket_name): + payload["spec"]["name"] = bucket_name + object_store_uuid = get_resource_ext_id( + "https://{}:{}/api/objects/{}/operations/object-stores".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@" + ), + "frost", + ) + if "@@{enable_versioning}@@" == "Yes": + add_versioning() + print(f"create payload: {payload}") + response = session.post( + f"https://{PC_IP}:{PC_PORT}/oss/api/nutanix/v3/objectstores/{object_store_uuid}/buckets", + json=payload, + ) + print(f"create response: {response.json()}") + response.raise_for_status() + + status_path = f"https://{PC_IP}:{PC_PORT}/oss/api/nutanix/v3/objectstores/{object_store_uuid}/buckets/{bucket_name}" + print(f"task_status = {get_creation_status(status_path)}") + if "@@{wait}@@" == "Yes": + while True: + bucket_create_status = get_creation_status(status_path) + if bucket_create_status == "COMPLETE": + break + sleep(30) + + +create("@@{bucket_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_Delete_Task_DeleteBucket.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_Delete_Task_DeleteBucket.py new file mode 100644 index 000000000..dae1ee74f --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_Delete_Task_DeleteBucket.py @@ -0,0 +1,58 @@ +raise Exception( + "Not reccommented to use this script with @@{objects_api_version}@@ API version" +) + +# import requests + +# PC_USERNAME = "@@{account.username}@@" +# PC_PASSWORD = "@@{account.password}@@" +# PC_IP = "@@{account.pc_server}@@" +# PC_PORT = "@@{account.pc_port}@@" +# session = requests.Session() +# session.auth = (PC_USERNAME, PC_PASSWORD) +# session.verify = False + + +# def get_resource_ext_id(url, name, id_key="extId"): +# response = session.get( +# url, +# headers={ +# "accept": "application/json", +# }, +# params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, +# ) +# print(f"get {name} response: {response.json()}") +# response.raise_for_status() +# data = response.json().get("data") +# if data: +# if isinstance(data, list): +# if id_key in data[0] and data[0]["name"] == name: +# return data[0][id_key] +# else: +# if id_key in data: +# return data[id_key] +# raise Exception(f"failed to get extId for {name}") + + +# def delete(bucket_name): +# object_store_uuid = get_resource_ext_id( +# "https://{}:{}/api/objects/{}/operations/object-stores".format( +# PC_IP, PC_PORT, "@@{objects_api_version}@@" +# ), +# "@@{object_store_name}@@", +# ) +# for x in range(3): +# try: +# response = session.delete( +# f"https://{PC_IP}:{PC_PORT}/oss/api/nutanix/v3/objectstores/{object_store_uuid}/buckets/{bucket_name}", +# timeout=120, +# ) +# print(f"delete response: {response.text}") +# response.raise_for_status() +# return +# except Exception as e: +# continue +# raise Exception("unable to delete the bucket") + + +# delete("@@{bucket_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_List_Task_ListBuckets.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_List_Task_ListBuckets.py new file mode 100644 index 000000000..631c79bf2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_List_Task_ListBuckets.py @@ -0,0 +1,105 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = { + "entity_type": "bucket", + "group_member_sort_attribute": "name", + "group_member_sort_order": "ASCENDING", + "group_member_offset": 0, + "filter_criteria": 'federation_name==""', + "group_member_attributes": [ + { + "attribute": "name", + }, + { + "attribute": "storage_usage_bytes", + }, + { + "attribute": "object_count", + }, + { + "attribute": "versioning", + }, + { + "attribute": "worm", + }, + { + "attribute": "outbound_replication_status", + }, + { + "attribute": "nfs", + }, + { + "attribute": "bucket_notification_state", + }, + { + "attribute": "website", + }, + { + "attribute": "owner_name", + }, + { + "attribute": "retention_start", + }, + { + "attribute": "retention_duration_days", + }, + { + "attribute": "inbound_replication_status", + }, + { + "attribute": "suspend_versioning", + }, + { + "attribute": "cors", + }, + ], +} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def list_buckets(object_store_name): + object_store_uuid = get_resource_ext_id( + "https://{}:{}/api/objects/{}/operations/object-stores".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@" + ), + object_store_name, + ) + response = session.post( + f"https://{PC_IP}:{PC_PORT}/oss/api/nutanix/v3/objectstore_proxy/{object_store_uuid}/groups", + json=payload, + ) + response.raise_for_status() + print( + f"buckets = {json.dumps(response.json()['group_results'][0]['entity_results'])}" + ) + print(response.json()) + + +list_buckets("@@{object_store_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_ModifyAccess_Task_ModifyAccess.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_ModifyAccess_Task_ModifyAccess.py new file mode 100644 index 000000000..363112d4c --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Bucket_Action_ModifyAccess_Task_ModifyAccess.py @@ -0,0 +1,54 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def modify_access(bucket_name): + object_store_uuid = get_resource_ext_id( + "https://{}:{}/api/objects/{}/operations/object-stores".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@" + ), + "@@{object_store_name}@@", + ) + permissions = [x for x in "@@{permissions}@@".split(",") if x != "NONE"] + payload = { + "name": bucket_name, + "bucket_permissions": [ + {"username": "@@{username}@@", "permissions": permissions} + ], + } + response = session.post( + f"https://localhost:9440/oss/api/nutanix/v3/objectstores/{object_store_uuid}/buckets/{bucket_name}/share" + ) + print(f"got response : {response.json()}") + response.raise_for_status() + + +modify_access("@@{bucket_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_CreateSMBmount_Task_CreateSMBmount.py b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_CreateSMBmount_Task_CreateSMBmount.py new file mode 100644 index 000000000..286184945 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_CreateSMBmount_Task_CreateSMBmount.py @@ -0,0 +1,165 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {"protocol": "SMB", "smbProperties": {}} + + +def get_file_server_extId(file_server_name): + params = { + "$page": 0, + "$limit": 1, + "$select": "extId, name, sizeInGib", + "$orderby": "name", + "filter": f"name eq '{file_server_name}'", + } + response = session.get( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + params=params, + ) + print(f"list file server response: {response.json()}") + response.raise_for_status() + file_server_data = response.json()["data"] + if ( + len(file_server_data) == 1 + and file_server_data[0]["name"] == "@@{file_server_name}@@" + ): + return file_server_data[0]["extId"] + else: + raise Exception("File server not found") + + +def add_description(): + payload["description"] = "@@{description}@@" + + +def add_type(): + payload["type"] = "@@{type}@@" + + +def add_access_based_enumeration_enabled(): + payload["smbProperties"]["isAccessBasedEnumerationEnabled"] = ( + True if "@@{access_based_enumeration_enabled}@@" == "Yes" else False + ) + + +def add_compression_enabled(): + payload["isCompressionEnabled"] = ( + True if "@@{compression_enabled}@@" == "Yes" else False + ) + + +def add_encryption_enabled(): + payload["smbProperties"]["isSmb3EncryptionEnabled"] = ( + True if "@@{encryption_enabled}@@" == "Yes" else False + ) + + +def add_ca_enaled(): + payload["smbProperties"]["isCaEnabled"] = ( + True if "@@{ca_enabled}@@" == "Yes" else False + ) + + +def add_share_acl(): + if "@@{share_acl}@@".strip(): + payload["smbProperties"]["shareACL"] = [] + for value in "@@{share_acl}@@".split(","): + if value.strip(): + split_acl_data = value.split(":") + payload["smbProperties"]["shareACL"].append( + { + "userOrGroupName": split_acl_data[0], + "permissionType": split_acl_data[1], + "smbAccessType": split_acl_data[2], + } + ) + + +def add_compression_enabled(): + payload["enableCompression"] = ( + True if "@@{compression_enabled}@@" == "Yes" else False + ) + + +def add_max_size_in_gb(): + payload["maxSizeGiBytes"] = int("@@{max_size_in_gb}@@") + + +def add_name(): + payload["name"] = "@@{file_server_name}@@" + + +def add_path(): + if "@@{path}@@".strip(): + payload["path"] = "@@{path}@@" + + +def create_payload(): + add_path() + add_name() + add_max_size_in_gb() + add_compression_enabled() + add_access_based_enumeration_enabled() + add_encryption_enabled() + add_ca_enaled() + add_share_acl() + add_description() + add_type() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create(): + create_payload() + print(f"payload: {payload}") + create_file_server_response = session.post( + "https://{}:{}/api/files/{}/config/file-servers/{}/mount-targets".format( + PC_IP, + PC_PORT, + "@@{files_api_version}@@", + get_file_server_extId("@@{file_server_name}@@"), + ), + json=payload, + ) + print(f"create response: {create_file_server_response.json()}") + create_file_server_response.raise_for_status() + task_uuid = create_file_server_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + + +create() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Create_Task_Createfileserver.py b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Create_Task_Createfileserver.py new file mode 100644 index 000000000..bca5b296b --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Create_Task_Createfileserver.py @@ -0,0 +1,237 @@ +import requests +import re + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {"platform": {"$objectType": "files.v4.config.OnPrem"}} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_subnet_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/subnets".format( + PC_IP, PC_PORT, "@@{networking_api_version}@@" + ), + name, + ) + + +def get_storage_container_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/clustermgmt/{}/config/storage-containers".format( + PC_IP, PC_PORT, "@@{cluster_mgt_api_version}@@" + ), + name, + "containerExtId", + ) + + +def get_cluster_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/clustermgmt/{}/config/clusters".format( + PC_IP, PC_PORT, "@@{cluster_mgt_api_version}@@" + ), + name, + ) + + +def add_storage_container(): + payload["storageContainerId"] = get_storage_container_ext_id( + "@@{storage_container}@@" + ) + + +def add_file_server_storage(): + payload["sizeGiBytes"] = int("@@{file_server_size_in_gb}@@") + + +def add_server_version(): + payload["version"] = "@@{file_server_version}@@" + + +def add_rebalance_enabled(): + payload["isRebalanceEnabled"] = ( + True if "@@{rebalance_enabled}@@" == "Yes" else False + ) + + +def add_compression_enabled(): + payload["isCompressionEnabled"] = ( + True if "@@{compression_enabled}@@" == "Yes" else False + ) + + +def add_file_blocking_extensions(): + if "@@{file_blocking_extensions}@@": + payload["fileBlockingExtensions"] = "@@{file_blocking_extensions}@@" + + +def add_dns_domain(): + payload["dnsDomainName"] = "@@{dns_domain_name}@@" + + +def add_dns_servers(): + payload["dnsServers"] = [] + for dns_server_ip in "@@{dns_servers}@@".split(","): + payload["dnsServers"].append({"value": dns_server_ip}) + + +def add_ntp_servers(): + payload["ntpServers"] = [] + for ntp_server_ip in "@@{ntp_servers}@@".split(","): + if re.search(r"\w+", ntp_server_ip): + payload["ntpServers"].append({"fqdn": {"value": ntp_server_ip}}) + else: + payload["ntpServers"].append({"ipv4": {"value": ntp_server_ip}}) + + +def add_deployment_type(): + payload["deploymentType"] = "@@{type_of_deployment}@@" + + +def add_platform_type(): + payload["platformType"] = "ON_PREM" + + +def add_internal_network_reference(): + payload["platform"]["internalNetworks"] = [ + { + "isManaged": ( + True if "@@{internal_network_is_managed}@@" == "Yes" else False + ), + "networkExtId": get_subnet_ext_id("@@{internal_network_vlan}@@"), + "subnetMask": {"ipv4": {"value": "@@{internal_network_subnet_mask}@@"}}, + "defaultGateway": { + "ipv4": {"value": "@@{internal_network_default_gateway}@@"} + }, + "ipAddresses": [ + {"ipv4": {"value": ip}} + for ip in "@@{internal_network_ip_address}@@".split(",") + ], + } + ] + + +def add_external_network_reference(): + payload["platform"]["externalNetworks"] = [ + { + "isManaged": ( + True if "@@{external_network_is_managed}@@" == "Yes" else False + ), + "networkExtId": get_subnet_ext_id("@@{external_network_vlan}@@"), + "subnetMask": {"ipv4": {"value": "@@{external_network_subnet_mask}@@"}}, + "defaultGateway": { + "ipv4": {"value": "@@{external_network_default_gateway}@@"} + }, + "ipAddresses": [ + {"ipv4": {"value": ip}} + for ip in "@@{external_network_ip_address}@@".split(",") + ], + } + ] + + +def add_vm(): + payload["nvmsCount"] = int("@@{vm_count}@@") + + +def add_cluster(): + payload["platform"]["clusterExtId"] = get_cluster_ext_id("@@{cluster_name}@@") + + +def add_vm_vcpu(): + payload["platform"]["vcpus"] = int("@@{vm_vcpu}@@") + + +def add_vm_memory(): + payload["platform"]["memoryGib"] = int("@@{vm_memory_gib}@@") + + +def create_payload(): + add_storage_container() + add_file_server_storage() + add_server_version() + add_rebalance_enabled() + add_compression_enabled() + add_file_blocking_extensions() + add_dns_domain() + add_dns_servers() + add_ntp_servers() + add_deployment_type() + add_platform_type() + add_internal_network_reference() + add_external_network_reference() + add_vm() + add_cluster() + add_vm_vcpu() + add_vm_memory() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create(name): + payload["name"] = name + create_payload() + print(f"payload: {payload}") + create_file_server_response = session.post( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + json=payload, + ) + print(f"create response: {create_file_server_response.json()}") + create_file_server_response.raise_for_status() + task_uuid = create_file_server_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + + +create("@@{file_server_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Get_Task_Getfileserver.py b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Get_Task_Getfileserver.py new file mode 100644 index 000000000..d6e496043 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Get_Task_Getfileserver.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def get_file_server(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + print(f"response: {response.json()}") + response.raise_for_status() + file_server_data = response.json()["data"] + if ( + len(file_server_data) == 1 + and file_server_data[0]["name"] == "@@{file_server_name}@@" + ): + print("file_server = " + json.dumps(json.dumps(file_server_data[0]))) + else: + raise Exception("File server not found") + + +params = { + "$page": 0, + "$limit": 1, + "$select": "extId, name, sizeInGib", + "$orderby": "name", + "filter": "name eq '@@{file_server_name}@@'", +} + +get_file_server(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Getmounttarget_Task_Getmounttarget.py b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Getmounttarget_Task_Getmounttarget.py new file mode 100644 index 000000000..e4bd90997 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Getmounttarget_Task_Getmounttarget.py @@ -0,0 +1,84 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False + + +def get_file_server_extId(file_server_name): + params = { + "$page": 0, + "$limit": 1, + "$select": "extId, name", + "$orderby": "name", + "filter": f"name eq '{file_server_name}'", + } + response = session.get( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + params=params, + ) + print(f"list file server response: {response.json()}") + response.raise_for_status() + file_server_data = response.json()["data"] + if ( + len(file_server_data) == 1 + and file_server_data[0]["name"] == "@@{file_server_name}@@" + ): + return file_server_data[0]["extId"] + else: + raise Exception("File server not found") + + +def get_mount_target_extId(name): + params = { + "$page": 0, + "$limit": 1, + "$select": "extId, name", + "$orderby": "name", + "filter": f"name eq '{name}'", + } + response = session.get( + "https://{}:{}/api/files/{}/config/file-servers/{}/mount-targets".format( + PC_IP, + PC_PORT, + "@@{files_api_version}@@", + get_file_server_extId("@@{file_server_name}@@"), + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + ) + print(f"list mount target response: {response.json()}") + response.raise_for_status() + mount_target_response_data = response.json()["data"] + if len(mount_target_response_data) == 1: + return mount_target_response_data[0]["extId"] + else: + raise Exception("Mount target not found") + + +def get_mount_target(name): + get_mount_target_response = session.get( + "https://{}:{}/api/files/{}/config/file-servers/{}/mount-targets/{}".format( + PC_IP, + PC_PORT, + "@@{files_api_version}@@", + get_file_server_extId("@@{file_server_name}@@"), + get_mount_target_extId(name), + ) + ) + print(f"get mount target response: {get_mount_target_response.json()}") + get_mount_target_response.raise_for_status() + print( + "mount_target = " + + json.dumps(json.dumps(get_mount_target_response.json()["data"])) + ) + + +get_mount_target("@@{mount_target_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_List_Task_Listfileservers.py b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_List_Task_Listfileservers.py new file mode 100644 index 000000000..587bc93ca --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_List_Task_Listfileservers.py @@ -0,0 +1,36 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def list_file_servers(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + print(f"response: {response.json()}") + response.raise_for_status() + print("file_servers = " + json.dumps(json.dumps(response.json()["data"]))) + + +params = { + "$page": @@{page}@@, + "$limit": @@{limit}@@, + "$select": "@@{select}@@", + "$orderby": "@@{order_by}@@", +} +if "@@{filter}@@": + params["filter"] = "@@{filter}@@" + +list_file_servers(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Listmounttargets_Task_Listmounttargets.py b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Listmounttargets_Task_Listmounttargets.py new file mode 100644 index 000000000..3840d2efc --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Listmounttargets_Task_Listmounttargets.py @@ -0,0 +1,64 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False + + +def get_file_server_extId(file_server_name): + params = { + "$page": 0, + "$limit": 1, + "$select": "extId, name, sizeInGib", + "$orderby": "name", + "filter": f"name eq '{file_server_name}'", + } + response = session.get( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + params=params, + ) + print(f"lsit file server response: {response.json()}") + response.raise_for_status() + file_server_data = response.json()["data"] + if ( + len(file_server_data) == 1 + and file_server_data[0]["name"] == "@@{file_server_name}@@" + ): + return file_server_data[0]["extId"] + else: + raise Exception("File server not found") + + +def list_mount_targets(params): + response = session.get( + "https://{}:{}/api/files/{}/config/file-servers/{}/mount-targets".format( + PC_IP, + PC_PORT, + "@@{files_api_version}@@", + get_file_server_extId("@@{file_server_name}@@"), + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + ) + print(f"response: {response.json()}") + response.raise_for_status() + print("mount_targets = " + json.dumps(json.dumps(response.json()["data"]))) + + +params = { + "$page": @@{page}@@, + "$limit": @@{limit}@@, + "$select": "@@{select}@@", + "$orderby": "@@{order_by}@@", +} +if "@@{filter}@@": + params["filter"] = "@@{filter}@@" + +list_mount_targets(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_UpdateSMBmount_Task_UpdateSMBmount.py b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_UpdateSMBmount_Task_UpdateSMBmount.py new file mode 100644 index 000000000..84b4d3ce9 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_UpdateSMBmount_Task_UpdateSMBmount.py @@ -0,0 +1,216 @@ +import requests +import uuid + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {"protocol": "SMB", "smbProperties": {}} + + +def get_file_server_extId(file_server_name): + params = { + "$page": 0, + "$limit": 1, + "$select": "extId, name, sizeInGib", + "$orderby": "name", + "filter": f"name eq '{file_server_name}'", + } + response = session.get( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + params=params, + ) + print(f"list file server response: {response.json()}") + response.raise_for_status() + file_server_data = response.json()["data"] + if ( + len(file_server_data) == 1 + and file_server_data[0]["name"] == "@@{file_server_name}@@" + ): + return file_server_data[0]["extId"] + else: + raise Exception("File server not found") + + +def update_description(): + if "@@{description}@@".strip(): + payload["description"] = "@@{description}@@" + + +def update_type(): + if "@@{type}@@" != "-": + payload["type"] = "@@{type}@@" + + +def update_access_based_enumeration_enabled(): + if "@@{access_based_enumeration_enabled}@@" != "-": + payload["smbProperties"]["isAccessBasedEnumerationEnabled"] = ( + True if "@@{access_based_enumeration_enabled}@@" == "Yes" else False + ) + + +def update_compression_enabled(): + if "@@{compression_enabled}@@" != "-": + payload["isCompressionEnabled"] = ( + True if "@@{compression_enabled}@@" == "Yes" else False + ) + + +def update_encryption_enabled(): + if "@@{encryption_enabled}@@" != "-": + payload["smbProperties"]["isSmb3EncryptionEnabled"] = ( + True if "@@{encryption_enabled}@@" == "Yes" else False + ) + + +def update_ca_enaled(): + if "@@{ca_enabled}@@" != "-": + payload["smbProperties"]["isCaEnabled"] = ( + True if "@@{ca_enabled}@@" == "Yes" else False + ) + + +def update_share_acl(): + if "@@{share_acl}@@".strip(): + payload["smbProperties"]["shareACL"] = [] + for value in "@@{share_acl}@@".split(","): + if value.strip(): + split_acl_data = value.split(":") + payload["smbProperties"]["shareACL"].append( + { + "userOrGroupName": split_acl_data[0], + "permissionType": split_acl_data[1], + "smbAccessType": split_acl_data[2], + } + ) + + +def update_compression_enabled(): + if "@@{compression_enabled}@@" != "-": + payload["enableCompression"] = ( + True if "@@{compression_enabled}@@" == "Yes" else False + ) + + +def update_max_size_in_gb(): + if "@@{max_size_in_gb}@@".strip() and int("@@{max_size_in_gb}@@") != 0: + payload["maxSizeGiBytes"] = int("@@{max_size_in_gb}@@") + + +def update_path(): + if "@@{path}@@".strip(): + payload["path"] = "@@{path}@@" + + +def create_payload(): + update_path() + update_max_size_in_gb() + update_compression_enabled() + update_access_based_enumeration_enabled() + update_encryption_enabled() + update_ca_enaled() + update_share_acl() + update_description() + update_type() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def get_mount_target_extId(name): + params = { + "$page": 0, + "$limit": 1, + "$select": "extId, name", + "$orderby": "name", + "filter": f"name eq '{name}'", + } + response = session.get( + "https://{}:{}/api/files/{}/config/file-servers/{}/mount-targets".format( + PC_IP, + PC_PORT, + "@@{files_api_version}@@", + get_file_server_extId("@@{file_server_name}@@"), + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + ) + print(f"list mount target response: {response.json()}") + response.raise_for_status() + mount_target_response_data = response.json()["data"] + if len(mount_target_response_data) == 1: + return mount_target_response_data[0]["extId"] + else: + raise Exception("Mount target not found") + + +def update(name): + payload["name"] = name + create_payload() + print(f"payload: {payload}") + mount_target_extId = get_mount_target_extId(name) + filer_server_extId = get_file_server_extId("@@{file_server_name}@@") + mount_target = session.get( + "https://{}:{}/api/files/{}/config/file-servers/{}/mount-targets/{}".format( + PC_IP, + PC_PORT, + "@@{files_api_version}@@", + filer_server_extId, + mount_target_extId, + ) + ) + print(f"get mount target response: {mount_target.json()}") + mount_target.raise_for_status() + update_mount_target_response = session.put( + "https://{}:{}/api/files/{}/config/file-servers/{}/mount-targets/{}".format( + PC_IP, + PC_PORT, + "@@{files_api_version}@@", + filer_server_extId, + mount_target_extId, + ), + headers={ + "Content-Type": "application/json", + "If-Match": mount_target.headers["ETag"], + "NTNX-Request-Id": str(uuid.uuid4()), + }, + json=payload, + ) + print(f"update response: {update_mount_target_response.json()}") + update_mount_target_response.raise_for_status() + task_uuid = update_mount_target_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + + +update("@@{mount_target_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Update_Task_Updatefileserver.py b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Update_Task_Updatefileserver.py new file mode 100644 index 000000000..3c81c4abd --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_FileServer_Action_Update_Task_Updatefileserver.py @@ -0,0 +1,210 @@ +import requests +import re +import uuid + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = { + "platform": { + "$objectType": "files.v4.config.OnPrem", + } +} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_subnet_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/subnets".format( + PC_IP, PC_PORT, "@@{networking_api_version}@@" + ), + name, + ) + + +def add_file_server_storage(): + if "@@{file_server_size_in_gb}@@".strip(): + payload["sizeGiBytes"] = int("@@{file_server_size_in_gb}@@") + + +def add_rebalance_enabled(): + if "@@{rebalance_enabled}@@" != "-": + payload["isRebalanceEnabled"] = ( + True if "@@{rebalance_enabled}@@" == "Yes" else False + ) + + +def add_compression_enabled(): + if "@@{compression_enabled}@@" != "-": + payload["isCompressionEnabled"] = ( + True if "@@{compression_enabled}@@" == "Yes" else False + ) + + +def add_file_blocking_extensions(): + if "@@{file_blocking_extensions}@@".strip(): + payload["fileBlockingExtensions"] = "@@{file_blocking_extensions}@@" + + +def add_dns_domain(): + if "@@{dns_domain_name}@@".strip(): + payload["dnsDomainName"] = "@@{dns_domain_name}@@" + + +def add_dns_servers(): + if "@@{dns_servers}@@".strip(): + payload["dnsServers"] = [] + for dns_server_ip in "@@{dns_servers}@@".split(","): + payload["dnsServers"].append({"value": dns_server_ip}) + + +def add_ntp_servers(): + if "@@{ntp_servers}@@".strip(): + payload["ntpServers"] = [] + for ntp_server_ip in "@@{ntp_servers}@@".split(","): + if re.search(r"\w+", ntp_server_ip): + payload["ntpServers"].append({"fqdn": {"value": ntp_server_ip}}) + else: + payload["ntpServers"].append({"ipv4": {"value": ntp_server_ip}}) + + +def add_vm(): + if "@@{vm_count}@@".strip() and int("@@{vm_count}@@") != 0: + payload["nvmsCount"] = int("@@{vm_count}@@") + + +def add_vm_vcpu(): + if "@@{vm_vcpu}@@".strip() and int("@@{vm_vcpu}@@") != 0: + payload["platform"]["vcpus"] = int("@@{vm_vcpu}@@") + + +def add_vm_memory(): + if "@@{vm_memory_gib}@@".strip() and int("@@{vm_memory_gib}@@") != 0: + payload["platform"]["memoryGib"] = int("@@{vm_memory_gib}@@") + + +def create_payload(): + add_file_server_storage() + add_rebalance_enabled() + add_compression_enabled() + add_file_blocking_extensions() + add_dns_domain() + add_dns_servers() + add_ntp_servers() + add_vm() + add_vm_vcpu() + add_vm_memory() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def get_file_server_response(params): + response = session.get( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + params=params, + ) + print(f"list file server response: {response.json()}") + response.raise_for_status() + file_server_data = response.json()["data"] + if ( + len(file_server_data) == 1 + and file_server_data[0]["name"] == "@@{file_server_name}@@" + ): + file_server_get_response = session.get( + "https://{}:{}/api/files/{}/config/file-servers/{}".format( + PC_IP, + PC_PORT, + "@@{files_api_version}@@", + file_server_data[0]["extId"], + ) + ) + print(f"get file server response: {response.json()}") + file_server_get_response.raise_for_status() + return file_server_get_response + else: + raise Exception("File server not found") + + +def update(name): + payload["name"] = name + create_payload() + print(f"payload: {payload}") + file_server_get_response = get_file_server_response( + params={ + "$page": 0, + "$limit": 1, + "$select": "extId, name, sizeInGib", + "$orderby": "name", + "filter": f"name eq '{name}'", + } + ) + update_file_server_response = session.put( + "https://{}:{}/api/files/{}/config/file-servers".format( + PC_IP, PC_PORT, "@@{files_api_version}@@" + ), + headers={ + "Content-Type": "application/json", + "If-Match": file_server_get_response.headers["ETag"], + "NTNX-Request-Id": str(uuid.uuid4()), + }, + json=payload, + ) + print(f"update response: {update_file_server_response.json()}") + update_file_server_response.raise_for_status() + task_uuid = update_file_server_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + if "@@{wait}@@" == "Yes": + wait(task_uuid) + + +update("@@{file_server_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Create_Task_CreateObjectStorage.py b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Create_Task_CreateObjectStorage.py new file mode 100644 index 000000000..db047d054 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Create_Task_CreateObjectStorage.py @@ -0,0 +1,179 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def add_total_capacity(): + if "@@{total_capacity_gib}@@".strip(): + payload["totalCapacityGiB"] = int("@@{total_capacity_gib}@@") + + +def add_public_network_ips(): + if "@@{public_network_ips}@@": + payload["publicNetworkIps"] = [ + {"ipv4": {"value": ip, "prefixLength": 32}} + for ip in "@@{public_network_ips}@@".split(",") + ] + + +def add_public_network_reference(): + if "@@{public_network}@@": + ## get the public network reference extId + payload["publicNetworkReference"] = get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/subnets".format( + PC_IP, PC_PORT, "@@{networking_api_version}@@" + ), + "@@{public_network}@@", + ) + + +def add_storage_network_dns_ip(): + if "@@{storage_network_dns_ip}@@": + payload["storageNetworkDnsIp"] = { + "ipv4": {"value": "@@{storage_network_dns_ip}@@", "prefixLength": 32} + } + + +def add_storage_network_vip(): + if "@@{storage_network_vip}@@": + payload["storageNetworkVip"] = { + "ipv4": {"value": "@@{storage_network_vip}@@", "prefixLength": 32} + } + + +def add_storage_network_reference(): + if "@@{storage_network}@@": + ## get the storage network reference extId + payload["storageNetworkReference"] = get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/subnets".format( + PC_IP, PC_PORT, "@@{networking_api_version}@@" + ), + "@@{storage_network}@@", + ) + + +def add_cluster_reference(): + if "@@{cluster}@@": + ## get the cluster reference extId + payload["clusterReference"] = get_resource_ext_id( + "https://{}:{}/api/clustermgmt/{}/config/clusters".format( + PC_IP, PC_PORT, "@@{cluster_mgt_api_version}@@" + ), + "@@{cluster}@@", + ) + + +def add_worker_nodes(): + if "@@{number_of_worker_nodes}@@".strip(): + payload["numWorkerNodes"] = int("@@{number_of_worker_nodes}@@") + + +def add_region(): + if "@@{region}@@": + payload["region"] = "@@{region}@@" + + +def add_domain(): + if "@@{domain}@@": + payload["domain"] = "@@{domain}@@" + + +def add_deployment_version(): + if "@@{deployment_version}@@": + payload["deployment_version"] = "@@{deployment_version}@@" + + +def add_description(): + if "@@{description}@@": + payload["description"] = "@@{description}@@" + + +def create_payload(): + add_total_capacity() + add_public_network_ips() + add_public_network_reference() + add_storage_network_dns_ip() + add_storage_network_vip() + sleep(2) + add_storage_network_reference() + add_cluster_reference() + add_worker_nodes() + add_region() + add_domain() + add_deployment_version() + add_description() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create(name): + payload["name"] = name + create_payload() + print(f"payload: {payload}") + create_object_storage_response = session.post( + "https://{}:{}/api/objects/{}/operations/object-stores".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@" + ), + json=payload, + ) + print(f"create response: {create_object_storage_response.json()}") + create_object_storage_response.raise_for_status() + task_uuid = create_object_storage_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + if "@@{wait}@@" == "Yes": + wait(task_uuid) + + +create("@@{object_store_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Delete_Task_Deleteobjectstore.py b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Delete_Task_Deleteobjectstore.py new file mode 100644 index 000000000..73f3981e6 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Delete_Task_Deleteobjectstore.py @@ -0,0 +1,90 @@ +raise Exception( + "Not reccommented to use this script with @@{objects_api_version}@@ API version" +) + +# import requests + +# PC_USERNAME = "@@{account.username}@@" +# PC_PASSWORD = "@@{account.password}@@" +# PC_IP = "@@{account.pc_server}@@" +# PC_PORT = "@@{account.pc_port}@@" +# session = requests.Session() +# session.auth = (PC_USERNAME, PC_PASSWORD) +# session.verify = False +# payload = {} + + +# def get_resource_ext_id(url, name, id_key="extId"): +# response = session.get( +# url, +# headers={ +# "accept": "application/json", +# }, +# params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, +# ) +# print(f"get {name} response: {response.json()}") +# response.raise_for_status() +# data = response.json().get("data") +# if data: +# if isinstance(data, list): +# if id_key in data[0] and data[0]["name"] == name: +# return data[0][id_key] +# else: +# if id_key in data: +# return data[id_key] +# raise Exception(f"failed to get extId for {name}") + + +# def wait(task_uuid, timeout=1800): +# max_count = timeout / 10 +# count = 0 +# task_status_response = None +# while count <= 18: +# task_status_response = session.get( +# "https://{}:{}/api/prism/{}/config/tasks/{}".format( +# PC_IP, PC_PORT, "@@{prism_api_version}@@",task_uuid +# ), +# ) +# if task_status_response.status_code != 200: +# raise Exception( +# f"failed to get task, got response {task_status_response.json()}" +# ) +# print(f"task status is {task_status_response.json()['data']['status']}") +# if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: +# count += 1 +# sleep(10) +# else: +# count = 0 +# break +# print(f"task_status = {task_status_response.json()['data']['status']}") +# if count != 0: +# raise Exception("timed out waiting for task to complete") + + +# def delete(name): +# object_store_ext_id = get_resource_ext_id( +# "https://{}:{}/api/objects/{}/operations/object-stores".format(PC_IP, PC_PORT, "@@{objects_api_version}@@"), name +# ) +# ## get the object store +# get_object_store_response = session.get( +# "https://{}:{}/api/objects/{}/operations/object-stores/{extId}".format( +# PC_IP, PC_PORT, "@@{objects_api_version}@@",object_store_ext_id +# ) +# ) +# print(f"get object store response : {get_object_store_response.json()}") +# get_object_store_response.raise_for_status() +# delete_object_store_response = session.delete( +# "https://{}:{}/api/objects/{}/operations/object-stores/{}".format( +# PC_IP, PC_PORT, "@@{objects_api_version}@@",object_store_ext_id +# ), +# headers={"If-Match": get_object_store_response.headers["Etag"]}, +# ) +# print(f"delete object store response : {delete_object_store_response.json()}") +# delete_object_store_response.raise_for_status() +# task_uuid = delete_object_store_response.json()["data"]["extId"] +# print(f"task_uuid = {task_uuid}") +# if "@@{wait}@@": +# wait(task_uuid) + + +# delete("@@{object_store_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Get_Task_Getanobjectstore.py b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Get_Task_Getanobjectstore.py new file mode 100644 index 000000000..9de8f9da7 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Get_Task_Getanobjectstore.py @@ -0,0 +1,36 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def get(object_store): + params = {"$filter": f"name eq '{object_store}'", "$page": 0, "$limit": 1} + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/objects/{}/operations/object-stores".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + print(f"response = {response.json()}") + response.raise_for_status() + data = response.json().get("data") + print(data[0]["name"], len(data), object_store) + if data: + if len(data) == 1 and data[0]["name"] == object_store: + print( + "object_store = " + json.dumps(json.dumps(response.json()["data"][0])) + ) + return + raise Exception(f"failed to get object store with name {object_store}") + + +get("@@{object_store_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_List_Task_ListObjectStore.py b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_List_Task_ListObjectStore.py new file mode 100644 index 000000000..cb7dcc1b5 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_List_Task_ListObjectStore.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def list_object_stores(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/objects/{}/operations/object-stores".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + print(f"response: {response.json()}") + response.raise_for_status() + print("object_stores = " + json.dumps(json.dumps(response.json()["data"]))) + + +params = { + "$page": @@{page}@@, + "$limit": @@{limit}@@, + "$select": "@@{select}@@", + "$orderby": "@@{orderby}@@", +} + +if "@@{filter}@@".strip(): + params["$filter"] = "@@{filter}@@" + +if "@@{expand}@@".strip(): + params["$expand"] = "@@{expand}@@" + + +list_object_stores(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Update_Task_UpdateObjectStorage.py b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Update_Task_UpdateObjectStorage.py new file mode 100644 index 000000000..fcbb200af --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_ObjectStore_Action_Update_Task_UpdateObjectStorage.py @@ -0,0 +1,195 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {"name": "@@{object_store_name}@@"} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def add_total_capacity(): + if "@@{total_capacity_gib}@@".strip(): + payload["totalCapacityGiB"] = int("@@{total_capacity_gib}@@") + + +def add_public_network_ips(): + if "@@{public_network_ips}@@": + payload["publicNetworkIps"] = { + "ipv4": {"value": "@@{public_network_ips}@@", "prefixLength": 32} + } + + +def add_public_network_reference(): + if "@@{public_network}@@": + ## get the public network reference extId + payload["publicNetworkReference"] = get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/subnets".format( + PC_IP, PC_PORT, "@@{networking_api_version}@@" + ), + "@@{public_network}@@", + ) + + +def add_storage_network_dns_ip(): + if "@@{storage_network_dns_ip}@@": + payload["storageNetworkDnsIp"] = { + "ipv4": {"value": "@@{storage_network_dns_ip}@@", "prefixLength": 32} + } + + +def add_storage_network_vip(): + if "@@{storage_network_vip}@@": + payload["storageNetworkVip"] = { + "ipv4": {"value": "@@{storage_network_vip}@@", "prefixLength": 32} + } + + +def add_storage_network_reference(): + if "@@{storage_network}@@": + ## get the storage network reference extId + payload["storageNetworkReference"] = get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/subnets".format( + PC_IP, PC_PORT, "@@{networking_api_version}@@" + ), + "@@{storage_network}@@", + ) + + +def add_cluster_reference(): + if "@@{cluster}@@": + ## get the cluster reference extId + payload["clusterReference"] = get_resource_ext_id( + "https://{}:{}/api/clustermgmt/{}/config/clusters".format( + PC_IP, PC_PORT, "@@{cluster_mgt_api_version}@@" + ), + "@@{cluster}@@", + ) + + +def add_worker_nodes(): + if "@@{number_of_worker_nodes}@@".strip(): + payload["numWorkerNodes"] = int("@@{number_of_worker_nodes}@@") + + +def add_region(): + if "@@{region}@@": + payload["region"] = "@@{region}@@" + + +def add_domain(): + if "@@{domain}@@": + payload["domain"] = "@@{domain}@@" + + +def add_deployment_version(): + if "@@{deployment_version}@@": + payload["deployment_version"] = "@@{deployment_version}@@" + + +def add_description(): + if "@@{description}@@": + payload["description"] = "@@{description}@@" + + +def create_payload(): + add_total_capacity() + add_public_network_ips() + add_public_network_reference() + add_storage_network_dns_ip() + add_storage_network_vip() + sleep(2) + add_storage_network_reference() + add_cluster_reference() + add_worker_nodes() + add_region() + add_domain() + add_deployment_version() + add_description() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def update(name): + object_store_ext_id = get_resource_ext_id( + "https://{}:{}/api/objects/{}/operations/object-stores".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@" + ), + name, + ) + create_payload() + print(f"payload: {payload}") + object_details_response = session.get( + "https://{}:{}/api/objects/{}/operations/object-stores/{}".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@", object_store_ext_id + ), + ) + print(f"object details response: {object_details_response.json()}") + object_details_response.raise_for_status() + update_object_storage_response = session.put( + "https://{}:{}/api/objects/{}/operations/object-stores/{}".format( + PC_IP, PC_PORT, "@@{objects_api_version}@@", object_store_ext_id + ), + json=payload, + headers={ + "Content-Type": "application/json", + "If-Match": object_details_response.headers["ETag"], + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"update response: {update_object_storage_response.json()}") + update_object_storage_response.raise_for_status() + task_uuid = update_object_storage_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + if "@@{wait}@@": + wait(task_uuid) + + +update("@@{object_store_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_Task_CreateApplicationPolicy.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_Task_CreateApplicationPolicy.py new file mode 100644 index 000000000..5a0f1cf03 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_Task_CreateApplicationPolicy.py @@ -0,0 +1,384 @@ +import requests +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + keyname = name.split(":")[0] + keyvalue = name.split(":")[1] + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={ + "$page": 0, + "$limit": 1, + "$filter": f"key eq '{keyname}' and value eq '{keyvalue}'", + }, + ) + # print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if ( + id_key in data[0] + and data[0]["key"] == keyname + and data[0]["value"] == keyvalue + ): + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_category_extID(category_name): + return get_resource_ext_id( + "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@" + ), + category_name, + ) + + +def get_categorygroup_extId(names): + group_names = names.split(",") + extId_list = [] + for category_name in group_names: + extId_list.append(get_category_extID(category_name)) + return extId_list + + +def get_servicesAndAddr_extId(url, c_name, id_key="extId"): + response = session.get( + url, + headers={"accept": "application/json"}, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{c_name}'"}, + ) + # print(f"response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == c_name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {c_name}") + + +def get_serviesGroup_extId(names): + # value - ServiceName:, ServiceName: - , ... + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + extId = get_servicesAndAddr_extId(url, names) + return extId + + +def get_addressGroup_extID(names): + group_names = names.split(",") + extId_list = [] + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + for group_name in group_names: + extId_list.append(get_servicesAndAddr_extId(url, group_name)) + return extId_list + + +# ------------------------------------------------------------------------------------------ + + +def add_tcpudpports(proto_value): + tcpports = proto_value.split(",") + tmp = [] + for ports in tcpports: + if "-" in ports: + startport = ports.split("-")[0] + endport = ports.split("-")[1] + else: + startport = ports + endport = ports + tmp.append({"startPort": int(startport), "endPort": int(endport)}) + return tmp + + +def add_icmpports(proto_value): + # format : ANY,ANY or ANY,port or port,ANY or port,port + icmp_data = proto_value.split(",") + icmpService_data = [] + tmp1 = {} + if proto_value == "ANY,ANY": + tmp1["isAllAllowed"] = True + elif icmp_data[0] != "ANY": + tmp1["type"] = int(icmp_data[0]) + elif icmp_data[1] != "ANY": + tmp1["code"] = int(icmp_data[1]) + icmpService_data.append(tmp1) + return icmpService_data + + +def check_protocol(proto): + # default will be ALL + if "ALL" in proto or proto == "": + proto_type = "ALL" + else: + proto_type = proto.split(":")[0] + proto_value = proto.split(":")[1] + if proto_type == "ALL": + key1 = "isAllProtocolAllowed" + value1 = True + elif proto_type == "TCP" or proto_type == "UDP": + key1 = "tcpServices" + value1 = add_tcpudpports(proto_value) + elif proto_type == "ICMP": + key1 = "icmpServices" + value1 = add_icmpports(proto_value) + elif proto_type == "SERVICE": + proto_value = proto.split(":", 1)[1] + s_tmp = [] + for p in proto_value.split(","): + service_name = p.split(":")[0] + s_tmp.append(get_serviesGroup_extId(service_name)) + key1 = "serviceGroupReferences" + value1 = s_tmp + return key1, value1 + + +def add_common(type, dictkey, dictvalue, proto_key, proto_val): + basic = { + "type": type, + "spec": { + "$objectType": "microseg.v4.config.ApplicationRuleSpec", + "securedGroupCategoryReferences": get_categorygroup_extId( + "@@{securedGroup}@@" + ), + dictkey: dictvalue, + }, + } + if proto_key != "": + basic["spec"][proto_key] = proto_val + return basic + + +def add_application_rule( + endpoint, CategoryRef, SubnetRef, AddressGroupRef, AllGroupType +): + # variables : + # repeate 16. securedGroup = securedGroupCategoryReferences + # 19. srcCategoryReferences -> src_catProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 21. srcSubnet -> src_subnetProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 23. srcAddressGroupReferences -> src_addrProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 25. srcAllGroupType = ALL or None -> src_allProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 27. serviceGroupReferences + # add_proto == src_ProtocolType|dest_ProtocolType ==> ALL; TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT + # And servicesGroupReference + add_proto = "" + + if endpoint == "src": + if "@@{src_serviceGroupReferences}@@" != "": + add_proto = "SERVICE:@@{src_serviceGroupReferences}@@" + category_add_proto = add_proto + ";" + "@@{src_CategoryProtocolType}@@" + subnet_add_proto = add_proto + ";" + "@@{src_SubnetProtocolType}@@" + address_add_proto = add_proto + ";" + "@@{src_AddressProtocolType}@@" + allgroup_add_proto = add_proto + ";" + "@@{src_AllGroupProtocolType}@@" + elif endpoint == "dest": + if "@@{dest_serviceGroupReferences}@@" != "": + add_proto = "SERVICE:@@{dest_serviceGroupReferences}@@" + category_add_proto = add_proto + ";" + "@@{dest_CategoryProtocolType}@@" + subnet_add_proto = add_proto + ";" + "@@{dest_SubnetProtocolType}@@" + address_add_proto = add_proto + ";" + "@@{dest_AddressProtocolType}@@" + allgroup_add_proto = add_proto + ";" + "@@{dest_AllGroupProtocolType}@@" + + # if no protocol defined then default willbe ALL + if category_add_proto == ";": + category_add_proto = "ALL" + if subnet_add_proto == ";": + subnet_add_proto = "ALL" + if address_add_proto == ";": + address_add_proto = "ALL" + if allgroup_add_proto == ";": + allgroup_add_proto = "ALL" + + if AllGroupType == "ALL": + for each_proto in filter(None, allgroup_add_proto.split(";")): + print(f"AllGroupType : checking protocol: {each_proto}") + all_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "AllowSpec", + "ALL", + all_protocol[0], + all_protocol[1], + ) + ) + if CategoryRef != "": + for each_proto in filter(None, category_add_proto.split(";")): + print(f"CategoryRef : checking protocol: {each_proto}") + cat_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "CategoryReferences", + get_categorygroup_extId(CategoryRef), + cat_protocol[0], + cat_protocol[1], + ) + ) + if SubnetRef != "": + for each_proto in filter(None, subnet_add_proto.split(";")): + print(f"SubnetRef : checking protocol: {each_proto}") + sub_protocol = check_protocol(each_proto) + subnet = SubnetRef.split("/") + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "Subnet", + {"value": subnet[0], "prefixLength": int(subnet[1])}, + sub_protocol[0], + sub_protocol[1], + ) + ) + if AddressGroupRef != "": + for each_proto in filter(None, address_add_proto.split(";")): + print(f"AddressGroupRef : checking protocol: {each_proto}") + addr_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "AddressGroupReferences", + get_addressGroup_extID(AddressGroupRef), + addr_protocol[0], + addr_protocol[1], + ) + ) + print(f"Added {endpoint} rule: {payload['rules']} \n") + + +def add_intra_group_rule(): + # variables + # 16. app_securedGroup 17. intra_groupAction = DENY / ALLOW + intra_spec = { + "spec": { + "$objectType": "microseg.v4.config.IntraEntityGroupRuleSpec", + "securedGroupCategoryReferences": get_categorygroup_extId( + "@@{securedGroup}@@" + ), + "securedGroupAction": "@@{intra_groupAction}@@", + }, + "type": "INTRA_GROUP", + } + return intra_spec + + +def create_payload(): + # Variables :: + # 1. name, 2. description : + # 3. type : ISOLATION / [QUARANTINE/APPLICATION] APPLICATION -> 3.1. Generic 3.2. VDI + # 4. state : SAVE, MONITOR(Apply), ENFORCE(Apply Enforce) + # 5. isIpv6TrafficAllowed : True/False 6. isHitlogEnabled : True/False + # 7. scope : ALL_VLAN / VPC_LIST 8. vpcReferences [if no vpcRef then def scope is all_vlan] + # 9. rule_type : QUARANTINE(QUARANTINE) / [INTRA_GROUP | APPLICATION] (APPLICATION/VDI) / TWO_ENV_ISOLATION (ISOLATION) + payload["name"] = "@@{policy_name}@@".strip() + payload["type"] = "APPLICATION".strip() + payload["$objectType"] = "microseg.v4.config.NetworkSecurityPolicy" + payload["state"] = "@@{policy_state}@@" + payload["scope"] = "ALL_VLAN" + if "@@{vpcReferences}@@" != "": + payload["scope"] = "VPC_LIST" + vpcextid = [x.split(":")[1] for x in "@@{vpcReferences}@@".split(",")] + payload["vpcReferences"] = vpcextid + if "@@{description}@@" != "": + payload["description"] = "@@{description}@@" + if "@@{isIpv6TrafficAllowed}@@" == "False": + payload["isIpv6TrafficAllowed"] = False + else: + payload["isIpv6TrafficAllowed"] = True + if "@@{isHitlogEnabled}@@" == "False": + payload["isHitlogEnabled"] = False + else: + payload["isHitlogEnabled"] = True + + payload["rules"] = [] + payload["rules"].append(add_intra_group_rule()) + print("Adding source references") + add_application_rule( + "src", + "@@{srcCategoryReferences}@@", + "@@{srcSubnet}@@", + "@@{srcAddressGroupReferences}@@", + "@@{srcAllGroupType}@@", + ) + print("Adding destination references") + add_application_rule( + "dest", + "@@{destCategoryReferences}@@", + "@@{destSubnet}@@", + "@@{destAddressGroupReferences}@@", + "@@{destAllGroupType}@@", + ) + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + if task_status_response.json()["data"]["status"] == "FAILED": + raise Exception(f"Task failed, got response {task_status_response.json()}") + + +def create(): + create_payload() + print(f"payload: {payload}") + create_security_policy_response = session.post( + "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@" + ), + json=payload, + headers={ + "Content-Type": "application/json", + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"create response: {create_security_policy_response.json()}") + create_security_policy_response.raise_for_status() + task_uuid = create_security_policy_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +create() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_destAddressGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_destAddressGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..e95f804a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_destAddressGroupReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_addressGroup(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_addressGroup() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_destCategoryReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_destCategoryReferences_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_destCategoryReferences_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_dest_serviceGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_dest_serviceGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..1ae94d4e1 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_dest_serviceGroupReferences_Task_SampleTask.py @@ -0,0 +1,52 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_policies(): + headers = {"Accept": "application/json"} + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + limit = 100 + page = 0 + results_len = 1 + tmp = [] + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + try: + data = response.json()["data"] + for each in data: + if "tcpServices" in each.keys(): + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["tcpServices"] + ] + else: + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["udpServices"] + ] + tmp.append(each["name"] + ":" + ",".join(port_data)) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_policies() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_securedGroup_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_securedGroup_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_securedGroup_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_srcAddressGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_srcAddressGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..e95f804a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_srcAddressGroupReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_addressGroup(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_addressGroup() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_srcCategoryReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_srcCategoryReferences_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_srcCategoryReferences_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_src_serviceGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_src_serviceGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..8fb89c854 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_src_serviceGroupReferences_Task_SampleTask.py @@ -0,0 +1,52 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_services(): + headers = {"Accept": "application/json"} + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + limit = 100 + page = 0 + results_len = 1 + tmp = [] + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + try: + data = response.json()["data"] + for each in data: + if "tcpServices" in each.keys(): + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["tcpServices"] + ] + else: + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["udpServices"] + ] + tmp.append(each["name"] + ":" + ",".join(port_data)) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_services() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_vpcReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_vpcReferences_Task_SampleTask.py new file mode 100644 index 000000000..f3e1d5f87 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateApplicationPolicyGeneric_variable_vpcReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_vpcs(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/networking/{}/config/vpcs".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"] + ":" + each["extId"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_vpcs() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_Task_create_isolation_policy.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_Task_create_isolation_policy.py new file mode 100644 index 000000000..1ffb51750 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_Task_create_isolation_policy.py @@ -0,0 +1,164 @@ +import requests +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + keyname = name.split(":")[0] + keyvalue = name.split(":")[1] + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={ + "$page": 0, + "$limit": 1, + "$filter": f"key eq '{keyname}' and value eq '{keyvalue}'", + }, + ) + # print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if ( + id_key in data[0] + and data[0]["key"] == keyname + and data[0]["value"] == keyvalue + ): + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_category_extID(category_name): + return get_resource_ext_id( + "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@" + ), + category_name, + ) + + +def get_categorygroup_extId(names): + group_names = names.split(",") + extId_list = [] + for category_name in group_names: + extId_list.append(get_category_extID(category_name)) + return extId_list + + +def add_two_env_isolation_rule(): + # Variables : + # 10. firstIsolationGroup + # 11. secondIsolationGroup #both comma separated list + payload["rules"][0].update( + { + "spec": { + "firstIsolationGroup": get_categorygroup_extId( + "@@{firstIsolationGroup}@@" + ), + "secondIsolationGroup": get_categorygroup_extId( + "@@{secondIsolationGroup}@@" + ), + "$objectType": "microseg.v4.config.TwoEnvIsolationRuleSpec", + } + } + ) + + +def create_payload(): + # Variables :: + # 1. name, 2. description : + # 3. type : ISOLATION / [QUARANTINE/APPLICATION] APPLICATION -> 3.1. Generic 3.2. VDI + # 4. state : SAVE, MONITOR(Apply), ENFORCE(Apply Enforce) + # 5. isIpv6TrafficAllowed : True/False 6. isHitlogEnabled : True/False + # 7. scope : ALL_VLAN / VPC_LIST 8. vpcReferences [if no vpcRef then def scope is all_vlan] + # 9. rule_type : QUARANTINE / INTRA_GROUP / APPLICATION / TWO_ENV_ISOLATION + payload["name"] = "@@{policy_name}@@".strip() + payload["type"] = "ISOLATION" + payload["$objectType"] = "microseg.v4.config.NetworkSecurityPolicy" + payload["state"] = "@@{policy_state}@@" + + if "@@{scope}@@" != "": + payload["scope"] = "@@{scope}@@" + elif "@@{vpcReferences}@@" != "": + payload["scope"] = "VPC_LIST" + vpclist = [vpc.split(":")[1] for vpc in "@@{vpcReferences}@@".split(",")] + payload["vpcReferences"] = vpclist + else: + payload["scope"] = "ALL_VLAN" + + if "@@{description}@@" != "": + payload["description"] = "@@{description}@@" + + payload["isIpv6TrafficAllowed"] = False + if "@@{isIpv6TrafficAllowed}@@" == "True": + payload["isIpv6TrafficAllowed"] = True + payload["isHitlogEnabled"] = False + if "@@{isHitlogEnabled}@@" == "True": + payload["isHitlogEnabled"] = True + + payload["rules"] = [{"type": "TWO_ENV_ISOLATION"}] + add_two_env_isolation_rule() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create(): + create_payload() + print(f"payload: {payload}") + create_security_policy_response = session.post( + "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@" + ), + json=payload, + headers={ + "Content-Type": "application/json", + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"create response: {create_security_policy_response.json()}") + create_security_policy_response.raise_for_status() + task_uuid = create_security_policy_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +create() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_firstIsolationGroup_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_firstIsolationGroup_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_firstIsolationGroup_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_secondIsolationGroup_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_secondIsolationGroup_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_secondIsolationGroup_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_vpcReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_vpcReferences_Task_SampleTask.py new file mode 100644 index 000000000..4706e71bd --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateIsolationPolicy_variable_vpcReferences_Task_SampleTask.py @@ -0,0 +1,27 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def apiCall(url): + r = requests.post(url, json=payload, auth=(PC_USERNAME, PC_PASSWORD), verify=False) + r = json.loads(r.content) + return r + + +# get vpc uuid +api = "https://{}:{}/api/nutanix/v3/vpcs/list".format(PC_IP, PC_PORT) +payload = {"kind": "vpc", "offset": 0} +r = apiCall(api) + +tmp = [] +for cname in r["entities"]: + vpcname = cname["status"]["name"] + vpcuuid = cname["metadata"]["uuid"] + vpctype = cname["status"]["resources"]["vpc_type"] + specid = cname["metadata"]["spec_version"] + tmp.append(vpcname + ":" + vpcuuid + ":" + vpctype) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_Task_CreateVDIpolicy.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_Task_CreateVDIpolicy.py new file mode 100644 index 000000000..5a0f1cf03 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_Task_CreateVDIpolicy.py @@ -0,0 +1,384 @@ +import requests +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + keyname = name.split(":")[0] + keyvalue = name.split(":")[1] + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={ + "$page": 0, + "$limit": 1, + "$filter": f"key eq '{keyname}' and value eq '{keyvalue}'", + }, + ) + # print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if ( + id_key in data[0] + and data[0]["key"] == keyname + and data[0]["value"] == keyvalue + ): + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_category_extID(category_name): + return get_resource_ext_id( + "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@" + ), + category_name, + ) + + +def get_categorygroup_extId(names): + group_names = names.split(",") + extId_list = [] + for category_name in group_names: + extId_list.append(get_category_extID(category_name)) + return extId_list + + +def get_servicesAndAddr_extId(url, c_name, id_key="extId"): + response = session.get( + url, + headers={"accept": "application/json"}, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{c_name}'"}, + ) + # print(f"response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == c_name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {c_name}") + + +def get_serviesGroup_extId(names): + # value - ServiceName:, ServiceName: - , ... + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + extId = get_servicesAndAddr_extId(url, names) + return extId + + +def get_addressGroup_extID(names): + group_names = names.split(",") + extId_list = [] + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + for group_name in group_names: + extId_list.append(get_servicesAndAddr_extId(url, group_name)) + return extId_list + + +# ------------------------------------------------------------------------------------------ + + +def add_tcpudpports(proto_value): + tcpports = proto_value.split(",") + tmp = [] + for ports in tcpports: + if "-" in ports: + startport = ports.split("-")[0] + endport = ports.split("-")[1] + else: + startport = ports + endport = ports + tmp.append({"startPort": int(startport), "endPort": int(endport)}) + return tmp + + +def add_icmpports(proto_value): + # format : ANY,ANY or ANY,port or port,ANY or port,port + icmp_data = proto_value.split(",") + icmpService_data = [] + tmp1 = {} + if proto_value == "ANY,ANY": + tmp1["isAllAllowed"] = True + elif icmp_data[0] != "ANY": + tmp1["type"] = int(icmp_data[0]) + elif icmp_data[1] != "ANY": + tmp1["code"] = int(icmp_data[1]) + icmpService_data.append(tmp1) + return icmpService_data + + +def check_protocol(proto): + # default will be ALL + if "ALL" in proto or proto == "": + proto_type = "ALL" + else: + proto_type = proto.split(":")[0] + proto_value = proto.split(":")[1] + if proto_type == "ALL": + key1 = "isAllProtocolAllowed" + value1 = True + elif proto_type == "TCP" or proto_type == "UDP": + key1 = "tcpServices" + value1 = add_tcpudpports(proto_value) + elif proto_type == "ICMP": + key1 = "icmpServices" + value1 = add_icmpports(proto_value) + elif proto_type == "SERVICE": + proto_value = proto.split(":", 1)[1] + s_tmp = [] + for p in proto_value.split(","): + service_name = p.split(":")[0] + s_tmp.append(get_serviesGroup_extId(service_name)) + key1 = "serviceGroupReferences" + value1 = s_tmp + return key1, value1 + + +def add_common(type, dictkey, dictvalue, proto_key, proto_val): + basic = { + "type": type, + "spec": { + "$objectType": "microseg.v4.config.ApplicationRuleSpec", + "securedGroupCategoryReferences": get_categorygroup_extId( + "@@{securedGroup}@@" + ), + dictkey: dictvalue, + }, + } + if proto_key != "": + basic["spec"][proto_key] = proto_val + return basic + + +def add_application_rule( + endpoint, CategoryRef, SubnetRef, AddressGroupRef, AllGroupType +): + # variables : + # repeate 16. securedGroup = securedGroupCategoryReferences + # 19. srcCategoryReferences -> src_catProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 21. srcSubnet -> src_subnetProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 23. srcAddressGroupReferences -> src_addrProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 25. srcAllGroupType = ALL or None -> src_allProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 27. serviceGroupReferences + # add_proto == src_ProtocolType|dest_ProtocolType ==> ALL; TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT + # And servicesGroupReference + add_proto = "" + + if endpoint == "src": + if "@@{src_serviceGroupReferences}@@" != "": + add_proto = "SERVICE:@@{src_serviceGroupReferences}@@" + category_add_proto = add_proto + ";" + "@@{src_CategoryProtocolType}@@" + subnet_add_proto = add_proto + ";" + "@@{src_SubnetProtocolType}@@" + address_add_proto = add_proto + ";" + "@@{src_AddressProtocolType}@@" + allgroup_add_proto = add_proto + ";" + "@@{src_AllGroupProtocolType}@@" + elif endpoint == "dest": + if "@@{dest_serviceGroupReferences}@@" != "": + add_proto = "SERVICE:@@{dest_serviceGroupReferences}@@" + category_add_proto = add_proto + ";" + "@@{dest_CategoryProtocolType}@@" + subnet_add_proto = add_proto + ";" + "@@{dest_SubnetProtocolType}@@" + address_add_proto = add_proto + ";" + "@@{dest_AddressProtocolType}@@" + allgroup_add_proto = add_proto + ";" + "@@{dest_AllGroupProtocolType}@@" + + # if no protocol defined then default willbe ALL + if category_add_proto == ";": + category_add_proto = "ALL" + if subnet_add_proto == ";": + subnet_add_proto = "ALL" + if address_add_proto == ";": + address_add_proto = "ALL" + if allgroup_add_proto == ";": + allgroup_add_proto = "ALL" + + if AllGroupType == "ALL": + for each_proto in filter(None, allgroup_add_proto.split(";")): + print(f"AllGroupType : checking protocol: {each_proto}") + all_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "AllowSpec", + "ALL", + all_protocol[0], + all_protocol[1], + ) + ) + if CategoryRef != "": + for each_proto in filter(None, category_add_proto.split(";")): + print(f"CategoryRef : checking protocol: {each_proto}") + cat_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "CategoryReferences", + get_categorygroup_extId(CategoryRef), + cat_protocol[0], + cat_protocol[1], + ) + ) + if SubnetRef != "": + for each_proto in filter(None, subnet_add_proto.split(";")): + print(f"SubnetRef : checking protocol: {each_proto}") + sub_protocol = check_protocol(each_proto) + subnet = SubnetRef.split("/") + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "Subnet", + {"value": subnet[0], "prefixLength": int(subnet[1])}, + sub_protocol[0], + sub_protocol[1], + ) + ) + if AddressGroupRef != "": + for each_proto in filter(None, address_add_proto.split(";")): + print(f"AddressGroupRef : checking protocol: {each_proto}") + addr_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "AddressGroupReferences", + get_addressGroup_extID(AddressGroupRef), + addr_protocol[0], + addr_protocol[1], + ) + ) + print(f"Added {endpoint} rule: {payload['rules']} \n") + + +def add_intra_group_rule(): + # variables + # 16. app_securedGroup 17. intra_groupAction = DENY / ALLOW + intra_spec = { + "spec": { + "$objectType": "microseg.v4.config.IntraEntityGroupRuleSpec", + "securedGroupCategoryReferences": get_categorygroup_extId( + "@@{securedGroup}@@" + ), + "securedGroupAction": "@@{intra_groupAction}@@", + }, + "type": "INTRA_GROUP", + } + return intra_spec + + +def create_payload(): + # Variables :: + # 1. name, 2. description : + # 3. type : ISOLATION / [QUARANTINE/APPLICATION] APPLICATION -> 3.1. Generic 3.2. VDI + # 4. state : SAVE, MONITOR(Apply), ENFORCE(Apply Enforce) + # 5. isIpv6TrafficAllowed : True/False 6. isHitlogEnabled : True/False + # 7. scope : ALL_VLAN / VPC_LIST 8. vpcReferences [if no vpcRef then def scope is all_vlan] + # 9. rule_type : QUARANTINE(QUARANTINE) / [INTRA_GROUP | APPLICATION] (APPLICATION/VDI) / TWO_ENV_ISOLATION (ISOLATION) + payload["name"] = "@@{policy_name}@@".strip() + payload["type"] = "APPLICATION".strip() + payload["$objectType"] = "microseg.v4.config.NetworkSecurityPolicy" + payload["state"] = "@@{policy_state}@@" + payload["scope"] = "ALL_VLAN" + if "@@{vpcReferences}@@" != "": + payload["scope"] = "VPC_LIST" + vpcextid = [x.split(":")[1] for x in "@@{vpcReferences}@@".split(",")] + payload["vpcReferences"] = vpcextid + if "@@{description}@@" != "": + payload["description"] = "@@{description}@@" + if "@@{isIpv6TrafficAllowed}@@" == "False": + payload["isIpv6TrafficAllowed"] = False + else: + payload["isIpv6TrafficAllowed"] = True + if "@@{isHitlogEnabled}@@" == "False": + payload["isHitlogEnabled"] = False + else: + payload["isHitlogEnabled"] = True + + payload["rules"] = [] + payload["rules"].append(add_intra_group_rule()) + print("Adding source references") + add_application_rule( + "src", + "@@{srcCategoryReferences}@@", + "@@{srcSubnet}@@", + "@@{srcAddressGroupReferences}@@", + "@@{srcAllGroupType}@@", + ) + print("Adding destination references") + add_application_rule( + "dest", + "@@{destCategoryReferences}@@", + "@@{destSubnet}@@", + "@@{destAddressGroupReferences}@@", + "@@{destAllGroupType}@@", + ) + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + if task_status_response.json()["data"]["status"] == "FAILED": + raise Exception(f"Task failed, got response {task_status_response.json()}") + + +def create(): + create_payload() + print(f"payload: {payload}") + create_security_policy_response = session.post( + "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@" + ), + json=payload, + headers={ + "Content-Type": "application/json", + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"create response: {create_security_policy_response.json()}") + create_security_policy_response.raise_for_status() + task_uuid = create_security_policy_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +create() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_destAddressGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_destAddressGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..e95f804a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_destAddressGroupReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_addressGroup(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_addressGroup() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_destCategoryReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_destCategoryReferences_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_destCategoryReferences_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..1ae94d4e1 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py @@ -0,0 +1,52 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_policies(): + headers = {"Accept": "application/json"} + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + limit = 100 + page = 0 + results_len = 1 + tmp = [] + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + try: + data = response.json()["data"] + for each in data: + if "tcpServices" in each.keys(): + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["tcpServices"] + ] + else: + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["udpServices"] + ] + tmp.append(each["name"] + ":" + ",".join(port_data)) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_policies() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..e95f804a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_addressGroup(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_addressGroup() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_srcCategoryReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_srcCategoryReferences_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_srcCategoryReferences_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..8fb89c854 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py @@ -0,0 +1,52 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_services(): + headers = {"Accept": "application/json"} + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + limit = 100 + page = 0 + results_len = 1 + tmp = [] + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + try: + data = response.json()["data"] + for each in data: + if "tcpServices" in each.keys(): + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["tcpServices"] + ] + else: + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["udpServices"] + ] + tmp.append(each["name"] + ":" + ",".join(port_data)) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_services() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_vpcReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_vpcReferences_Task_SampleTask.py new file mode 100644 index 000000000..f3e1d5f87 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_CreateVDIPolicy_variable_vpcReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_vpcs(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/networking/{}/config/vpcs".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"] + ":" + each["extId"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_vpcs() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Delete_Task_Delete.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Delete_Task_Delete.py new file mode 100644 index 000000000..dfea82815 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Delete_Task_Delete.py @@ -0,0 +1,57 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def delete(extId): + response = session.delete( + "https://{}:{}/api/microseg/{}/config/policies/{}".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@", extId + ), + headers={ + "Content-Type": "application/json", + "NTNX-Request-Id": str(uuid.uuid4()), + }, + timeout=120, + ) + print(f"delete response: {response.text}") + response.raise_for_status() + task_uuid = response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +policy_extId = "@@{policy_name}@@".split(":")[1] +delete(policy_extId) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Delete_variable_policy_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Delete_variable_policy_name_Task_SampleTask.py new file mode 100644 index 000000000..bd228810e --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Delete_variable_policy_name_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_policies(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["name"] + ":" + each["extId"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_policies() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Get_Task_Get.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Get_Task_Get.py new file mode 100644 index 000000000..4e00293b6 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_Get_Task_Get.py @@ -0,0 +1,27 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def get_policies(extId): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/microseg/{}/config/policies/{}".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@", extId + ), + auth=(PC_USERNAME, PC_PASSWORD), + headers=headers, + verify=False, + ) + print(f"response: {response.json()}") + response.raise_for_status() + print("get_policies = " + json.dumps(json.dumps(response.json()["data"]))) + + +extId = "@@{extId}@@".strip() +get_policies(extId) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_List_Task_List.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_List_Task_List.py new file mode 100644 index 000000000..ea18678e7 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_List_Task_List.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def list_security_policy(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + print(f"response: {response.json()}") + response.raise_for_status() + data = response.json() + if "data" in data: + print("list_security_policies = " + json.dumps(json.dumps(data))) + + +params = { + "$page": @@{page}@@, + "$limit": @@{limit}@@, +} +if "@@{select}@@".strip(): + params["$select"] = "@@{select}@@" +if "@@{orderby}@@".strip(): + params["$orderby"] = "@@{orderby}@@" +if "@@{filter}@@": + params["filter"] = "@@{filter}@@" + + +list_security_policy(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_Task_UpdateApplicationPolicy.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_Task_UpdateApplicationPolicy.py new file mode 100644 index 000000000..02a2ee07c --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_Task_UpdateApplicationPolicy.py @@ -0,0 +1,418 @@ +import requests +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + keyname = name.split(":")[0] + keyvalue = name.split(":")[1] + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={ + "$page": 0, + "$limit": 1, + "$filter": f"key eq '{keyname}' and value eq '{keyvalue}'", + }, + ) + # print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if ( + id_key in data[0] + and data[0]["key"] == keyname + and data[0]["value"] == keyvalue + ): + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_category_extID(category_name): + return get_resource_ext_id( + "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@" + ), + category_name, + ) + + +def get_categorygroup_extId(names): + group_names = names.split(",") + extId_list = [] + for category_name in group_names: + extId_list.append(get_category_extID(category_name)) + return extId_list + + +def get_servicesAndAddr_extId(url, c_name, id_key="extId"): + response = session.get( + url, + headers={"accept": "application/json"}, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{c_name}'"}, + ) + # print(f"response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == c_name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {c_name}") + + +def get_serviesGroup_extId(names): + # value - ServiceName:, ServiceName: - , ... + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + extId = get_servicesAndAddr_extId(url, names) + return extId + + +def get_addressGroup_extID(names): + group_names = names.split(",") + extId_list = [] + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + for group_name in group_names: + extId_list.append(get_servicesAndAddr_extId(url, group_name)) + return extId_list + + +def get_policy_extId_eTag(name): + headers = {"accept": "application/json"} + params = {"$page": 0, "$limit": 50, "$filter": f"name eq '{name}'"} + response = session.get( + "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@" + ), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + extId = [d["extId"] for d in response.json()["data"] if d["name"] == name] + + response1 = session.get( + "https://{}:{}/api/microseg/{}/config/policies/{}".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@", extId[0] + ), + verify=False, + ) + response1.raise_for_status() + return extId[0], response1.headers["ETag"] + + +# ------------------------------------------------------------------------------------------ + + +def add_tcpudpports(proto_value): + tcpports = proto_value.split(",") + tmp = [] + for ports in tcpports: + if "-" in ports: + startport = ports.split("-")[0] + endport = ports.split("-")[1] + else: + startport = ports + endport = ports + tmp.append({"startPort": int(startport), "endPort": int(endport)}) + return tmp + + +def add_icmpports(proto_value): + # format : ANY,ANY or ANY,port or port,ANY or port,port + icmp_data = proto_value.split(",") + icmpService_data = [] + tmp1 = {} + if proto_value == "ANY,ANY": + tmp1["isAllAllowed"] = True + elif icmp_data[0] != "ANY": + tmp1["type"] = int(icmp_data[0]) + elif icmp_data[1] != "ANY": + tmp1["code"] = int(icmp_data[1]) + icmpService_data.append(tmp1) + return icmpService_data + + +def check_protocol(proto): + # default will be ALL + key1 = "" + value1 = "" + if "ALL" in proto or proto == "": + proto_type = "ALL" + else: + proto_type = proto.split(":")[0] + proto_value = proto.split(":")[1] + if proto_type == "ALL": + key1 = "isAllProtocolAllowed" + value1 = True + elif proto_type == "TCP" or proto_type == "UDP": + key1 = "tcpServices" + value1 = add_tcpudpports(proto_value) + elif proto_type == "ICMP": + key1 = "icmpServices" + value1 = add_icmpports(proto_value) + elif proto_type == "SERVICE": + proto_value = proto.split(":", 1)[1] + s_tmp = [] + for p in proto_value.split(","): + service_name = p.split(":")[0] + s_tmp.append(get_serviesGroup_extId(service_name)) + key1 = "serviceGroupReferences" + value1 = s_tmp + return key1, value1 + + +def add_common(type, dictkey, dictvalue, proto_key, proto_val): + basic = { + "type": type, + "spec": { + "$objectType": "microseg.v4.config.ApplicationRuleSpec", + "securedGroupCategoryReferences": get_categorygroup_extId( + "@@{securedGroup}@@" + ), + dictkey: dictvalue, + }, + } + if proto_key != "": + basic["spec"][proto_key] = proto_val + return basic + + +def add_application_rule( + endpoint, CategoryRef, SubnetRef, AddressGroupRef, AllGroupType +): + # variables : + # repeate 16. securedGroup = securedGroupCategoryReferences + # 19. srcCategoryReferences -> src_catProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 21. srcSubnet -> src_subnetProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 23. srcAddressGroupReferences -> src_addrProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 25. srcAllGroupType = ALL or None -> src_allProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 27. serviceGroupReferences + # add_proto == src_ProtocolType|dest_ProtocolType ==> ALL; TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT + # And servicesGroupReference + add_proto = "" + + if endpoint == "src": + if "@@{src_serviceGroupReferences}@@" != "": + add_proto = "SERVICE:@@{src_serviceGroupReferences}@@" + category_add_proto = add_proto + ";" + "@@{src_CategoryProtocolType}@@" + subnet_add_proto = add_proto + ";" + "@@{src_SubnetProtocolType}@@" + address_add_proto = add_proto + ";" + "@@{src_AddressProtocolType}@@" + allgroup_add_proto = add_proto + ";" + "@@{src_AllGroupProtocolType}@@" + elif endpoint == "dest": + if "@@{dest_serviceGroupReferences}@@" != "": + add_proto = "SERVICE:@@{dest_serviceGroupReferences}@@" + category_add_proto = add_proto + ";" + "@@{dest_CategoryProtocolType}@@" + subnet_add_proto = add_proto + ";" + "@@{dest_SubnetProtocolType}@@" + address_add_proto = add_proto + ";" + "@@{dest_AddressProtocolType}@@" + allgroup_add_proto = add_proto + ";" + "@@{dest_AllGroupProtocolType}@@" + + # if no protocol defined then default willbe ALL + if category_add_proto == ";": + category_add_proto = "ALL" + if subnet_add_proto == ";": + subnet_add_proto = "ALL" + if address_add_proto == ";": + address_add_proto = "ALL" + if allgroup_add_proto == ";": + allgroup_add_proto = "ALL" + + if AllGroupType == "ALL": + for each_proto in filter(None, allgroup_add_proto.split(";")): + print(f"AllGroupType : checking protocol: {each_proto}") + all_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "AllowSpec", + "ALL", + all_protocol[0], + all_protocol[1], + ) + ) + if CategoryRef != "": + for each_proto in filter(None, category_add_proto.split(";")): + print(f"CategoryRef : checking protocol: {each_proto}") + cat_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "CategoryReferences", + get_categorygroup_extId(CategoryRef), + cat_protocol[0], + cat_protocol[1], + ) + ) + if SubnetRef != "": + for each_proto in filter(None, subnet_add_proto.split(";")): + print(f"SubnetRef : checking protocol: {each_proto}") + sub_protocol = check_protocol(each_proto) + subnet = SubnetRef.split("/") + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "Subnet", + {"value": subnet[0], "prefixLength": int(subnet[1])}, + sub_protocol[0], + sub_protocol[1], + ) + ) + if AddressGroupRef != "": + for each_proto in filter(None, address_add_proto.split(";")): + print(f"AddressGroupRef : checking protocol: {each_proto}") + addr_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "APPLICATION", + endpoint + "AddressGroupReferences", + get_addressGroup_extID(AddressGroupRef), + addr_protocol[0], + addr_protocol[1], + ) + ) + print(f"Added {endpoint} rule: {payload['rules']} \n") + + +def add_intra_group_rule(): + # variables + # 16. app_securedGroup 17. intra_groupAction = DENY / ALLOW + intra_spec = { + "spec": { + "$objectType": "microseg.v4.config.IntraEntityGroupRuleSpec", + "securedGroupCategoryReferences": get_categorygroup_extId( + "@@{securedGroup}@@" + ), + "securedGroupAction": "@@{intra_groupAction}@@", + }, + "type": "INTRA_GROUP", + } + return intra_spec + + +def create_payload(): + # Variables :: + # 1. name, 2. description : + # 3. type : ISOLATION / [QUARANTINE/APPLICATION] APPLICATION -> 3.1. Generic 3.2. VDI + # 4. state : SAVE, MONITOR(Apply), ENFORCE(Apply Enforce) + # 5. isIpv6TrafficAllowed : True/False 6. isHitlogEnabled : True/False + # 7. scope : ALL_VLAN / VPC_LIST 8. vpcReferences [if no vpcRef then def scope is all_vlan] + # 9. rule_type : QUARANTINE(QUARANTINE) / [INTRA_GROUP | APPLICATION] (APPLICATION/VDI) / TWO_ENV_ISOLATION (ISOLATION) + payload["name"] = "@@{policy_new_name}@@".strip() + payload["type"] = "APPLICATION".strip() + payload["$objectType"] = "microseg.v4.config.NetworkSecurityPolicy" + payload["state"] = "@@{policy_state}@@" + payload["scope"] = "ALL_VLAN" + if "@@{vpcReferences}@@" != "": + payload["scope"] = "VPC_LIST" + vpcextid = [x.split(":")[1] for x in "@@{vpcReferences}@@".split(",")] + payload["vpcReferences"] = vpcextid + if "@@{description}@@" != "": + payload["description"] = "@@{description}@@" + if "@@{isIpv6TrafficAllowed}@@" == "False": + payload["isIpv6TrafficAllowed"] = False + else: + payload["isIpv6TrafficAllowed"] = True + if "@@{isHitlogEnabled}@@" == "False": + payload["isHitlogEnabled"] = False + else: + payload["isHitlogEnabled"] = True + + # Quarantine only for update forensic type. + # if 'QUARANTINE' in rule_type: + # payload['rules']['type'] = "QUARANTINE" + # payload['rules'].append(add_quarantine_rule()) + + payload["rules"] = [] + payload["rules"].append(add_intra_group_rule()) + print("Adding source references") + add_application_rule( + "src", + "@@{srcCategoryReferences}@@", + "@@{srcSubnet}@@", + "@@{srcAddressGroupReferences}@@", + "@@{srcAllGroupType}@@", + ) + print("Adding destination references") + add_application_rule( + "dest", + "@@{destCategoryReferences}@@", + "@@{destSubnet}@@", + "@@{destAddressGroupReferences}@@", + "@@{destAllGroupType}@@", + ) + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + if task_status_response.json()["data"]["status"] == "FAILED": + raise Exception(f"Task failed, got response {task_status_response.json()}") + + +def update(): + create_payload() + print(f"payload: {payload}") + extId, eTag = get_policy_extId_eTag("@@{policy_name}@@") + print(f'Policy Name:{"@@{policy_name}@@"}, ETag:{eTag}') + update_security_policy_response = session.put( + "https://{}:{}/api/microseg/{}/config/policies/{}".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@", extId + ), + json=payload, + headers={ + "Content-Type": "application/json", + "If-Match": eTag, + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"create response: {update_security_policy_response.json()}") + update_security_policy_response.raise_for_status() + task_uuid = update_security_policy_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +update() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_destAddressGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_destAddressGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..e95f804a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_destAddressGroupReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_addressGroup(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_addressGroup() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_destCategoryReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_destCategoryReferences_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_destCategoryReferences_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..1ae94d4e1 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py @@ -0,0 +1,52 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_policies(): + headers = {"Accept": "application/json"} + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + limit = 100 + page = 0 + results_len = 1 + tmp = [] + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + try: + data = response.json()["data"] + for each in data: + if "tcpServices" in each.keys(): + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["tcpServices"] + ] + else: + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["udpServices"] + ] + tmp.append(each["name"] + ":" + ",".join(port_data)) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_policies() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_policy_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_policy_name_Task_SampleTask.py new file mode 100644 index 000000000..2a069889a --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_policy_name_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_policies(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + if each["type"] == "APPLICATION": + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_policies() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_securedGroup_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_securedGroup_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_securedGroup_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..e95f804a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_addressGroup(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_addressGroup() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_srcCategoryReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_srcCategoryReferences_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_srcCategoryReferences_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..8fb89c854 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py @@ -0,0 +1,52 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_services(): + headers = {"Accept": "application/json"} + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + limit = 100 + page = 0 + results_len = 1 + tmp = [] + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + try: + data = response.json()["data"] + for each in data: + if "tcpServices" in each.keys(): + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["tcpServices"] + ] + else: + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["udpServices"] + ] + tmp.append(each["name"] + ":" + ",".join(port_data)) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_services() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_vpcReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_vpcReferences_Task_SampleTask.py new file mode 100644 index 000000000..f3e1d5f87 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateApplicationPolicy_variable_vpcReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_vpcs(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/networking/{}/config/vpcs".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"] + ":" + each["extId"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_vpcs() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_Task_update_isolation_policy.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_Task_update_isolation_policy.py new file mode 100644 index 000000000..7fff9a3eb --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_Task_update_isolation_policy.py @@ -0,0 +1,192 @@ +import requests +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + keyname = name.split(":")[0] + keyvalue = name.split(":")[1] + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={ + "$page": 0, + "$limit": 1, + "$filter": f"key eq '{keyname}' and value eq '{keyvalue}'", + }, + ) + # print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if ( + id_key in data[0] + and data[0]["key"] == keyname + and data[0]["value"] == keyvalue + ): + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_category_extID(category_name): + return get_resource_ext_id( + "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@" + ), + category_name, + ) + + +def get_categorygroup_extId(names): + group_names = names.split(",") + extId_list = [] + for category_name in group_names: + extId_list.append(get_category_extID(category_name)) + return extId_list + + +def get_policy_extId_eTag(name): + headers = {"accept": "application/json"} + params = {"$page": 0, "$limit": 50, "$filter": f"name eq '{name}'"} + response = session.get( + "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@" + ), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + extId = [d["extId"] for d in response.json()["data"] if d["name"] == name] + + response1 = session.get( + "https://{}:{}/api/microseg/{}/config/policies/{}".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@", extId[0] + ), + verify=False, + ) + response1.raise_for_status() + return extId[0], response1.headers["ETag"] + + +# --------------------------------------------------------------------------------- +def add_two_env_isolation_rule(): + # Variables : + # 10. firstIsolationGroup + # 11. secondIsolationGroup #both comma separated list + payload["rules"][0].update( + { + "spec": { + "firstIsolationGroup": get_categorygroup_extId( + "@@{firstIsolationGroup}@@" + ), + "secondIsolationGroup": get_categorygroup_extId( + "@@{secondIsolationGroup}@@" + ), + "$objectType": "microseg.v4.config.TwoEnvIsolationRuleSpec", + } + } + ) + + +def create_payload(): + # Variables :: + # 1. name, 2. description : + # 3. type : ISOLATION / [QUARANTINE/APPLICATION] APPLICATION -> 3.1. Generic 3.2. VDI + # 4. state : SAVE, MONITOR(Apply), ENFORCE(Apply Enforce) + # 5. isIpv6TrafficAllowed : True/False 6. isHitlogEnabled : True/False + # 7. scope : ALL_VLAN / VPC_LIST 8. vpcReferences [if no vpcRef then def scope is all_vlan] + # 9. rule_type : QUARANTINE / INTRA_GROUP / APPLICATION / TWO_ENV_ISOLATION + payload["name"] = "@@{policy_new_name}@@" + payload["type"] = "ISOLATION" + payload["$objectType"] = "microseg.v4.config.NetworkSecurityPolicy" + payload["state"] = "@@{policy_state}@@" + + if "@@{scope}@@" != "": + payload["scope"] = "@@{scope}@@" + elif "@@{vpcReferences}@@" != "": + payload["scope"] = "VPC_LIST" + vpclist = [vpc.split(":")[1] for vpc in "@@{vpcReferences}@@".split(",")] + payload["vpcReferences"] = vpclist + else: + payload["scope"] = "ALL_VLAN" + + if "@@{description}@@" != "": + payload["description"] = "@@{description}@@" + + payload["isIpv6TrafficAllowed"] = False + if "@@{isIpv6TrafficAllowed}@@" == "True": + payload["isIpv6TrafficAllowed"] = True + payload["isHitlogEnabled"] = False + if "@@{isHitlogEnabled}@@" == "True": + payload["isHitlogEnabled"] = True + + payload["rules"] = [{"type": "TWO_ENV_ISOLATION"}] + add_two_env_isolation_rule() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def update(): + create_payload() + print(f"payload: {payload}") + extId, eTag = get_policy_extId_eTag("@@{policy_name}@@") + print(f'Policy Name:{"@@{policy_name}@@"}, ETag:{eTag}') + update_security_policy_response = session.put( + "https://{}:{}/api/microseg/{}/config/policies/{}".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@", extId + ), + json=payload, + headers={ + "Content-Type": "application/json", + "If-Match": eTag, + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"create response: {update_security_policy_response.json()}") + update_security_policy_response.raise_for_status() + task_uuid = update_security_policy_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +update() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_firstIsolationGroup_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_firstIsolationGroup_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_firstIsolationGroup_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_policy_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_policy_name_Task_SampleTask.py new file mode 100644 index 000000000..9537e00c7 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_policy_name_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_policies(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + if each["type"] == "ISOLATION": + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_policies() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_secondIsolationGroup_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_secondIsolationGroup_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_secondIsolationGroup_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_vpcReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_vpcReferences_Task_SampleTask.py new file mode 100644 index 000000000..4706e71bd --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateIsolationPolicy_variable_vpcReferences_Task_SampleTask.py @@ -0,0 +1,27 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def apiCall(url): + r = requests.post(url, json=payload, auth=(PC_USERNAME, PC_PASSWORD), verify=False) + r = json.loads(r.content) + return r + + +# get vpc uuid +api = "https://{}:{}/api/nutanix/v3/vpcs/list".format(PC_IP, PC_PORT) +payload = {"kind": "vpc", "offset": 0} +r = apiCall(api) + +tmp = [] +for cname in r["entities"]: + vpcname = cname["status"]["name"] + vpcuuid = cname["metadata"]["uuid"] + vpctype = cname["status"]["resources"]["vpc_type"] + specid = cname["metadata"]["spec_version"] + tmp.append(vpcname + ":" + vpcuuid + ":" + vpctype) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_Task_updatequarantineforensicpolicy.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_Task_updatequarantineforensicpolicy.py new file mode 100644 index 000000000..1650628a4 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_Task_updatequarantineforensicpolicy.py @@ -0,0 +1,407 @@ +import requests +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + keyname = name.split(":")[0] + keyvalue = name.split(":")[1] + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={ + "$page": 0, + "$limit": 1, + "$filter": f"key eq '{keyname}' and value eq '{keyvalue}'", + }, + ) + # print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if ( + id_key in data[0] + and data[0]["key"] == keyname + and data[0]["value"] == keyvalue + ): + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_category_extID(category_name): + return get_resource_ext_id( + "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@" + ), + category_name, + ) + + +def get_categorygroup_extId(names): + group_names = names.split(",") + extId_list = [] + for category_name in group_names: + extId_list.append(get_category_extID(category_name)) + return extId_list + + +def get_servicesAndAddr_extId(url, c_name, id_key="extId"): + response = session.get( + url, + headers={"accept": "application/json"}, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{c_name}'"}, + ) + # print(f"response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == c_name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {c_name}") + + +def get_serviesGroup_extId(names): + # value - ServiceName:, ServiceName: - , ... + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + extId = get_servicesAndAddr_extId(url, names) + return extId + + +def get_addressGroup_extID(names): + group_names = names.split(",") + extId_list = [] + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + for group_name in group_names: + extId_list.append(get_servicesAndAddr_extId(url, group_name)) + return extId_list + + +def get_policy_extId_eTag(name): + headers = {"accept": "application/json"} + params = {"$page": 0, "$limit": 50, "$filter": f"name eq '{name}'"} + response = session.get( + "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@" + ), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + extId = [d["extId"] for d in response.json()["data"] if d["name"] == name] + + response1 = session.get( + "https://{}:{}/api/microseg/{}/config/policies/{}".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@", extId[0] + ), + verify=False, + ) + response1.raise_for_status() + return extId[0], response1.headers["ETag"] + + +# ------------------------------------------------------------------------------------------ + + +def add_tcpudpports(proto_value): + tcpports = proto_value.split(",") + tmp = [] + for ports in tcpports: + if "-" in ports: + startport = ports.split("-")[0] + endport = ports.split("-")[1] + else: + startport = ports + endport = ports + tmp.append({"startPort": int(startport), "endPort": int(endport)}) + return tmp + + +def add_icmpports(proto_value): + # format : ANY,ANY or ANY,port or port,ANY or port,port + icmp_data = proto_value.split(",") + icmpService_data = [] + tmp1 = {} + if proto_value == "ANY,ANY": + tmp1["isAllAllowed"] = True + elif icmp_data[0] != "ANY": + tmp1["type"] = int(icmp_data[0]) + elif icmp_data[1] != "ANY": + tmp1["code"] = int(icmp_data[1]) + icmpService_data.append(tmp1) + return icmpService_data + + +def check_protocol(proto): + # default will be ALL + if "ALL" in proto or proto == "": + proto_type = "ALL" + else: + proto_type = proto.strip().split(":")[0] + proto_value = proto.strip().split(":")[1] + if proto_type == "ALL": + key1 = "isAllProtocolAllowed" + value1 = True + elif proto_type == "TCP" or proto_type == "UDP": + key1 = "tcpServices" + value1 = add_tcpudpports(proto_value) + elif proto_type == "ICMP": + key1 = "icmpServices" + value1 = add_icmpports(proto_value) + elif proto_type == "SERVICE": + proto_value = proto.split(":", 1)[1] + s_tmp = [] + for p in proto_value.split(","): + service_name = p.split(":")[0] + s_tmp.append(get_serviesGroup_extId(service_name)) + key1 = "serviceGroupReferences" + value1 = s_tmp + return key1, value1 + + +def add_common(type, dictkey, dictvalue, proto_key, proto_val): + basic = { + "type": type, + "spec": { + "$objectType": "microseg.v4.config.ApplicationRuleSpec", + "securedGroupCategoryReferences": get_categorygroup_extId( + "Quarantine:Forensics" + ), + dictkey: dictvalue, + }, + } + if proto_key != "": + basic["spec"][proto_key] = proto_val + return basic + + +def add_application_rule( + endpoint, CategoryRef, SubnetRef, AddressGroupRef, AllGroupType +): + # variables : + # repeate 16. securedGroup = securedGroupCategoryReferences + # 19. srcCategoryReferences -> src_catProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 21. srcSubnet -> src_subnetProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 23. srcAddressGroupReferences -> src_addrProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 25. srcAllGroupType = ALL or None -> src_allProtocolType (drop down list [ALL|TCP|UDP|ICMP|SERVICE]) + # 27. serviceGroupReferences + # add_proto == src_ProtocolType|dest_ProtocolType ==> ALL; TCP:22,22-25; UDP:22,22-25; ICMP:ANY,ANY or ANY,PORT or PORT,ANY or PORT,PORT + # And servicesGroupReference + add_proto = "" + + if endpoint == "src": + if "@@{src_serviceGroupReferences}@@" != "": + add_proto = "SERVICE:@@{src_serviceGroupReferences}@@" + category_add_proto = add_proto + ";" + "@@{src_CategoryProtocolType}@@" + subnet_add_proto = add_proto + ";" + "@@{src_SubnetProtocolType}@@" + address_add_proto = add_proto + ";" + "@@{src_AddressProtocolType}@@" + allgroup_add_proto = add_proto + ";" + "@@{src_AllGroupProtocolType}@@" + elif endpoint == "dest": + if "@@{dest_serviceGroupReferences}@@" != "": + add_proto = "SERVICE:@@{dest_serviceGroupReferences}@@" + category_add_proto = add_proto + ";" + "@@{dest_CategoryProtocolType}@@" + subnet_add_proto = add_proto + ";" + "@@{dest_SubnetProtocolType}@@" + address_add_proto = add_proto + ";" + "@@{dest_AddressProtocolType}@@" + allgroup_add_proto = add_proto + ";" + "@@{dest_AllGroupProtocolType}@@" + + # if no protocol defined then default willbe ALL + if category_add_proto == ";": + category_add_proto = "ALL" + if subnet_add_proto == ";": + subnet_add_proto = "ALL" + if address_add_proto == ";": + address_add_proto = "ALL" + if allgroup_add_proto == ";": + allgroup_add_proto = "ALL" + + if AllGroupType == "ALL": + for each_proto in filter(None, allgroup_add_proto.split(";")): + print(f"AllGroupType : checking protocol: {each_proto}") + all_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "QUARANTINE", + endpoint + "AllowSpec", + "ALL", + all_protocol[0], + all_protocol[1], + ) + ) + if CategoryRef != "": + for each_proto in filter(None, category_add_proto.split(";")): + print(f"CategoryRef : checking protocol: {each_proto}") + cat_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "QUARANTINE", + endpoint + "CategoryReferences", + get_categorygroup_extId(CategoryRef), + cat_protocol[0], + cat_protocol[1], + ) + ) + if SubnetRef != "": + for each_proto in filter(None, subnet_add_proto.split(";")): + print(f"SubnetRef : checking protocol: {each_proto}") + sub_protocol = check_protocol(each_proto) + subnet = SubnetRef.split("/") + payload["rules"].append( + add_common( + "QUARANTINE", + endpoint + "Subnet", + {"value": subnet[0], "prefixLength": int(subnet[1])}, + sub_protocol[0], + sub_protocol[1], + ) + ) + if AddressGroupRef != "": + for each_proto in filter(None, address_add_proto.split(";")): + print(f"AddressGroupRef : checking protocol: {each_proto}") + addr_protocol = check_protocol(each_proto) + payload["rules"].append( + add_common( + "QUARANTINE", + endpoint + "AddressGroupReferences", + get_addressGroup_extID(AddressGroupRef), + addr_protocol[0], + addr_protocol[1], + ) + ) + print(f"Added {endpoint} rule: {payload['rules']} \n") + + +def add_intra_group_rule(name): + # variables + # 16. app_securedGroup 17. intra_groupAction = DENY / ALLOW + intra_spec = { + "spec": { + "$objectType": "microseg.v4.config.IntraEntityGroupRuleSpec", + "securedGroupCategoryReferences": get_categorygroup_extId(name), + "securedGroupAction": "DENY", + }, + "type": "INTRA_GROUP", + } + return intra_spec + + +def create_payload(): + # Variables :: + # 1. name, 2. description : + # 3. type : ISOLATION / [QUARANTINE/APPLICATION] APPLICATION -> 3.1. Generic 3.2. VDI + # 4. state : SAVE, MONITOR(Apply), ENFORCE(Apply Enforce) + # 5. isIpv6TrafficAllowed : True/False 6. isHitlogEnabled : True/False + # 7. scope : ALL_VLAN / VPC_LIST 8. vpcReferences [if no vpcRef then def scope is all_vlan] + # 9. rule_type : QUARANTINE(QUARANTINE) / [INTRA_GROUP | APPLICATION] (APPLICATION/VDI) / TWO_ENV_ISOLATION (ISOLATION) + payload["name"] = "Quarantine Forensic Policy" + payload["type"] = "QUARANTINE" + payload[ + "description" + ] = "System defined quarantine policy for Quarantine:Forensics category" + payload["$objectType"] = "microseg.v4.config.NetworkSecurityPolicy" + payload["state"] = "@@{policy_state}@@" + payload["scope"] = "ALL_VLAN" + + if "@@{isIpv6TrafficAllowed}@@" == "False": + payload["isIpv6TrafficAllowed"] = False + else: + payload["isIpv6TrafficAllowed"] = True + if "@@{isHitlogEnabled}@@" == "False": + payload["isHitlogEnabled"] = False + else: + payload["isHitlogEnabled"] = True + + payload["rules"] = [] + payload["rules"].append(add_intra_group_rule("Quarantine:Forensics")) + print("Adding source references") + add_application_rule( + "src", + "@@{srcCategoryReferences}@@", + "@@{srcSubnet}@@", + "@@{srcAddressGroupReferences}@@", + "@@{srcAllGroupType}@@", + ) + print("Adding destination references") + add_application_rule( + "dest", + "@@{destCategoryReferences}@@", + "@@{destSubnet}@@", + "@@{destAddressGroupReferences}@@", + "@@{destAllGroupType}@@", + ) + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + if task_status_response.json()["data"]["status"] == "FAILED": + raise Exception(f"Task failed, got response {task_status_response.json()}") + + +def update(): + create_payload() + print(f"payload: {payload}") + extId, eTag = get_policy_extId_eTag("@@{policy_name}@@") + print(f'Policy Name:{"@@{policy_name}@@"}, ETag:{eTag}') + update_security_policy_response = session.put( + "https://{}:{}/api/microseg/{}/config/policies/{}".format( + PC_IP, PC_PORT, "@@{flow_api_version}@@", extId + ), + json=payload, + headers={ + "Content-Type": "application/json", + "If-Match": eTag, + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"create response: {update_security_policy_response.json()}") + update_security_policy_response.raise_for_status() + task_uuid = update_security_policy_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +update() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_destAddressGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_destAddressGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..e95f804a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_destAddressGroupReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_addressGroup(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_addressGroup() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_destCategoryReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_destCategoryReferences_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_destCategoryReferences_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..1ae94d4e1 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_dest_serviceGroupReferences_Task_SampleTask.py @@ -0,0 +1,52 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_policies(): + headers = {"Accept": "application/json"} + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + limit = 100 + page = 0 + results_len = 1 + tmp = [] + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + try: + data = response.json()["data"] + for each in data: + if "tcpServices" in each.keys(): + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["tcpServices"] + ] + else: + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["udpServices"] + ] + tmp.append(each["name"] + ":" + ",".join(port_data)) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_policies() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_policy_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_policy_name_Task_SampleTask.py new file mode 100644 index 000000000..d6f6c31e0 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_policy_name_Task_SampleTask.py @@ -0,0 +1,45 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_policies(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + if ( + each["type"] == "QUARANTINE" + and each["name"] == "Quarantine Forensic Policy" + ): + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_policies() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..e95f804a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_srcAddressGroupReferences_Task_SampleTask.py @@ -0,0 +1,41 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_addressGroup(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/microseg/{}/config/address-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + tmp.append(each["name"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_addressGroup() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_srcCategoryReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_srcCategoryReferences_Task_SampleTask.py new file mode 100644 index 000000000..08c6e6187 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_srcCategoryReferences_Task_SampleTask.py @@ -0,0 +1,42 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_category(): + headers = { + "Accept": "application/json", + } + url = "https://{}:{}/api/prism/{}/config/categories".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + page = 0 + results_len = 1 + tmp = [] + + while results_len != 0: + params = {"$limit": 100, "$orderby": "key", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + response.raise_for_status() + try: + data = response.json()["data"] + for each in data: + # print(each['key']+':'+each['value']) + tmp.append(each["key"] + ":" + each["value"]) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_category() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py new file mode 100644 index 000000000..8fb89c854 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_SecurityPolicies_Action_UpdateQuarantineForensicPolicy_variable_src_serviceGroupReferences_Task_SampleTask.py @@ -0,0 +1,52 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def list_services(): + headers = {"Accept": "application/json"} + url = "https://{}:{}/api/microseg/{}/config/service-groups".format( + PC_IP, PC_PORT, "v4.0.b1" + ) + limit = 100 + page = 0 + results_len = 1 + tmp = [] + while results_len != 0: + params = {"$limit": 100, "$orderby": "name", "$page": page} + response = requests.get( + url, + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + try: + data = response.json()["data"] + for each in data: + if "tcpServices" in each.keys(): + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["tcpServices"] + ] + else: + port_data = [ + str(d["startPort"]) + if d["startPort"] == d["endPort"] + else str(d["startPort"]) + "-" + str(d["endPort"]) + for d in each["udpServices"] + ] + tmp.append(each["name"] + ":" + ",".join(port_data)) + results_len = len(data) + page += 1 + except: + break + print(",".join(tmp)) + + +list_services() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_Task_CreateExternalIPAMSubnet.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_Task_CreateExternalIPAMSubnet.py new file mode 100644 index 000000000..8a8720ed1 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_Task_CreateExternalIPAMSubnet.py @@ -0,0 +1,102 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def create_payload(): + pe_cluster_uuid = "@@{pe_cluster_uuid}@@".strip().split(":")[1] + virtual_switch_uuid = "@@{virtual_switch_uuid}@@".strip().split(":")[1] + description = "@@{description}@@" + vlan_id = int("@@{vlan_id}@@".strip()) + subnet_name = "@@{subnet_name}@@".strip() + subnet_ip = "@@{subnet_ip}@@".strip().split("/")[0] + prefix_length = int("@@{subnet_ip}@@".strip().split("/")[1]) + default_gateway_ip = "@@{default_gateway_ip}@@".strip() + ip_pool_list = [] + plist = "@@{pool_list}@@".strip().split(",") + if len(plist[0]) != 0: + for p in "@@{pool_list}@@".strip().split(","): + p = p.replace('"', "") + val = {"range": p} + ip_pool_list.append(val) + else: + raise Exception("IP Pool list is not provided.") + + enable_nat = @@{enable_nat}@@ + + payload1 = { + "spec": { + "name": subnet_name, + "description": description, + "resources": { + "subnet_type": "VLAN", + "virtual_switch_uuid": virtual_switch_uuid, + "is_external": True, + "ip_config": { + "default_gateway_ip": default_gateway_ip, + "pool_list": ip_pool_list, + "prefix_length": prefix_length, + "subnet_ip": subnet_ip, + }, + "enable_nat": enable_nat, + "external_connectivity_state": "DISABLED", + "vlan_id": vlan_id, + }, + "cluster_reference": {"kind": "cluster", "uuid": pe_cluster_uuid}, + }, + "metadata": {"kind": "subnet"}, + } + payload.update(payload1) + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create_ext_subnet_ipam(): + create_payload() + print(f"payload = {payload}") + create = session.post( + "https://{}:{}/api/nutanix/v3/subnets".format(PC_IP, PC_PORT), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"response = {create.json()}") + create.raise_for_status() + task_uuid = create.json()["status"]["execution_context"]["task_uuid"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +create_ext_subnet_ipam() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py new file mode 100644 index 000000000..f702b35dc --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py @@ -0,0 +1,31 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get pe cluster uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/clusters/list" +payload = {"kind": "cluster"} +r = apiCall(api) + +tmp = [] +pe_cluster_uuid = [ + cname["metadata"]["uuid"] + for cname in r["entities"] + if cname["spec"]["name"] != "Unnamed" +] +for cname in r["entities"]: + if cname["spec"]["name"] == "Unnamed": + continue + clustername = cname["spec"]["name"] + clusteruuid = cname["metadata"]["uuid"] + tmp.append(clustername + ":" + clusteruuid) + +# print("pe_cluster_uuid={}".format(pe_cluster_uuid[0])) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py new file mode 100644 index 000000000..66c123aa2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateExternalIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py @@ -0,0 +1,24 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.get(url, headers=headers, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get virtual switch uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/networking/v4.0.b1/config/virtual-switches" +headers = { + "x-cluster-id": "@@{pe_cluster_uuid}@@".strip().split(":")[1], + "accept": "application/json", +} + +r = apiCall(api) + +name = r["data"][0]["name"] +uuid = r["data"][0]["extId"] +# print("virtual_switch={}".format(name+':'+uuid)) +print(name + ":" + uuid) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_Task_CreateNutanixIPAM.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_Task_CreateNutanixIPAM.py new file mode 100644 index 000000000..51b89f0f1 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_Task_CreateNutanixIPAM.py @@ -0,0 +1,146 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def pool_list_payload(plist): + plist = plist.strip().split(",") + ip_pool_list = [] + if len(plist[0]) != 0: + for p in plist: + p = p.replace('"', "") + val = {"range": p} + ip_pool_list.append(val) + payload["spec"]["resources"]["ip_config"].update({"pool_list": ip_pool_list}) + + +def dhcp_payload(): + if ( + "@@{domain_name_server_list}@@" != "" + or "@@{boot_file_name}@@" != "" + or "@@{domain_name}@@" != "" + or "@@{tftp_server_name}@@" != "" + or "@@{domain_search_list}@@" != "" + ): + payload["spec"]["resources"]["ip_config"]["dhcp_options"] = {} + dnsip_list = "@@{domain_name_server_list}@@".strip().split(",") + dns_ip_list = [] + if len(dnsip_list[0]) != 0: + for p in dnsip_list: + p = p.strip().replace('"', "") + val = p + dns_ip_list.append(val) + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_name_server_list": dns_ip_list} + ) + + if "@@{boot_file_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"boot_file_name": "@@{boot_file_name}@@"} + ) + if "@@{domain_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_name": "@@{domain_name}@@"} + ) + if "@@{tftp_server_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"tftp_server_name": "@@{tftp_server_name}@@"} + ) + + ds_list = "@@{domain_search_list}@@".strip().split(",") + ds_ip_list = [] + if len(ds_list[0]) != 0: + for p in ds_list: + p = p.strip().replace('"', "") + val = p + ds_ip_list.append(val) + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_search_list": ds_ip_list} + ) + + +def create_payload(): + payload["spec"] = {"name": "@@{subnet_name}@@".strip()} + payload["spec"]["cluster_reference"] = { + "kind": "cluster", + "uuid": "@@{pe_cluster_uuid}@@".strip().split(":")[1], + } + if "@@{description}@@" != "": + payload["spec"].update({"description": "@@{description}@@"}) + + payload["spec"]["resources"] = { + "vlan_id": int("@@{vlan_id}@@".strip()), + "subnet_type": "VLAN", + "virtual_switch_uuid": "@@{virtual_switch_uuid}@@".strip().split(":")[1], + } + if "@@{default_gateway_ip}@@" != "": + payload["spec"]["resources"]["ip_config"] = { + "default_gateway_ip": "@@{default_gateway_ip}@@".strip() + } + if "@@{dhcp_server_address}@@" != "": + payload["spec"]["resources"]["ip_config"].update( + {"dhcp_server_address": {"ip": "@@{dhcp_server_address}@@"}} + ) + if "@@{subnet_ip}@@" != "": + payload["spec"]["resources"]["ip_config"].update( + { + "prefix_length": int("@@{subnet_ip}@@".strip().split("/")[1]), + "subnet_ip": "@@{subnet_ip}@@".strip().split("/")[0], + } + ) + pool_list_payload("@@{pool_list}@@") + dhcp_payload() + payload["metadata"] = {"kind": "subnet"} + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create_subnet(): + create_payload() + print(f"payload = {payload}") + create = session.post( + "https://{}:{}/api/nutanix/v3/subnets".format(PC_IP, PC_PORT), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"response = {create.json()}") + create.raise_for_status() + task_uuid = create.json()["status"]["execution_context"]["task_uuid"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +create_subnet() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py new file mode 100644 index 000000000..f702b35dc --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py @@ -0,0 +1,31 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get pe cluster uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/clusters/list" +payload = {"kind": "cluster"} +r = apiCall(api) + +tmp = [] +pe_cluster_uuid = [ + cname["metadata"]["uuid"] + for cname in r["entities"] + if cname["spec"]["name"] != "Unnamed" +] +for cname in r["entities"]: + if cname["spec"]["name"] == "Unnamed": + continue + clustername = cname["spec"]["name"] + clusteruuid = cname["metadata"]["uuid"] + tmp.append(clustername + ":" + clusteruuid) + +# print("pe_cluster_uuid={}".format(pe_cluster_uuid[0])) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py new file mode 100644 index 000000000..66c123aa2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateNutanixIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py @@ -0,0 +1,24 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.get(url, headers=headers, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get virtual switch uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/networking/v4.0.b1/config/virtual-switches" +headers = { + "x-cluster-id": "@@{pe_cluster_uuid}@@".strip().split(":")[1], + "accept": "application/json", +} + +r = apiCall(api) + +name = r["data"][0]["name"] +uuid = r["data"][0]["extId"] +# print("virtual_switch={}".format(name+':'+uuid)) +print(name + ":" + uuid) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateOverlaySubnet_Task_CreateOverlaySubnet.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateOverlaySubnet_Task_CreateOverlaySubnet.py new file mode 100644 index 000000000..b6075eb22 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateOverlaySubnet_Task_CreateOverlaySubnet.py @@ -0,0 +1,144 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def pool_list_payload(plist): + plist = plist.strip().split(",") + ip_pool_list = [] + if len(plist[0]) != 0: + for p in plist: + p = p.replace('"', "") + val = {"range": p} + ip_pool_list.append(val) + payload["spec"]["resources"]["ip_config"].update({"pool_list": ip_pool_list}) + + +def dhcp_payload(): + if ( + "@@{domain_name_server_list}@@" != "" + or "@@{boot_file_name}@@" != "" + or "@@{domain_name}@@" != "" + or "@@{tftp_server_name}@@" != "" + or "@@{domain_search_list}@@" != "" + ): + payload["spec"]["resources"]["ip_config"]["dhcp_options"] = {} + dnsip_list = "@@{domain_name_server_list}@@".strip().split(",") + dns_ip_list = [] + if len(dnsip_list[0]) != 0: + for p in dnsip_list: + p = p.strip().replace('"', "") + val = p + dns_ip_list.append(val) + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_name_server_list": dns_ip_list} + ) + + if "@@{boot_file_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"boot_file_name": "@@{boot_file_name}@@"} + ) + if "@@{domain_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_name": "@@{domain_name}@@"} + ) + if "@@{tftp_server_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"tftp_server_name": "@@{tftp_server_name}@@"} + ) + + ds_list = "@@{domain_search_list}@@".strip().split(",") + ds_ip_list = [] + if len(ds_list[0]) != 0: + for p in ds_list: + p = p.strip().replace('"', "") + val = p + ds_ip_list.append(val) + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_search_list": ds_ip_list} + ) + + +def create_payload(): + payload["spec"] = {"name": "@@{subnet_name}@@".strip()} + if "@@{description}@@" != "": + payload["spec"].update({"description": "@@{description}@@"}) + + payload["spec"]["resources"] = {"subnet_type": "OVERLAY"} + payload["spec"]["resources"]["vpc_reference"] = { + "kind": "vpc", + "uuid": "@@{vpc_uuid}@@".strip().split(":")[1], + } + payload["spec"]["resources"]["ip_config"] = { + "default_gateway_ip": "@@{default_gateway_ip}@@".strip() + } + payload["spec"]["resources"]["ip_config"].update( + { + "prefix_length": int("@@{subnet_ip}@@".strip().split("/")[1]), + "subnet_ip": "@@{subnet_ip}@@".strip().split("/")[0], + } + ) + pool_list_payload("@@{pool_list}@@") + dhcp_payload() + payload["spec"]["resources"].update( + { + "virtual_network_reference": { + "kind": "virtual_network", + "uuid": "@@{vpc_uuid}@@".strip().split(":")[1], + } + } + ) + payload["metadata"] = {"kind": "subnet"} + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create_overlay_subnet(): + create_payload() + print(f"payload = {payload}") + create = session.post( + "https://{}:{}/api/nutanix/v3/subnets".format(PC_IP, PC_PORT), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"response = {create.json()}") + create.raise_for_status() + task_uuid = create.json()["status"]["execution_context"]["task_uuid"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +create_overlay_subnet() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateOverlaySubnet_variable_vpc_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateOverlaySubnet_variable_vpc_uuid_Task_SampleTask.py new file mode 100644 index 000000000..59956c29d --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_CreateOverlaySubnet_variable_vpc_uuid_Task_SampleTask.py @@ -0,0 +1,22 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(url, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get vpc uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/vpcs/list" +payload = {"kind": "vpc", "offset": 0} +r = apiCall(api) + +tmp = [] +for cname in r["entities"]: + vpcname = cname["status"]["name"] + vpcuuid = cname["metadata"]["uuid"] + tmp.append(vpcname + ":" + vpcuuid) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Delete_Task_DeleteSubnet.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Delete_Task_DeleteSubnet.py new file mode 100644 index 000000000..0b2480223 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Delete_Task_DeleteSubnet.py @@ -0,0 +1,49 @@ +import requests + +pc_user = "@@{account.username}@@" +pc_passwd = "@@{account.password}@@" +subnetuuid = "@@{subnet_list}@@".split(":")[1] + +headers = { + "accept": "application/json", + "content-type": "application/json", +} + +payload = {} +r = requests.delete( + "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/subnets/{}".format( + subnetuuid + ), + headers=headers, + json=payload, + verify=False, + auth=(pc_user, pc_passwd), +) +r = json.loads(r.content) +print("response:", r) +state = r["status"]["state"] +print("-----------------------------------------------") +taskuuid = r["status"]["execution_context"]["task_uuid"] +print(f"task_uuid = {taskuuid}") +print("Payload submitted and status is {} and task_uuid is {}".format(state, taskuuid)) + +c = 0 +wait_for_exec = True +if wait_for_exec: + api = "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/tasks/{}".format( + taskuuid + ) + # api = 'https://10.115.150.164:9440/api/nutanix/v3/tasks/{}'.format(taskuuid) + r1 = requests.get(api, auth=(pc_user, pc_passwd), verify=False) + state = r1.json()["status"] + while state != "SUCCEEDED": + c += 1 + sleep(10) + print("Waiting For Task To Finish, state:", state) + r = requests.get(api, auth=(pc_user, pc_passwd), verify=False) + state = r.json()["status"] + if c >= 18: + print("Timed out after 3min, Check task status on UI") + exit(1) + + print(f"task_status = {state}") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Delete_variable_subnet_list_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Delete_variable_subnet_list_Task_SampleTask.py new file mode 100644 index 000000000..f14d61942 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Delete_variable_subnet_list_Task_SampleTask.py @@ -0,0 +1,20 @@ +import requests + +pc_user = "@@{username}@@" +pc_pass = "@@{password}@@" +# pc_user = 'admin' +# pc_pass = 'Nutanix.123' +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/subnets/list" +payload = {"kind": "subnet", "offset": 0} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r = json.loads(r.content) + +# for sname in r['entities']: +# print(sname['status']['name']) + +slist = [ + sname["status"]["name"] + ":" + sname["metadata"]["uuid"] for sname in r["entities"] +] + +print(",".join(slist)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Get_Task_GetSubnet.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Get_Task_GetSubnet.py new file mode 100644 index 000000000..f4d652950 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_Get_Task_GetSubnet.py @@ -0,0 +1,29 @@ +import requests + +pc_user = "@@{account.username}@@" +pc_pass = "@@{account.password}@@" + +api = ( + "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/subnets/list" +) +payload = { + "kind": "subnet", + "filter": "name==@@{subnet_name}@@", + "offset": 0, + "length": 50, +} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r.raise_for_status() +r = json.loads(r.content) +print(json.dumps(r, indent=4)) + +tmp = [] +for sname in r["entities"]: + tmp1 = sname["status"]["name"] + tmp1 = tmp1 + ":" + sname["status"]["resources"]["subnet_type"] + if "vlan_id" in sname["status"]["resources"]: + tmp1 = tmp1 + ":" + str(sname["status"]["resources"]["vlan_id"]) + tmp.append(tmp1) + +print("subnets=" + ",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_List_Task_SubnetList.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_List_Task_SubnetList.py new file mode 100644 index 000000000..ade602976 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_List_Task_SubnetList.py @@ -0,0 +1,28 @@ +import requests + +pc_user = "@@{account.username}@@" +pc_pass = "@@{account.password}@@" +api = ( + "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/subnets/list" +) +payload = { + "kind": "subnet", + "offset": @@{offset}@@, + "sort_attribute": "@@{sort_attribute}@@", + "sort_order": "@@{sort_order}@@", + "length": @@{length}@@ +} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r.raise_for_status() +r = json.loads(r.content) +tmp = [] +for sname in r["entities"]: + tmp1 = sname["status"]["name"] + tmp1 = tmp1 + ":" + sname["status"]["resources"]["subnet_type"] + if "vlan_id" in sname["status"]["resources"]: + tmp1 = tmp1 + ":" + str(sname["status"]["resources"]["vlan_id"]) + tmp1 = tmp1 + ":" + sname["metadata"]["uuid"] + tmp.append(tmp1) + +print("subnet_list=" + ",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_Task_UpdateExternalSubnet.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_Task_UpdateExternalSubnet.py new file mode 100644 index 000000000..033a322a2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_Task_UpdateExternalSubnet.py @@ -0,0 +1,205 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_data(url, name, payload): + response = session.post( + url, + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"get {url} response: {response.json()}") + response.raise_for_status() + + r = json.loads(response.content) + for sname in r["entities"]: + if sname["status"]["name"] == name: + uuid = sname["metadata"]["uuid"] + spec_v = sname["metadata"]["spec_version"] + return uuid, int(spec_v) + + raise Exception(f"failed to get extId for {name}") + + +def get_other_details(name): + payload = {"kind": "subnet", "length": 50, "offset": 0} + response = session.post( + "https://{}:{}/api/nutanix/v3/subnets/list".format(PC_IP, PC_PORT), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"get immutable_data response: {response.json()}") + response.raise_for_status() + + r = json.loads(response.content) + for sname in r["entities"]: + if sname["status"]["name"] == name: + vlan_id = sname["status"]["resources"]["vlan_id"] + external_connectivity_state = sname["status"]["resources"][ + "external_connectivity_state" + ] + enable_nat = sname["status"]["resources"]["enable_nat"] + subnet_ip = sname["status"]["resources"]["ip_config"]["subnet_ip"] + prefix_length = sname["status"]["resources"]["ip_config"]["prefix_length"] + default_gateway_ip = sname["status"]["resources"]["ip_config"][ + "default_gateway_ip" + ] + return ( + vlan_id, + subnet_ip, + prefix_length, + enable_nat, + external_connectivity_state, + default_gateway_ip, + ) + + raise Exception(f"failed to get extId for {name}") + + +def get_uuid(name): + return get_data( + "https://{}:{}/api/nutanix/v3/subnets/list".format(PC_IP, PC_PORT), + name, + {"kind": "subnet", "length": 50, "offset": 0}, + ) + + +def get_ip_pool(): + subnetnm = "@@{subnet_name}@@".strip().split(":")[0] + api = "https://{}:{}/api/nutanix/v3/subnets/list".format(PC_IP, PC_PORT) + payload = { + "kind": "subnet", + "filter": f"name=={subnetnm}", + "offset": 0, + "length": 50, + } + r = session.post(api, json=payload) + r.raise_for_status() + r = json.loads(r.content) + tmp_pool = [] + if "ip_config" in r["entities"][0]["spec"]["resources"]: + if "pool_list" in r["entities"][0]["spec"]["resources"]["ip_config"]: + for pools in r["entities"][0]["spec"]["resources"]["ip_config"][ + "pool_list" + ]: + tmp_pool.append(pools) + print("Existing pool_list:", tmp_pool) + return tmp_pool + + +def pool_list_payload(plist): + plist = plist.strip().split(",") + # get the existing ip pool list + ip_pool_list = get_ip_pool() + if len(plist[0]) != 0: + for p in plist: + p = p.replace('"', "") + val = {"range": p} + ip_pool_list.append(val) + payload["spec"]["resources"]["ip_config"].update({"pool_list": ip_pool_list}) + + +def create_payload(spec_version): + payload["spec"] = {"name": "@@{subnet_name}@@".strip().split(":")[0]} + payload["spec"]["cluster_reference"] = { + "kind": "cluster", + "uuid": "@@{pe_cluster_uuid}@@".strip().split(":")[1], + } + if "@@{description}@@" != "": + payload["spec"].update({"description": "@@{description}@@"}) + + payload["spec"]["resources"] = {"subnet_type": "VLAN", "is_external": True} + + # hard code immutable values + ( + vlan_id, + subnet_ip, + prefix_length, + enable_nat, + external_connectivity_state, + default_gateway_ip, + ) = get_other_details("@@{subnet_name}@@".strip().split(":")[0]) + payload["spec"]["resources"].update( + { + "enable_nat": enable_nat, + "external_connectivity_state": external_connectivity_state, + "vlan_id": vlan_id, + } + ) + payload["spec"]["resources"]["ip_config"] = { + "subnet_ip": subnet_ip, + "prefix_length": prefix_length, + } + + if "@@{default_gateway_ip}@@".strip() != "": + payload["spec"]["resources"]["ip_config"].update( + {"default_gateway_ip": "@@{default_gateway_ip}@@".strip()} + ) + else: + payload["spec"]["resources"]["ip_config"].update( + {"default_gateway_ip": default_gateway_ip} + ) + + if "@@{virtual_switch_uuid}@@".strip() != "": + payload["spec"]["resources"].update( + {"virtual_switch_uuid": "@@{virtual_switch_uuid}@@".strip().split(":")[1]} + ) + + pool_list_payload("@@{pool_list}@@") + + payload["metadata"] = {"kind": "subnet", "spec_version": spec_version} + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def update_subnet(): + UUID, spec_version = get_uuid("@@{subnet_name}@@".strip().split(":")[0]) + create_payload(spec_version) + print(f"payload = {payload}") + update = session.put( + "https://{}:{}/api/nutanix/v3/subnets/{}".format(PC_IP, PC_PORT, UUID), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"response = {update.json()}") + update.raise_for_status() + task_uuid = update.json()["status"]["execution_context"]["task_uuid"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +update_subnet() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py new file mode 100644 index 000000000..919b64de4 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py @@ -0,0 +1,31 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(url, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get pe cluster uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/clusters/list" +payload = {"kind": "cluster"} +r = apiCall(api) + +tmp = [] +pe_cluster_uuid = [ + cname["metadata"]["uuid"] + for cname in r["entities"] + if cname["spec"]["name"] != "Unnamed" +] +for cname in r["entities"]: + if cname["spec"]["name"] == "Unnamed": + continue + clustername = cname["spec"]["name"] + clusteruuid = cname["metadata"]["uuid"] + tmp.append(clustername + ":" + clusteruuid) + +# print("pe_cluster_uuid={}".format(pe_cluster_uuid[0])) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_subnet_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_subnet_name_Task_SampleTask.py new file mode 100644 index 000000000..548a7b278 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_subnet_name_Task_SampleTask.py @@ -0,0 +1,26 @@ +import requests + +pc_user = "@@{username}@@" +pc_pass = "@@{password}@@" + +cluster_uuid = "@@{pe_cluster_uuid}@@".split(":")[1] +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/subnets/list" +payload = { + "kind": "subnet", + "filter": "is_external==true;cluster_uuid==%s" % cluster_uuid, + "offset": 0, +} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r = json.loads(r.content) + +# for sname in r['entities']: +# print(sname['status']['name']) + +slist = [ + sname["status"]["name"] + ":" + str(sname["status"]["resources"]["vlan_id"]) + for sname in r["entities"] + if "is_external" in sname["status"]["resources"] +] + +print(",".join(slist)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py new file mode 100644 index 000000000..66c123aa2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateExternalIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py @@ -0,0 +1,24 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.get(url, headers=headers, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get virtual switch uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/networking/v4.0.b1/config/virtual-switches" +headers = { + "x-cluster-id": "@@{pe_cluster_uuid}@@".strip().split(":")[1], + "accept": "application/json", +} + +r = apiCall(api) + +name = r["data"][0]["name"] +uuid = r["data"][0]["extId"] +# print("virtual_switch={}".format(name+':'+uuid)) +print(name + ":" + uuid) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_Task_UpdateNutanixIPAMSubnet.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_Task_UpdateNutanixIPAMSubnet.py new file mode 100644 index 000000000..61ce3f60c --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_Task_UpdateNutanixIPAMSubnet.py @@ -0,0 +1,204 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_data(url, name, payload): + response = session.post( + url, + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"get {url} response: {response.json()}") + response.raise_for_status() + + r = json.loads(response.content) + for sname in r["entities"]: + if sname["status"]["name"] == name: + uuid = sname["metadata"]["uuid"] + spec_v = sname["metadata"]["spec_version"] + return uuid, int(spec_v) + + raise Exception(f"failed to get extId for {name}") + + +def get_uuid(name): + return get_data( + "https://{}:{}/api/nutanix/v3/subnets/list".format(PC_IP, PC_PORT), + name, + {"kind": "subnet", "length": 50, "offset": 0}, + ) + + +def get_ip_pool(): + subnetnm = "@@{subnet_name}@@".strip().split(":")[0] + api = "https://{}:{}/api/nutanix/v3/subnets/list".format(PC_IP, PC_PORT) + payload = { + "kind": "subnet", + "filter": f"name=={subnetnm}", + "offset": 0, + "length": 50, + } + r = session.post(api, json=payload) + r.raise_for_status() + r = json.loads(r.content) + tmp_pool = [] + if "ip_config" in r["entities"][0]["spec"]["resources"]: + if "pool_list" in r["entities"][0]["spec"]["resources"]["ip_config"]: + for pools in r["entities"][0]["spec"]["resources"]["ip_config"][ + "pool_list" + ]: + tmp_pool.append(pools) + print("Existing pool_list:", tmp_pool) + return tmp_pool + + +def pool_list_payload(plist): + plist = plist.strip().split(",") + ip_pool_list = get_ip_pool() + if len(plist[0]) != 0: + for p in plist: + p = p.replace('"', "") + val = {"range": p} + ip_pool_list.append(val) + payload["spec"]["resources"]["ip_config"].update({"pool_list": ip_pool_list}) + + +def dhcp_payload(): + if ( + "@@{domain_name_server_list}@@" != "" + or "@@{boot_file_name}@@" != "" + or "@@{domain_name}@@" != "" + or "@@{tftp_server_name}@@" != "" + or "@@{domain_search_list}@@" != "" + ): + payload["spec"]["resources"]["ip_config"]["dhcp_options"] = {} + dnsip_list = "@@{domain_name_server_list}@@".strip().split(",") + dns_ip_list = [] + if len(dnsip_list[0]) != 0: + for p in dnsip_list: + p = p.strip().replace('"', "") + val = p + dns_ip_list.append(val) + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_name_server_list": dns_ip_list} + ) + + if "@@{boot_file_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"boot_file_name": "@@{boot_file_name}@@"} + ) + if "@@{domain_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_name": "@@{domain_name}@@"} + ) + if "@@{tftp_server_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"tftp_server_name": "@@{tftp_server_name}@@"} + ) + + ds_list = "@@{domain_search_list}@@".strip().split(",") + ds_ip_list = [] + if len(ds_list[0]) != 0: + for p in ds_list: + p = p.strip().replace('"', "") + val = p + ds_ip_list.append(val) + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_search_list": ds_ip_list} + ) + + +def create_payload(spec_version): + payload["spec"] = {"name": "@@{subnet_name}@@".strip().split(":")[0]} + payload["spec"]["cluster_reference"] = { + "kind": "cluster", + "uuid": "@@{pe_cluster_uuid}@@".strip().split(":")[1], + } + if "@@{description}@@" != "": + payload["spec"].update({"description": "@@{description}@@"}) + + payload["spec"]["resources"] = {"subnet_type": "VLAN"} + + if "@@{vlan_id}@@".strip() != "": + payload["spec"]["resources"].update({"vlan_id": int("@@{vlan_id}@@".strip())}) + payload["spec"]["resources"].update( + {"virtual_switch_uuid": "@@{virtual_switch_uuid}@@".strip().split(":")[1]} + ) + + if "@@{default_gateway_ip}@@".strip() != "": + payload["spec"]["resources"]["ip_config"].update( + {"default_gateway_ip": "@@{default_gateway_ip}@@".strip()} + ) + + if "@@{subnet_ip}@@".strip() != "": + payload["spec"]["resources"]["ip_config"].update( + { + "prefix_length": int("@@{subnet_ip}@@".strip().split("/")[1]), + "subnet_ip": "@@{subnet_ip}@@".strip().split("/")[0], + } + ) + + if "@@{dhcp_server_address}@@" != "": + payload["spec"]["resources"]["ip_config"].update( + {"dhcp_server_address": {"ip": "@@{dhcp_server_address}@@"}} + ) + + pool_list_payload("@@{pool_list}@@") + dhcp_payload() + + payload["metadata"] = {"kind": "subnet", "spec_version": spec_version} + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def update_subnet(): + UUID, spec_version = get_uuid("@@{subnet_name}@@".strip().split(":")[0]) + create_payload(spec_version) + print(f"payload = {payload}") + update = session.put( + "https://{}:{}/api/nutanix/v3/subnets/{}".format(PC_IP, PC_PORT, UUID), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"response = {update.json()}") + update.raise_for_status() + task_uuid = update.json()["status"]["execution_context"]["task_uuid"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +update_subnet() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py new file mode 100644 index 000000000..919b64de4 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_pe_cluster_uuid_Task_SampleTask.py @@ -0,0 +1,31 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(url, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get pe cluster uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/clusters/list" +payload = {"kind": "cluster"} +r = apiCall(api) + +tmp = [] +pe_cluster_uuid = [ + cname["metadata"]["uuid"] + for cname in r["entities"] + if cname["spec"]["name"] != "Unnamed" +] +for cname in r["entities"]: + if cname["spec"]["name"] == "Unnamed": + continue + clustername = cname["spec"]["name"] + clusteruuid = cname["metadata"]["uuid"] + tmp.append(clustername + ":" + clusteruuid) + +# print("pe_cluster_uuid={}".format(pe_cluster_uuid[0])) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_subnet_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_subnet_name_Task_SampleTask.py new file mode 100644 index 000000000..ce07783a7 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_subnet_name_Task_SampleTask.py @@ -0,0 +1,22 @@ +import requests + +pc_user = "@@{username}@@" +pc_pass = "@@{password}@@" + +cluster_uuid = "@@{pe_cluster_uuid}@@".split(":")[1] +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/subnets/list" +payload = {"kind": "subnet", "filter": "cluster_uuid==%s" % cluster_uuid, "offset": 0} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r = json.loads(r.content) + +# for sname in r['entities']: +# print(sname['status']['name']) + +slist = [ + sname["status"]["name"] + ":" + str(sname["status"]["resources"]["vlan_id"]) + for sname in r["entities"] + if "is_external" not in sname["status"]["resources"] +] + +print(",".join(slist)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py new file mode 100644 index 000000000..66c123aa2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateNutanixIPAMSubnet_variable_virtual_switch_uuid_Task_SampleTask.py @@ -0,0 +1,24 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.get(url, headers=headers, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get virtual switch uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/networking/v4.0.b1/config/virtual-switches" +headers = { + "x-cluster-id": "@@{pe_cluster_uuid}@@".strip().split(":")[1], + "accept": "application/json", +} + +r = apiCall(api) + +name = r["data"][0]["name"] +uuid = r["data"][0]["extId"] +# print("virtual_switch={}".format(name+':'+uuid)) +print(name + ":" + uuid) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_Task_update_overlay_subnet.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_Task_update_overlay_subnet.py new file mode 100644 index 000000000..c07ccd2f9 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_Task_update_overlay_subnet.py @@ -0,0 +1,236 @@ +# update_normal_subnet +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_data(url, name, payload): + response = session.post( + url, + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"get {url} response: {response.json()}") + response.raise_for_status() + + r = json.loads(response.content) + for sname in r["entities"]: + if sname["status"]["name"] == name: + uuid = sname["metadata"]["uuid"] + spec_v = sname["metadata"]["spec_version"] + return uuid, int(spec_v) + + raise Exception(f"failed to get extId for {name}") + + +def get_other_details(name): + payload = {"kind": "subnet", "length": 50, "offset": 0} + response = session.post( + "https://{}:{}/api/nutanix/v3/subnets/list".format(PC_IP, PC_PORT), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"get immutable_data response: {response.json()}") + response.raise_for_status() + + r = json.loads(response.content) + for sname in r["entities"]: + if sname["status"]["name"] == name: + virtual_network_reference_uuid = sname["status"]["resources"][ + "virtual_network_reference" + ]["uuid"] + external_connectivity_state = sname["status"]["resources"][ + "external_connectivity_state" + ] + vpc_reference_uuid = sname["status"]["resources"]["vpc_reference"]["uuid"] + subnet_ip = sname["status"]["resources"]["ip_config"]["subnet_ip"] + prefix_length = sname["status"]["resources"]["ip_config"]["prefix_length"] + default_gateway_ip = sname["status"]["resources"]["ip_config"][ + "default_gateway_ip" + ] + return ( + virtual_network_reference_uuid, + subnet_ip, + prefix_length, + vpc_reference_uuid, + external_connectivity_state, + default_gateway_ip, + ) + + raise Exception(f"failed to get extId for {name}") + + +def get_uuid(name): + return get_data( + "https://{}:{}/api/nutanix/v3/subnets/list".format(PC_IP, PC_PORT), + name, + {"kind": "subnet", "length": 50, "offset": 0}, + ) + + +def get_ip_pool(): + subnetnm = "@@{subnet_name}@@".strip().split(":")[0] + api = "https://{}:{}/api/nutanix/v3/subnets/list".format(PC_IP, PC_PORT) + payload = { + "kind": "subnet", + "filter": f"name=={subnetnm}", + "offset": 0, + "length": 50, + } + r = session.post(api, json=payload) + r.raise_for_status() + r = json.loads(r.content) + tmp_pool = [] + if "ip_config" in r["entities"][0]["spec"]["resources"]: + if "pool_list" in r["entities"][0]["spec"]["resources"]["ip_config"]: + for pools in r["entities"][0]["spec"]["resources"]["ip_config"][ + "pool_list" + ]: + tmp_pool.append(pools) + print("Existing pool_list:", tmp_pool) + return tmp_pool + + +def pool_list_payload(plist): + plist = plist.strip().split(",") + ip_pool_list = get_ip_pool() + if len(plist[0]) != 0: + for p in plist: + p = p.replace('"', "") + val = {"range": p} + ip_pool_list.append(val) + payload["spec"]["resources"]["ip_config"].update({"pool_list": ip_pool_list}) + + +def dhcp_payload(): + if ( + "@@{domain_name_server_list}@@" != "" + or "@@{boot_file_name}@@" != "" + or "@@{domain_name}@@" != "" + or "@@{tftp_server_name}@@" != "" + or "@@{domain_search_list}@@" != "" + ): + payload["spec"]["resources"]["ip_config"]["dhcp_options"] = {} + dnsip_list = "@@{domain_name_server_list}@@".strip().split(",") + dns_ip_list = [] + if len(dnsip_list[0]) != 0: + for p in dnsip_list: + p = p.strip().replace('"', "") + val = p + dns_ip_list.append(val) + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_name_server_list": dns_ip_list} + ) + if "@@{boot_file_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"boot_file_name": "@@{boot_file_name}@@"} + ) + if "@@{domain_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_name": "@@{domain_name}@@"} + ) + if "@@{tftp_server_name}@@" != "": + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"tftp_server_name": "@@{tftp_server_name}@@"} + ) + + ds_list = "@@{domain_search_list}@@".strip().split(",") + ds_ip_list = [] + if len(ds_list[0]) != 0: + for p in ds_list: + p = p.strip().replace('"', "") + val = p + ds_ip_list.append(val) + payload["spec"]["resources"]["ip_config"]["dhcp_options"].update( + {"domain_search_list": ds_ip_list} + ) + + +def create_payload(spec_version): + payload["spec"] = {"name": "@@{subnet_name}@@".strip().split(":")[0]} + # payload['spec']['cluster_reference'] = { "kind": "cluster","uuid":'@@{pe_cluster_uuid}@@'.strip().split(':')[1] } + if "@@{description}@@" != "": + payload["spec"].update({"description": "@@{description}@@"}) + # hard code immutable values + payload["spec"]["resources"] = {"subnet_type": "OVERLAY"} + ( + virtual_network_reference_uuid, + subnet_ip, + prefix_length, + vpc_reference_uuid, + external_connectivity_state, + default_gateway_ip, + ) = get_other_details("@@{subnet_name}@@".strip().split(":")[0]) + payload["spec"]["resources"].update( + {"vpc_reference": {"kind": "vpc", "uuid": vpc_reference_uuid}} + ) + payload["spec"]["resources"].update( + { + "virtual_network_reference": { + "kind": "virtual_network", + "uuid": virtual_network_reference_uuid, + } + } + ) + payload["spec"]["resources"]["ip_config"] = { + "default_gateway_ip": default_gateway_ip, + "subnet_ip": subnet_ip, + "prefix_length": prefix_length, + } + pool_list_payload("@@{pool_list}@@") + dhcp_payload() + payload["metadata"] = {"kind": "subnet", "spec_version": spec_version} + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def update_subnet(): + UUID, spec_version = get_uuid("@@{subnet_name}@@".strip().split(":")[0]) + create_payload(spec_version) + print(f"payload = {payload}") + update = session.put( + "https://{}:{}/api/nutanix/v3/subnets/{}".format(PC_IP, PC_PORT, UUID), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"response = {update.json()}") + update.raise_for_status() + task_uuid = update.json()["status"]["execution_context"]["task_uuid"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +update_subnet() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_variable_pe_cluster_uuid_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_variable_pe_cluster_uuid_Task_SampleTask.py new file mode 100644 index 000000000..f702b35dc --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_variable_pe_cluster_uuid_Task_SampleTask.py @@ -0,0 +1,31 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get pe cluster uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/clusters/list" +payload = {"kind": "cluster"} +r = apiCall(api) + +tmp = [] +pe_cluster_uuid = [ + cname["metadata"]["uuid"] + for cname in r["entities"] + if cname["spec"]["name"] != "Unnamed" +] +for cname in r["entities"]: + if cname["spec"]["name"] == "Unnamed": + continue + clustername = cname["spec"]["name"] + clusteruuid = cname["metadata"]["uuid"] + tmp.append(clustername + ":" + clusteruuid) + +# print("pe_cluster_uuid={}".format(pe_cluster_uuid[0])) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_variable_subnet_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_variable_subnet_name_Task_SampleTask.py new file mode 100644 index 000000000..83e848a04 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_Subnet_Action_UpdateOverlaySubnet_variable_subnet_name_Task_SampleTask.py @@ -0,0 +1,22 @@ +import requests + +pc_user = "@@{username}@@" +pc_pass = "@@{password}@@" + +cluster_uuid = "@@{pe_cluster_uuid}@@".split(":")[1] + +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/subnets/list" +payload = {"kind": "subnet", "filter": "subnet_type==OVERLAY", "offset": 0} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r = json.loads(r.content) + +# for sname in r['entities']: +# print(sname['status']['name']) + +slist = [ + sname["status"]["name"] + ":" + str(sname["metadata"]["spec_version"]) + for sname in r["entities"] +] + +print(",".join(slist)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Clone_Task_CloneVM.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Clone_Task_CloneVM.py new file mode 100644 index 000000000..ad02ca8ad --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Clone_Task_CloneVM.py @@ -0,0 +1,108 @@ +import requests +import uuid + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False + +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def update_resources(): + resource_values = { + "numSockets": "@@{num_of_sockets}@@", + "numCoresPerSocket": "@@{num_cores_per_socket}@@", + "numThreadsPerCore": "@@{num_threads_per_core}@@", + "memorySizeBytes": "@@{memory_size_bytes}@@", + } + for name, value in resource_values.items(): + if value.strip(): + payload[name] = int(value) + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def clone(clone_from_ext_id, cloned_vm_name): + payload["name"] = cloned_vm_name + update_resources() + vm_details_response = session.get( + "https://{}:{}/api/vmm/{}/ahv/config/vms/{}".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@", clone_from_ext_id + ), + headers={ + "accept": "application/json", + }, + ) + print(f"vm details response: {vm_details_response.json()}") + vm_details_response.raise_for_status() + clone_vm_response = session.post( + "https://{}:{}/api/vmm/{}/ahv/config/vms/{}/$actions/clone".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@", clone_from_ext_id + ), + headers={ + "accept": "application/json", + "If-Match": vm_details_response.headers["ETag"], + "NTNX-Request-Id": str(uuid.uuid4()), + }, + json=payload, + ) + print(f"vm clone response: {clone_vm_response.json()}") + clone_vm_response.raise_for_status() + task_uuid = clone_vm_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + if "@@{wait}@@" == "Yes": + wait(task_uuid) + + +clone("@@{clone_from_extId}@@", "@@{cloned_vm_name}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Create_Task_CreateVM.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Create_Task_CreateVM.py new file mode 100644 index 000000000..6aac788cd --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Create_Task_CreateVM.py @@ -0,0 +1,228 @@ +import requests +import uuid +import base64 + + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_cluster_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/clustermgmt/{}/config/clusters".format( + PC_IP, PC_PORT, "@@{cluster_mgt_api_version}@@" + ), + name, + ) + + +def get_subnet_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/subnets".format( + PC_IP, PC_PORT, "@@{networking_api_version}@@" + ), + name, + ) + + +def get_image_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/vmm/{}/content/images".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@" + ), + name, + ) + + +def get_storage_container_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/clustermgmt/{}/config/storage-containers".format( + PC_IP, PC_PORT, "@@{cluster_mgt_api_version}@@" + ), + name, + "containerExtId", + ) + + +def add_iso_image(iso_image): + print("creating vm with iso image") + payload["cd_roms"] = [ + { + "backingInfo": { + "dataSource": { + "reference": { + "$objectType": "vmm.v4.ahv.config.ImageReference", + "imageExtId": get_image_ext_id(iso_image), + } + } + } + } + ] + + +def add_disk_image(disk_image): + print("creating vm with disk image") + payload["disks"] = [ + { + "backingInfo": { + "$objectType": "vmm.v4.ahv.config.VmDisk", + "dataSource": { + "reference": { + "$objectType": "vmm.v4.ahv.config.ImageReference", + "imageExtId": get_image_ext_id(disk_image), + } + }, + } + } + ] + + +def add_disk(disk_size_in_bytes, storage_container): + payload["disks"] = [ + { + "backingInfo": { + "$objectType": "vmm.v4.ahv.config.VmDisk", + "diskSizeBytes": int(disk_size_in_bytes), + "storageContainer": { + "extId": get_storage_container_ext_id(storage_container) + }, + } + } + ] + + +def add_nic(subnet): + payload["nics"] = [ + {"networkInfo": {"subnet": {"extId": get_subnet_ext_id(subnet)}}} + ] + + +def add_cloud_init(cloud_init): + payload["guestCustomization"] = { + "config": { + "$objectType": "vmm.v4.ahv.config.CloudInit", + "cloudInitScript": { + "$objectType": "vmm.v4.ahv.config.Userdata", + "value": base64.b64encode(cloud_init.encode("ascii")).decode("ascii"), + }, + } + } + + +def add_sys_prep(sys_prep): + payload["guestCustomization"] = { + "config": { + "$objectType": "vmm.v4.ahv.config.Sysprep", + "sysprepScript": { + "$objectType": "vmm.v4.ahv.config.Unattendxml", + "value": base64.b64encode(sys_prep.encode("ascii")).decode("ascii"), + }, + } + } + + +def create_payload(): + cluster_uuid = get_cluster_ext_id("@@{cluster}@@") + payload.update( + { + "name": "@@{vm_name}@@", + "numSockets": int("@@{num_of_sockets}@@"), + "numCoresPerSocket": int("@@{num_cores_per_socket}@@"), + "numThreadsPerCore": int("@@{num_threads_per_core}@@"), + "memorySizeBytes": int("@@{memory_size_bytes}@@"), + "cluster": {"extId": cluster_uuid}, + } + ) + if "@@{iso_image}@@".strip(): + add_iso_image("@@{iso_image}@@") + if "@@{disk_image}@@".strip(): + add_disk_image("@@{disk_image}@@") + if "@@{disk_size_in_bytes}@@".strip(): + if not "@@{storage_container}@@".strip(): + raise Exception("storage container is required when disk size is provided") + add_disk("@@{disk_size_in_bytes}@@", "@@{storage_container}@@") + if "@@{subnet}@@".strip(): + add_nic("@@{subnet}@@") + if "@@{description}@@".strip(): + payload["description"] = "@@{description}@@" + if """@@{cloud_init}@@""".strip(): + add_cloud_init("""@@{cloud_init}@@""") + if """@@{sys_prep}@@""".strip(): + add_sys_prep("""@@{sys_prep}@@""") + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create(): + create_payload() + print(f"payload = {payload}") + create_vm_response = session.post( + "https://{}:{}/api/vmm/{}/ahv/config/vms".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@" + ), + json=payload, + headers={ + "Content-Type": "application/json", + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"response = {create_vm_response.json()}") + create_vm_response.raise_for_status() + task_uuid = create_vm_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + if "@@{wait}@@" == "Yes": + wait(task_uuid) + + +create() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Delete_Task_DeleteVM.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Delete_Task_DeleteVM.py new file mode 100644 index 000000000..9dd4b2614 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Delete_Task_DeleteVM.py @@ -0,0 +1,90 @@ +raise Exception( + "Not reccommented to use this script with @@{vm_api_version}@@ API version" +) + +# import requests +# import uuid + +# PC_USERNAME = "@@{account.username}@@" +# PC_PASSWORD = "@@{account.password}@@" +# PC_IP = "@@{account.pc_server}@@" +# PC_PORT = "@@{account.pc_port}@@" + +# session = requests.Session() +# session.auth = (PC_USERNAME, PC_PASSWORD) +# session.verify = False + + +# def get_resource_ext_id(url, name, id_key="extId"): +# response = session.get( +# url, +# headers={ +# "accept": "application/json", +# }, +# params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, +# ) +# print(f"get {name} response: {response.json()}") +# response.raise_for_status() +# data = response.json().get("data") +# if data: +# if isinstance(data, list): +# if id_key in data[0] and data[0]["name"] == name: +# return data[0][id_key] +# else: +# if id_key in data: +# return data[id_key] +# raise Exception(f"failed to get extId for {name}") + +# def wait(task_uuid, timeout=1800): +# max_count = timeout / 10 +# count = 0 +# task_status_response = None +# while count <= 18: +# task_status_response = session.get( +# "https://{}:{}/api/prism/{}/config/tasks/{}".format( +# PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid +# ), +# ) +# if task_status_response.status_code != 200: +# raise Exception( +# f"failed to get task, got response {task_status_response.json()}" +# ) +# print(f"task status is {task_status_response.json()['data']['status']}") +# if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: +# count += 1 +# sleep(10) +# else: +# count = 0 +# break +# print(f"task_status = {task_status_response.json()['data']['status']}") +# if count != 0: +# raise Exception("timed out waiting for task to complete") + + +# def delete_vm(vm_ext_id): +# vm_details_response = session.get( +# "https://{}:{}/api/vmm/{}/ahv/config/vms/{}".format( +# PC_IP, PC_PORT, "@@{vm_api_version}@@", vm_ext_id +# ), +# ) +# print(f"vm details response: {vm_details_response.json()}") +# vm_details_response.raise_for_status() +# print(f"deleting {name}") +# vm_delete_response = session.delete( +# "https://{}:{}/api/vmm/{}/ahv/config/vms/{}".format( +# PC_IP, PC_PORT, "@@{vm_api_version}@@", vm_ext_id +# ), +# headers={ +# "accept": "application/json", +# "If-Match": vm_details_response.headers["ETag"], +# "NTNX-Request-Id": str(uuid.uuid4()), +# }, +# ) +# print(f"delete vm response: {vm_delete_response.json()}") +# vm_details_response.raise_for_status() +# task_uuid = vm_delete_response.json()["data"]["extId"] +# if "@@{wait}@@" == "Yes": +# wait(task_uuid) + + +# delete_vm("@@{vm_extId}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Get_Task_GetVM.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Get_Task_GetVM.py new file mode 100644 index 000000000..aa077e060 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Get_Task_GetVM.py @@ -0,0 +1,26 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def get(vm_ext_id): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/vmm/{}/ahv/config/vms/{}".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@", vm_ext_id + ), + auth=(PC_USERNAME, PC_PASSWORD), + headers=headers, + verify=False, + ) + print(f"response = {response.json()}") + response.raise_for_status() + print("vm = " + json.dumps(json.dumps(response.json()["data"]))) + + +get("@@{vm_extId}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_List_Task_ListVMs.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_List_Task_ListVMs.py new file mode 100644 index 000000000..4c66d35b3 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_List_Task_ListVMs.py @@ -0,0 +1,38 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def list_vm(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/vmm/{}/ahv/config/vms".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + print(f"response: {response.json()}") + response.raise_for_status() + if not response.json().get("data"): + print(response.json()) + raise Exception("unable to list vms, please check the parameters") + print("vms = " + json.dumps(json.dumps(response.json()["data"]))) + + +params = { + "$page": @@{page}@@, + "$limit": @@{limit}@@, + "$select": "@@{select}@@", + "$orderby": "@@{orderby}@@", + "$filter": "@@{filter}@@", +} + +list_vm(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_PerformOperation_Task_PerformVMoperation.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_PerformOperation_Task_PerformVMoperation.py new file mode 100644 index 000000000..1d1c331a1 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_PerformOperation_Task_PerformVMoperation.py @@ -0,0 +1,89 @@ +import requests +import uuid + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def peform_action(vm_ext_id, action): + vm_details_response = session.get( + "https://{}:{}/api/vmm/{}/ahv/config/vms/{}".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@", vm_ext_id + ), + verify=False, + ) + print(f"vm details response: {vm_details_response.json()}") + vm_details_response.raise_for_status() + print(f"performing {action} on {vm_ext_id}".format(action)) + vm_action_response = session.post( + "https://{}:{}/api/vmm/{}/ahv/config/vms/{}/$actions/{}".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@", vm_ext_id, action + ), + headers={ + "accept": "application/json", + "If-Match": vm_details_response.headers["ETag"], + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"vm action response: {vm_action_response.json()}") + vm_action_response.raise_for_status() + task_uuid = vm_action_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + if "@@{wait}@@" == "Yes": + wait(task_uuid) + + +peform_action("@@{vm_extId}@@", "@@{action}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Update_Task_UpdateVM.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Update_Task_UpdateVM.py new file mode 100644 index 000000000..6037e2ce0 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VM_Action_Update_Task_UpdateVM.py @@ -0,0 +1,236 @@ +import requests +import uuid + + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_cluster_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/clustermgmt/{}/config/clusters".format( + PC_IP, PC_PORT, "@@{cluster_mgt_api_version}@@" + ), + name, + ) + + +def get_subnet_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/subnets".format( + PC_IP, PC_PORT, "@@{networking_api_version}@@" + ), + name, + ) + + +def get_image_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/vmm/{}/content/images".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@" + ), + name, + ) + + +def get_storage_container_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/clustermgmt/{}/config/storage-containers".format( + PC_IP, PC_PORT, "@@{cluster_mgt_api_version}@@" + ), + name, + "containerExtId", + ) + + +def add_iso_image(iso_image): + print("creating vm with iso image") + payload["cd_roms"] = [ + { + "backingInfo": { + "dataSource": { + "reference": { + "$objectType": "vmm.v4.ahv.config.ImageReference", + "imageExtId": get_image_ext_id(iso_image), + } + } + } + } + ] + + +def add_disk_image(disk_image): + print("creating vm with disk image") + payload["disks"] = [ + { + "backingInfo": { + "$objectType": "vmm.v4.ahv.config.VmDisk", + "dataSource": { + "reference": { + "$objectType": "vmm.v4.ahv.config.ImageReference", + "imageExtId": get_image_ext_id(disk_image), + } + }, + } + } + ] + + +def add_disk(disk_size_in_bytes, storage_container): + payload["disks"] = [ + { + "backingInfo": { + "$objectType": "vmm.v4.ahv.config.VmDisk", + "diskSizeBytes": int(disk_size_in_bytes), + "storageContainer": { + "extId": get_storage_container_ext_id(storage_container) + }, + } + } + ] + + +def add_nic(subnet): + payload["nics"] = [ + {"networkInfo": {"subnet": {"extId": get_subnet_ext_id(subnet)}}} + ] + + +def add_cloud_init(cloud_init): + payload["guestCustomization"] = { + "config": { + "$objectType": "vmm.v4.r0.b1.ahv.config.CloudInit", + "cloudInitScript": { + "$objectType": "vmm.v4.r0.b1.ahv.config.Userdata", + "value": base64.b64encode(cloud_init.encode("ascii")).decode("ascii"), + }, + } + } + + +def add_sys_prep(sys_prep): + payload["guestCustomization"] = { + "config": { + "$objectType": "vmm.v4.ahv.config.Sysprep", + "sysprepScript": { + "$objectType": "vmm.v4.ahv.config.Unattendxml", + "value": base64.b64encode(sys_prep.encode("ascii")).decode("ascii"), + }, + } + } + + +def create_payload(): + if "@@{cluster}@@": + cluster_uuid = get_cluster_ext_id("@@{cluster}@@") + payload["cluster"] = {"extId": cluster_uuid} + resource_values = { + "numSockets": "@@{num_of_sockets}@@", + "numCoresPerSocket": "@@{num_cores_per_socket}@@", + "numThreadsPerCore": "@@{num_threads_per_core}@@", + "memorySizeBytes": "@@{memory_size_bytes}@@", + } + for name, value in resource_values.items(): + if value.strip(): + payload[name] = int(value) + if "@@{iso_image}@@".strip(): + add_iso_image("@@{iso_image}@@") + if "@@{disk_image}@@".strip(): + add_disk_image("@@{disk_image}@@") + if "@@{disk_size_in_bytes}@@".strip(): + if not "@@{storage_container}@@".strip(): + raise Exception("storage container is required when disk size is provided") + add_disk("@@{disk_size_in_bytes}@@", "@@{storage_container}@@") + if "@@{subnet}@@".strip(): + add_nic("@@{subnet}@@") + if "@@{description}@@".strip(): + payload["description"] = "@@{description}@@" + if """@@{cloud_init}@@""".strip(): + add_cloud_init("""@@{cloud_init}@@""") + if """@@{sys_prep}@@""".strip(): + add_sys_prep("""@@{sys_prep}@@""") + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def update(vm_ext_id): + vm_details_response = session.get( + "https://{}:{}/api/vmm/{}/ahv/config/vms/{}".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@", vm_ext_id + ), + ) + print(f"vm details response: {vm_details_response.json()}") + vm_details_response.raise_for_status() + create_payload() + print(f"payload = {payload}") + update_vm_response = session.put( + "https://{}:{}/api/vmm/{}/ahv/config/vms/{}".format( + PC_IP, PC_PORT, "@@{vm_api_version}@@", vm_ext_id + ), + json=payload, + headers={ + "Content-Type": "application/json", + "If-Match": vm_details_response.headers["ETag"], + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"response = {update_vm_response.json()}") + update_vm_response.raise_for_status() + task_uuid = update_vm_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + if "@@{wait}@@" == "Yes": + wait(task_uuid) + + +update("@@{vm_extId}@@") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Create_Task_CreateVPC.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Create_Task_CreateVPC.py new file mode 100644 index 000000000..dee73cb76 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Create_Task_CreateVPC.py @@ -0,0 +1,100 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def create_payload(): + vpc_name = "@@{vpc_name}@@".strip() + vpc_type = "REGULAR" + external_subnet_reference = "@@{external_subnet_reference}@@".strip().split(":")[1] + + dnsip_list = "@@{common_domain_name_server_ip_list}@@".strip().split(",") + dns_ip_list = [] + if len(dnsip_list[0]) != 0: + for p in dnsip_list: + p = p.strip().replace('"', "") + val = {"ip": p} + dns_ip_list.append(val) + + extrouteip_list = "@@{externally_routable_prefix_list}@@".strip().split(",") + ext_route_prefix_list = [] + if len(extrouteip_list[0]) != 0: + for p in extrouteip_list: + p = p.strip().replace('"', "") + p_ip = p.split("/")[0] + p_prefix = p.split("/")[1] + val = {"ip": p_ip, "prefix_length": int(p_prefix)} + ext_route_prefix_list.append(val) + + payload["spec"] = {"name": vpc_name} + payload["spec"]["resources"] = {"vpc_type": vpc_type} + payload["spec"]["resources"]["external_subnet_list"] = [ + { + "external_subnet_reference": { + "kind": "subnet", + "uuid": external_subnet_reference, + } + } + ] + if dns_ip_list: + payload["spec"]["resources"]["common_domain_name_server_ip_list"] = dns_ip_list + if ext_route_prefix_list: + payload["spec"]["resources"][ + "externally_routable_prefix_list" + ] = ext_route_prefix_list + if "@@{Description}@@".strip() != "": + payload["spec"]["description"] = "@@{Description}@@".strip() + payload["metadata"] = {"kind": "vpc"} + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create_vpc(): + create_payload() + print(f"payload = {payload}") + create_vpc = session.post( + "https://{}:{}/api/nutanix/v3/vpcs".format(PC_IP, PC_PORT), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"response = {create_vpc.json()}") + create_vpc.raise_for_status() + task_uuid = create_vpc.json()["status"]["execution_context"]["task_uuid"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +create_vpc() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Create_variable_external_subnet_reference_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Create_variable_external_subnet_reference_Task_SampleTask.py new file mode 100644 index 000000000..dcfff1bd4 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Create_variable_external_subnet_reference_Task_SampleTask.py @@ -0,0 +1,15 @@ +import requests + +pc_user = "@@{username}@@" +pc_pass = "@@{password}@@" + +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/subnets/list" +payload = {"kind": "subnet", "filter": "is_external==true"} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r = json.loads(r.content) + +slist = [ + sname["status"]["name"] + ":" + sname["metadata"]["uuid"] for sname in r["entities"] +] +print(",".join(slist)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_Task_DeleteSubnet.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_Task_DeleteSubnet.py new file mode 100644 index 000000000..09351430e --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_Task_DeleteSubnet.py @@ -0,0 +1,57 @@ +import requests + +pc_user = "@@{account.username}@@" +pc_passwd = "@@{account.password}@@" +subnets = "@@{subnet_name}@@".split("|") +if len(subnets) == 1 and subnets[0] == "": + print("No subnet assiciated with this VPC, skipping subnet deletion.") + exit(0) +subnetuuids = [sname.split(":")[2] for sname in subnets] + +headers = { + "accept": "application/json", + "content-type": "application/json", +} + +payload = {} + +for suuid in subnetuuids: + r = requests.delete( + "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/subnets/{}".format( + suuid + ), + headers=headers, + json=payload, + verify=False, + auth=(pc_user, pc_passwd), + ) + r = json.loads(r.content) + print("response:", r) + state = r["status"]["state"] + print("-----------------------------------------------") + taskuuid = r["status"]["execution_context"]["task_uuid"] + print(f"subnet_task_uuid = {taskuuid}") + print( + "Payload submitted and status is {} and task_uuid is {}".format(state, taskuuid) + ) + + c = 0 + + api = "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/tasks/{}".format( + taskuuid + ) + r1 = requests.get(api, auth=(pc_user, pc_passwd), verify=False) + state = r1.json()["status"] + while state != "SUCCEEDED" or state != "SUCCESS": + c += 1 + sleep(10) + print("Waiting For Task To Finish, state:", state) + r = requests.get(api, auth=(pc_user, pc_passwd), verify=False) + state = r.json()["status"] + if state == "SUCCEEDED": + break + if c >= 18: + print("Timed out after 3min, Check task status on UI") + exit(1) + + print(f"subnet_task_status = {state}") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_Task_DeleteVPC.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_Task_DeleteVPC.py new file mode 100644 index 000000000..fc13598bf --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_Task_DeleteVPC.py @@ -0,0 +1,55 @@ +import requests + +pc_user = "@@{account.username}@@" +pc_passwd = "@@{account.password}@@" +vpclist = "@@{vpc_name}@@".split(",") +vpcuuidlist = [vpc.split(":")[1] for vpc in vpclist] + +headers = { + "accept": "application/json", + "content-type": "application/json", +} + +payload = {} + +for vpcuuid in vpcuuidlist: + r = requests.delete( + "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/vpcs/{}".format( + vpcuuid + ), + headers=headers, + json=payload, + verify=False, + auth=(pc_user, pc_passwd), + ) + r = json.loads(r.content) + print("response:", r) + state = r["status"]["state"] + print("-----------------------------------------------") + taskuuid = r["status"]["execution_context"]["task_uuid"] + + print(f"vpc_task_uuid = {taskuuid}") + print( + "Payload submitted and status is {} and task_uuid is {}".format(state, taskuuid) + ) + + c = 0 + + api = "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/tasks/{}".format( + taskuuid + ) + r1 = requests.get(api, auth=(pc_user, pc_passwd), verify=False) + state = r1.json()["status"] + while state != "SUCCEEDED" or state != "SUCCESS": + c += 1 + sleep(10) + print("Waiting For Task To Finish, state:", state) + r = requests.get(api, auth=(pc_user, pc_passwd), verify=False) + state = r.json()["status"] + if state == "SUCCEEDED": + break + if c >= 18: + print("Timed out after 3min, Check task status on UI") + exit(1) + + print(f"vpc_task_status = {state}") diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_variable_subnet_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_variable_subnet_name_Task_SampleTask.py new file mode 100644 index 000000000..14da5f633 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_variable_subnet_name_Task_SampleTask.py @@ -0,0 +1,29 @@ +import requests + +vpc_name = "@@{vpc_name}@@".split(",") + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(url, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get vpc uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/subnets/list" +payload = {"kind": "subnet", "offset": 0} +r = apiCall(api) + +tmp = [] +for v in vpc_name: + vname = v.split(":")[0] + vuuid = v.split(":")[1] + for i in r["entities"]: + name = i["status"]["name"] + if i["spec"]["resources"]["subnet_type"] == "OVERLAY": + if i["spec"]["resources"]["vpc_reference"]["uuid"] == vuuid: + suuid = i["metadata"]["uuid"] + tmp.append(vname + ":" + name + ":" + suuid) +print("|".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_variable_vpc_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_variable_vpc_name_Task_SampleTask.py new file mode 100644 index 000000000..59956c29d --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Delete_variable_vpc_name_Task_SampleTask.py @@ -0,0 +1,22 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(url, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get vpc uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/vpcs/list" +payload = {"kind": "vpc", "offset": 0} +r = apiCall(api) + +tmp = [] +for cname in r["entities"]: + vpcname = cname["status"]["name"] + vpcuuid = cname["metadata"]["uuid"] + tmp.append(vpcname + ":" + vpcuuid) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_List_Task_ListVPCs.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_List_Task_ListVPCs.py new file mode 100644 index 000000000..7c3c0d671 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_List_Task_ListVPCs.py @@ -0,0 +1,24 @@ +import requests + +pc_user = "@@{account.username}@@" +pc_pass = "@@{account.password}@@" +api = "https://@@{account.pc_server}@@:@@{account.pc_port}@@/api/nutanix/v3/vpcs/list" +payload = { + "kind": "vpc", + "offset": 0, + "sort_attribute": "@@{sort_attribute}@@", + "sort_order": "@@{sort_order}@@", + "length": 50, +} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r.raise_for_status() +r = json.loads(r.content) +tmp = [] +for sname in r["entities"]: + tmp1 = sname["status"]["name"] + tmp1 = tmp1 + ":" + sname["status"]["resources"]["vpc_type"] + tmp1 = tmp1 + ":" + sname["metadata"]["uuid"] + tmp.append(tmp1) + +print("vpc_list=" + ",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_Task_UpdateVPC.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_Task_UpdateVPC.py new file mode 100644 index 000000000..9fa605bc5 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_Task_UpdateVPC.py @@ -0,0 +1,105 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def create_payload(name, uuid, specid): + vpc_type = "REGULAR" + external_subnet_reference = "@@{external_subnet_reference}@@".strip().split(":")[2] + spec_version = int(specid) + + dns_ip_list = [] + dnsip_list = "@@{common_domain_name_server_ip_list}@@".strip().split(",") + if len(dnsip_list[0]) != 0: + for p in dnsip_list: + p = p.strip().replace('"', "") + val = {"ip": p} + dns_ip_list.append(val) + + ext_route_prefix_list = [] + extrouteip_list = "@@{externally_routable_prefix_list}@@".strip().split(",") + if len(extrouteip_list[0]) != 0: + for p in extrouteip_list: + p = p.strip().replace('"', "") + p_ip = p.split("/")[0] + p_prefix = p.split("/")[1] + val = {"ip": p_ip, "prefix_length": int(p_prefix)} + ext_route_prefix_list.append(val) + + payload["spec"] = {"name": name} + payload["spec"]["resources"] = {"vpc_type": vpc_type} + payload["spec"]["resources"]["external_subnet_list"] = [ + { + "external_subnet_reference": { + "kind": "subnet", + "uuid": external_subnet_reference, + } + } + ] + if dns_ip_list: + payload["spec"]["resources"]["common_domain_name_server_ip_list"] = dns_ip_list + if ext_route_prefix_list: + payload["spec"]["resources"][ + "externally_routable_prefix_list" + ] = ext_route_prefix_list + if "@@{Description}@@".strip() != "": + payload["spec"]["description"] = "@@{Description}@@".strip() + payload["metadata"] = {"kind": "vpc", "uuid": uuid, "spec_version": spec_version} + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= 18: + task_status_response = session.get( + "https://{}:{}/api/nutanix/v3/tasks/{}".format(PC_IP, PC_PORT, task_uuid), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['status']}") + if task_status_response.json()["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + elif task_status_response.json()["status"] == "FAILED": + raise Exception( + f"Task status is failed, response {task_status_response.json()}" + ) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def update_vpc(): + data1 = "@@{vpc_name}@@".strip().split(":") + name = data1[0] + uuid = data1[1] + specid = data1[3] + create_payload(name, uuid, specid) + print(f"payload = {payload}") + update_vpc = session.put( + "https://{}:{}/api/nutanix/v3/vpcs/{}".format(PC_IP, PC_PORT, uuid), + json=payload, + headers={"Content-Type": "application/json", "accept": "application/json"}, + ) + print(f"response = {update_vpc.json()}") + update_vpc.raise_for_status() + task_uuid = update_vpc.json()["status"]["execution_context"]["task_uuid"] + print(f"task_uuid = {task_uuid}") + if "Yes" == "Yes": + wait(task_uuid) + + +update_vpc() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_variable_external_subnet_reference_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_variable_external_subnet_reference_Task_SampleTask.py new file mode 100644 index 000000000..cc32ccf82 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_variable_external_subnet_reference_Task_SampleTask.py @@ -0,0 +1,24 @@ +import requests + +pc_user = "@@{username}@@" +pc_pass = "@@{password}@@" + +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/subnets/list" +payload = {"kind": "subnet", "offset": 0} + +r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) +r = json.loads(r.content) + +# slist = [ sname['status']['name'] + ':' + sname['metadata']['uuid'] for sname in r['entities'] ] +slist = [ + sname["status"]["name"] + + ":" + + sname["status"]["resources"]["subnet_type"] + + ":" + + sname["metadata"]["uuid"] + for sname in r["entities"] + if "is_external" in sname["status"]["resources"].keys() + if sname["status"]["resources"]["is_external"] +] + +print(",".join(slist)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_variable_vpc_name_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_variable_vpc_name_Task_SampleTask.py new file mode 100644 index 000000000..5e3da17e9 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VPC_Action_Update_variable_vpc_name_Task_SampleTask.py @@ -0,0 +1,24 @@ +import requests + + +def apiCall(url): + pc_user = "@@{username}@@" + pc_pass = "@@{password}@@" + r = requests.post(url, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + + +# get vpc uuid +api = "https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/vpcs/list" +payload = {"kind": "vpc", "offset": 0} +r = apiCall(api) + +tmp = [] +for cname in r["entities"]: + vpcname = cname["status"]["name"] + vpcuuid = cname["metadata"]["uuid"] + vpctype = cname["status"]["resources"]["vpc_type"] + specid = cname["metadata"]["spec_version"] + tmp.append(vpcname + ":" + vpcuuid + ":" + vpctype + ":" + str(specid)) +print(",".join(tmp)) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_ClearCounter_Task_Clear_Counter.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_ClearCounter_Task_Clear_Counter.py new file mode 100644 index 000000000..c5dfb1213 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_ClearCounter_Task_Clear_Counter.py @@ -0,0 +1,120 @@ +import requests +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_vpc_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/vpcs".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@" + ), + name, + ) + + +def get_policy_extid(): + vpc_name = "@@{clear_counter}@@".split(":")[0] + policy_name = "@@{clear_counter}@@".split(":")[1] + priority = "@@{clear_counter}@@".split(":")[2] + headers = {"Content-Type": "application/json"} + response = session.get( + "https://{}:{}/api/networking/{}/config/routing-policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ), + headers=headers, + verify=False, + ) + extID = [ + d["extId"] + for d in response.json()["data"] + if d["name"] == policy_name + and d["vpcExtId"] == get_vpc_ext_id(vpc_name) + and d["priority"] == int(priority) + ] + return extID[0] + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create_payload(): + vpc_name = "@@{clear_counter}@@".split(":")[0] + payload["vpcExtId"] = get_vpc_ext_id(vpc_name) + if "@@{select_all_policies}@@" == "No": + payload["routingPolicyExtId"] = get_policy_extid() + + +def clear_counter(): + create_payload() + response = session.post( + "https://{}:{}/api/networking/{}/stats/routing-policies/$actions/clear".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@" + ), + json=payload, + headers={ + "Content-Type": "application/json", + "NTNX-Request-Id": str(uuid.uuid4()), + }, + timeout=120, + ) + print(f"delete response: {response.text}") + response.raise_for_status() + task_uuid = response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +clear_counter() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_ClearCounter_variable_clear_counter_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_ClearCounter_variable_clear_counter_Task_SampleTask.py new file mode 100644 index 000000000..d9aeae27b --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_ClearCounter_variable_clear_counter_Task_SampleTask.py @@ -0,0 +1,70 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def get_vpc_name(extId): + response = requests.get( + "https://{}:{}/api/networking/{}/config/vpcs/{}".format( + PC_IP, PC_PORT, "v4.0.b1", extId + ), + auth=(PC_USERNAME, PC_PASSWORD), + params={}, + verify=False, + ) + name = response.json()["data"]["name"] + return name + + +def list_policies(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/networking/{}/config/routing-policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + # print(f"response: {response.json()}") + response.raise_for_status() + policy_list = [] + vpclist = {} + + if "data" in response.json(): + data = response.json()["data"] + for d in data: + name = d["name"] + priority = d["priority"] + if d["vpcExtId"] not in vpclist.keys(): + vpclist[d["vpcExtId"]] = get_vpc_name(d["vpcExtId"]) + actionType = d["policies"][0]["policyAction"]["actionType"] + policy_list.append( + vpclist[d["vpcExtId"]] + + ":" + + name + + ":" + + str(priority) + + ":" + + actionType + ) + if "@@{select_all_policies}@@" == "No": + print(f"{','.join(policy_list)}") + else: + policy_list1 = [t1.split(":")[0] for t1 in policy_list] + policy_list1 = sorted(set(policy_list1)) + print(f"{','.join(policy_list1)}") + + +params = { + "$page": 0, + "$limit": 50, + "$orderby": "priority", +} +list_policies(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Create_Task_Create.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Create_Task_Create.py new file mode 100644 index 000000000..7267b0ff5 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Create_Task_Create.py @@ -0,0 +1,305 @@ +import requests +import re +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + +ipv4 = r"^(?:\d{1,3}\.){3}\d{1,3}$" +ipv6 = r"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$" + + +def find(Ip): + if re.search(ipv4, Ip): + return "ipv4" + elif re.search(ipv6, Ip): + return "ipv6" + else: + print("Not a valid ip address") + + +# --------------------------------------------------------------------------- +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_vpc_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/vpcs".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@" + ), + name, + ) + + +# ------------------------------------------------------------------------------------------- +def add_subnet(address_Type, subnet_ip): + # addressType = EXTERNAL or ANY or Custom(SUBNET) + tmp1 = {} + tmp1["addressType"] = address_Type.strip() + if address_Type.strip() == "SUBNET": + subnet_ipaddr = subnet_ip.split("/")[0] + subnet_prefix = subnet_ip.split("/")[1] + tmp1["subnetPrefix"] = { + find(subnet_ipaddr): { + "ip": {"value": subnet_ipaddr, "prefixLength": int(subnet_prefix)}, + "prefixLength": int(subnet_prefix), + } + } + return tmp1 + + +def add_PortRanges(port_list): + tmp2 = [] + for port in port_list: + if "-" in port: + startPort = int(port.split("-")[0]) + endPort = int(port.split("-")[1]) + else: + startPort = int(port) + endPort = int(port) + tmp2.append({"startPort": startPort, "endPort": endPort}) + return tmp2 + + +def add_protocolParameters(): + # variables :: + # protocolType = ANY | PROTOCOL_NUMBER | TCP | UDP | ICMP + # TCP_UDP_DATA = ANY | < startPort,startPort:endPort,endPort > + # ICMP_DATA = ANY | < icmptype_no:[ANY|icmpcode_no] >, + # if ICMP_TYPE = ANY then ICMP_CODE = ANY + # if ICMP_TYPE = no then ICMP_CODE either ANY or No. + + if ( + "@@{protocolType}@@" in ["TCP", "UDP"] + and "@@{TCP_UDP_DATA}@@".strip() != "ANY:ANY" + ): + payload["policies"][0]["policyMatch"]["protocolParameters"] = { + "$objectType": "networking.v4.config.LayerFourProtocolObject" + } + + if "@@{protocolType}@@" in ["TCP", "UDP"]: + if "@@{TCP_UDP_DATA}@@" == "": + raise Exception("TCP_UDP_DATA values are not entered.") + source_port_list = "@@{TCP_UDP_DATA}@@".split(":")[0].split(",") + if "ANY" not in source_port_list: + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "sourcePortRanges" + ] = add_PortRanges(source_port_list) + + if "@@{protocolType}@@" in ["TCP", "UDP"]: + destination_port_list = "@@{TCP_UDP_DATA}@@".split(":")[1].split(",") + if "ANY" not in destination_port_list: + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "destinationPortRanges" + ] = add_PortRanges(destination_port_list) + + if "@@{protocolType}@@" == "PROTOCOL_NUMBER": + if "@@{protocolNumber}@@" == "": + raise Exception("protocolNumber value is not entered.") + payload["policies"][0]["policyMatch"]["protocolParameters"] = { + "$objectType": "networking.v4.config.ProtocolNumberObject" + } + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "protocolNumber" + ] = int("@@{protocolNumber}@@") + + if "@@{protocolType}@@" == "ICMP": + if "@@{ICMP_DATA}@@" == "": + raise Exception("ICMP_DATA values are not entered.") + if "@@{ICMP_DATA}@@".strip() != "ANY:ANY": + payload["policies"][0]["policyMatch"]["protocolParameters"] = { + "$objectType": "networking.v4.config.ICMPObject" + } + if "@@{ICMP_DATA}@@".split(":")[0] != "ANY": + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "icmpType" + ] = int("@@{ICMP_DATA}@@".split(":")[0]) + if "@@{ICMP_DATA}@@".split(":")[1] != "ANY": + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "icmpCode" + ] = int("@@{ICMP_DATA}@@".split(":")[1]) + + +def add_rerouteParams(): + # Variables: + # separate_reroute = YES | NO + # rerouteFallbackAction = PASSTHROUGH | NO_ACTION | ALLOW | DROP + # "serviceIp": { "ipv4": { "value": "string", "prefixLength": 32 }, "ipv6": { "value": "string", "prefixLength": 128} }, + # "ingressServiceIp": { "ipv4": { "value": "string", "prefixLength": 32 }, "ipv6": { "value": "string", "prefixLength": 128 } }, + # "egressServiceIp": { "ipv4": { "value": "string", "prefixLength": 32 }, "ipv6": { "value": "string", "prefixLength": 128 } } + payload["policies"][0]["policyAction"]["rerouteParams"] = [{}] + payload["policies"][0]["policyAction"]["rerouteParams"][0][ + "rerouteFallbackAction" + ] = "@@{rerouteFallbackAction}@@".strip() + + if "@@{separate_reroute}@@" == "Yes": + ingressPrefix = 32 + if find("@@{ingressServiceIp}@@") == "ipv6": + ingressPrefix = 128 + egressPrefix = 32 + if find("@@{egressServiceIp}@@") == "ipv6": + egressPrefix = 128 + + payload["policies"][0]["policyAction"]["rerouteParams"][0][ + "ingressServiceIp" + ] = { + find("@@{ingressServiceIp}@@"): { + "value": "@@{ingressServiceIp}@@", + "prefixLength": ingressPrefix, + } + } + payload["policies"][0]["policyAction"]["rerouteParams"][0][ + "egressServiceIp" + ] = { + find("@@{egressServiceIp}@@"): { + "value": "@@{egressServiceIp}@@", + "prefixLength": egressPrefix, + } + } + else: + prefix = 32 + if find("@@{serviceIp}@@") == "ipv6": + prefix = 128 + payload["policies"][0]["policyAction"]["rerouteParams"][0]["serviceIp"] = { + find("@@{serviceIp}@@"): { + "value": "@@{serviceIp}@@", + "prefixLength": prefix, + } + } + + +def add_forwardParams(): + prefix = 32 + if find("@@{forwardIp}@@") == "ipv6": + prefix = 128 + payload["policies"][0]["policyAction"]["nexthopIpAddress"] = { + find("@@{forwardIp}@@"): {"value": "@@{forwardIp}@@", "prefixLength": prefix} + } + + +def add_policy(): + # Variables: + # source_addressType = ANY, EXTERNAL, SUBNET(custom) + # destination_addressType = ANY, EXTERNAL, SUBNET(custom) + # actionType = PERMIT, DENY, REROUTE, FORWARD + # source_subnet_ip / destination_subnet_ip = + # isBidirectional = YES/NO + # protocolType = ANY | PROTOCOL_NUMBER | TCP | UDP | ICMP + source_addressType = "@@{source_addressType}@@" + if source_addressType == "CUSTOM": + source_addressType = "SUBNET" + destination_addressType = "@@{destination_addressType}@@" + if destination_addressType == "CUSTOM": + destination_addressType = "SUBNET" + payload["policies"] = [{}] + payload["policies"][0]["policyMatch"] = { + "source": add_subnet(source_addressType, "@@{source_subnet_ip}@@"), + "destination": add_subnet( + destination_addressType, "@@{destination_subnet_ip}@@" + ), + "protocolType": "@@{protocolType}@@", + } + + if "@@{protocolType}@@" != "ANY": + add_protocolParameters() + + payload["policies"][0]["policyAction"] = {"actionType": "@@{actionType}@@"} + + if "@@{actionType}@@" in ["REROUTE"]: + add_rerouteParams() + + if "@@{actionType}@@" in ["FORWARD"]: + add_forwardParams() + + if "@@{actionType}@@" != "FORWARD" and "@@{isBidirectional}@@".strip() == "Yes": + payload["policies"][0]["isBidirectional"] = True + else: + payload["policies"][0]["isBidirectional"] = False + + +def add_basic_details(): + payload["name"] = "@@{policy_name}@@".strip() + payload["priority"] = int("@@{priority}@@".strip()) + payload["vpcExtId"] = "@@{vpcExtId}@@".strip().split(":")[1] + if "@@{description}@@" != "": + payload["description"] = "@@{description}@@" + # payload['extId'] = get_vpc_ext_id("@@{vpcExtId}@@".strip().split(':')[0]) + + +def create_payload(): + add_basic_details() + add_policy() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def create(): + create_payload() + print(f"payload: {payload}") + create_routing_policy_response = session.post( + "https://{}:{}/api/networking/{}/config/routing-policies".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@" + ), + json=payload, + headers={ + "Content-Type": "application/json", + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"create response: {create_routing_policy_response.json()}") + create_routing_policy_response.raise_for_status() + task_uuid = create_routing_policy_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +create() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Create_variable_vpcExtId_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Create_variable_vpcExtId_Task_SampleTask.py new file mode 100644 index 000000000..375fbadac --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Create_variable_vpcExtId_Task_SampleTask.py @@ -0,0 +1,35 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" +# ---------------------------------- + + +def list_policies(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/networking/{}/config/vpcs".format(PC_IP, PC_PORT, "v4.0.b1"), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + # print(f"response: {response.json()}") + response.raise_for_status() + # print("object_stores = " + json.dumps(json.dumps(response.json()["data"]))) + tmp = [] + if "data" in response.json(): + for data in response.json()["data"]: + vpcname = data["name"] + vpcextId = data["extId"] + tmp.append(vpcname + ":" + vpcextId) + print(",".join(tmp)) + + +params = {} + +list_policies(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Delete_Task_Delete.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Delete_Task_Delete.py new file mode 100644 index 000000000..dd46fd211 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Delete_Task_Delete.py @@ -0,0 +1,108 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False + + +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_vpc_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/vpcs".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@" + ), + name, + ) + + +def get_policy_extid(): + vpc_name = "@@{delete_policy}@@".split(":")[0] + policy_name = "@@{delete_policy}@@".split(":")[1] + priority = "@@{delete_policy}@@".split(":")[2] + headers = {"Content-Type": "application/json"} + response = session.get( + "https://{}:{}/api/networking/{}/config/routing-policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ), + headers=headers, + verify=False, + ) + extID = [ + d["extId"] + for d in response.json()["data"] + if d["name"] == policy_name + and d["vpcExtId"] == get_vpc_ext_id(vpc_name) + and d["priority"] == int(priority) + ] + return extID[0] + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def delete(): + response = session.delete( + "https://{}:{}/api/networking/{}/config/routing-policies/{}".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@", get_policy_extid() + ), + headers={ + "Content-Type": "application/json", + "NTNX-Request-Id": str(uuid.uuid4()), + }, + timeout=120, + ) + print(f"delete response: {response.text}") + response.raise_for_status() + task_uuid = response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +delete() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Delete_variable_delete_policy_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Delete_variable_delete_policy_Task_SampleTask.py new file mode 100644 index 000000000..18c6135f0 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Delete_variable_delete_policy_Task_SampleTask.py @@ -0,0 +1,67 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def get_vpc_name(extId): + response = requests.get( + "https://{}:{}/api/networking/{}/config/vpcs/{}".format( + PC_IP, PC_PORT, "v4.0.b1", extId + ), + auth=(PC_USERNAME, PC_PASSWORD), + params={}, + verify=False, + ) + name = response.json()["data"]["name"] + return name + + +def list_policies(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/networking/{}/config/routing-policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + # print(f"response: {response.json()}") + response.raise_for_status() + tmp = [] + vpclist = {} + + if "data" in response.json(): + data = response.json()["data"] + for d in data: + name = d["name"] + priority = d["priority"] + if d["vpcExtId"] not in vpclist.keys(): + vpclist[d["vpcExtId"]] = get_vpc_name(d["vpcExtId"]) + protocolType = d["policies"][0]["policyMatch"]["protocolType"] + actionType = d["policies"][0]["policyAction"]["actionType"] + tmp.append( + vpclist[d["vpcExtId"]] + + ":" + + name + + ":" + + str(priority) + + ":" + + actionType + ) + + print(f"{','.join(tmp)}") + + +params = { + "$page": 0, + "$limit": 50, + "$orderby": "priority", +} +list_policies(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Get_Task_Get.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Get_Task_Get.py new file mode 100644 index 000000000..ae6e3cd86 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Get_Task_Get.py @@ -0,0 +1,27 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def get_policies(extId): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/networking/{}/config/routing-policies/{}".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@", extId + ), + auth=(PC_USERNAME, PC_PASSWORD), + headers=headers, + verify=False, + ) + print(f"response: {response.json()}") + response.raise_for_status() + print("get_policies = " + json.dumps(json.dumps(response.json()["data"]))) + + +extId = "@@{extId}@@".strip() +get_policies(extId) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_List_Task_Listpolicies.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_List_Task_Listpolicies.py new file mode 100644 index 000000000..2488197b2 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_List_Task_Listpolicies.py @@ -0,0 +1,38 @@ +import requests + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" + + +def list_policies(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/networking/{}/config/routing-policies".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + print(f"response: {response.json()}") + response.raise_for_status() + print("list_policies = " + json.dumps(json.dumps(response.json()["data"]))) + + +params = { + "$page": @@{page}@@, + "$limit": @@{limit}@@, +} +if "@@{filter}@@": + params["filter"] = "@@{filter}@@" +if "@@{select}@@": + params["select"] = "@@{select}@@" +if "@@{orderby}@@": + params["orderby"] = "@@{orderby}@@" + +list_policies(params) diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Update_Task_Update.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Update_Task_Update.py new file mode 100644 index 000000000..3d2fd3502 --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Update_Task_Update.py @@ -0,0 +1,342 @@ +import requests +import re +import uuid +import base64 + +PC_USERNAME = "@@{account.username}@@" +PC_PASSWORD = "@@{account.password}@@" +PC_IP = "@@{account.pc_server}@@" +PC_PORT = "@@{account.pc_port}@@" +session = requests.Session() +session.auth = (PC_USERNAME, PC_PASSWORD) +session.verify = False +payload = {} + +ipv4 = r"^(?:\d{1,3}\.){3}\d{1,3}$" +ipv6 = r"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$" + + +def find(Ip): + if re.search(ipv4, Ip): + return "ipv4" + elif re.search(ipv6, Ip): + return "ipv6" + else: + print("Not a valid ip address") + + +# --------------------------------------------------------------------------- +def get_resource_ext_id(url, name, id_key="extId"): + response = session.get( + url, + headers={ + "accept": "application/json", + }, + params={"$page": 0, "$limit": 1, "$filter": f"name eq '{name}'"}, + ) + print(f"get {name} response: {response.json()}") + response.raise_for_status() + data = response.json().get("data") + if data: + if isinstance(data, list): + if id_key in data[0] and data[0]["name"] == name: + return data[0][id_key] + else: + if id_key in data: + return data[id_key] + raise Exception(f"failed to get extId for {name}") + + +def get_vpc_ext_id(name): + return get_resource_ext_id( + "https://{}:{}/api/networking/{}/config/vpcs".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@" + ), + name, + ) + + +def get_policy_extId(): + vpcextid = payload["vpcExtId"] + priority = "@@{vpcname}@@".strip().split(":")[2] + headers = { + "accept": "application/json", + } + params = {"$page": 0, "$limit": 50} + response = session.get( + "https://{}:{}/api/networking/{}/config/routing-policies".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@" + ), + params=params, + headers=headers, + verify=False, + ) + extId = [ + d["extId"] + for d in response.json()["data"] + if d["vpcExtId"] == vpcextid and d["priority"] == int(priority) + ] + + response1 = session.get( + "https://{}:{}/api/networking/{}/config/routing-policies/{}".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@", extId[0] + ), + verify=False, + ) + response1.raise_for_status() + return extId[0], response1.headers["ETag"] + + +# ------------------------------------------------------------------------------------------- +def add_subnet(address_Type, subnet_ip): + # addressType = EXTERNAL or ANY or Custom(SUBNET) + tmp1 = {} + tmp1["addressType"] = address_Type.strip() + if address_Type.strip() == "SUBNET": + subnet_ipaddr = subnet_ip.split("/")[0] + subnet_prefix = subnet_ip.split("/")[1] + tmp1["subnetPrefix"] = { + find(subnet_ipaddr): { + "ip": {"value": subnet_ipaddr, "prefixLength": int(subnet_prefix)}, + "prefixLength": int(subnet_prefix), + } + } + return tmp1 + + +def add_PortRanges(port_list): + tmp2 = [] + for port in port_list: + if "-" in port: + startPort = int(port.split("-")[0]) + endPort = int(port.split("-")[1]) + else: + startPort = int(port) + endPort = int(port) + tmp2.append({"startPort": startPort, "endPort": endPort}) + return tmp2 + + +def add_protocolParameters(): + # variables :: + # protocolType = ANY | PROTOCOL_NUMBER | TCP | UDP | ICMP + # TCP_UDP_DATA = ANY | < startPort,startPort:endPort,endPort > + # ICMP_DATA = ANY | < icmptype_no:[ANY|icmpcode_no] >, + # if ICMP_TYPE = ANY then ICMP_CODE = ANY + # if ICMP_TYPE = no then ICMP_CODE either ANY or No. + + if ( + "@@{protocolType}@@" in ["TCP", "UDP"] + and "@@{TCP_UDP_DATA}@@".strip() != "ANY:ANY" + ): + payload["policies"][0]["policyMatch"]["protocolParameters"] = { + "$objectType": "networking.v4.config.LayerFourProtocolObject" + } + + if "@@{protocolType}@@" in ["TCP", "UDP"]: + if "@@{TCP_UDP_DATA}@@" == "": + raise Exception("TCP_UDP_DATA values are not entered.") + source_port_list = "@@{TCP_UDP_DATA}@@".split(":")[0].split(",") + if "ANY" not in source_port_list: + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "sourcePortRanges" + ] = add_PortRanges(source_port_list) + + if "@@{protocolType}@@" in ["TCP", "UDP"]: + destination_port_list = "@@{TCP_UDP_DATA}@@".split(":")[1].split(",") + if "ANY" not in destination_port_list: + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "destinationPortRanges" + ] = add_PortRanges(destination_port_list) + + if "@@{protocolType}@@" == "PROTOCOL_NUMBER": + if "@@{protocolNumber}@@" == "": + raise Exception("protocolNumber value is not entered.") + payload["policies"][0]["policyMatch"]["protocolParameters"] = { + "$objectType": "networking.v4.config.ProtocolNumberObject" + } + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "protocolNumber" + ] = int("@@{protocolNumber}@@") + + if "@@{protocolType}@@" == "ICMP": + if "@@{ICMP_DATA}@@" == "": + raise Exception("ICMP_DATA values are not entered.") + if "@@{ICMP_DATA}@@".strip() != "ANY:ANY": + payload["policies"][0]["policyMatch"]["protocolParameters"] = { + "$objectType": "networking.v4.config.ICMPObject" + } + if "@@{ICMP_DATA}@@".split(":")[0] != "ANY": + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "icmpType" + ] = int("@@{ICMP_DATA}@@".split(":")[0]) + if "@@{ICMP_DATA}@@".split(":")[1] != "ANY": + payload["policies"][0]["policyMatch"]["protocolParameters"][ + "icmpCode" + ] = int("@@{ICMP_DATA}@@".split(":")[1]) + + +def add_rerouteParams(): + # Variables: + # separate_reroute = YES | NO + # rerouteFallbackAction = PASSTHROUGH | NO_ACTION | ALLOW | DROP + # "serviceIp": { "ipv4": { "value": "string", "prefixLength": 32 }, "ipv6": { "value": "string", "prefixLength": 128} }, + # "ingressServiceIp": { "ipv4": { "value": "string", "prefixLength": 32 }, "ipv6": { "value": "string", "prefixLength": 128 } }, + # "egressServiceIp": { "ipv4": { "value": "string", "prefixLength": 32 }, "ipv6": { "value": "string", "prefixLength": 128 } } + payload["policies"][0]["policyAction"]["rerouteParams"] = [{}] + payload["policies"][0]["policyAction"]["rerouteParams"][0][ + "rerouteFallbackAction" + ] = "@@{rerouteFallbackAction}@@".strip() + + if "@@{separate_reroute}@@" == "Yes": + ingressPrefix = 32 + if find("@@{ingressServiceIp}@@") == "ipv6": + ingressPrefix = 128 + egressPrefix = 32 + if find("@@{egressServiceIp}@@") == "ipv6": + egressPrefix = 128 + + payload["policies"][0]["policyAction"]["rerouteParams"][0][ + "ingressServiceIp" + ] = { + find("@@{ingressServiceIp}@@"): { + "value": "@@{ingressServiceIp}@@", + "prefixLength": ingressPrefix, + } + } + payload["policies"][0]["policyAction"]["rerouteParams"][0][ + "egressServiceIp" + ] = { + find("@@{egressServiceIp}@@"): { + "value": "@@{egressServiceIp}@@", + "prefixLength": egressPrefix, + } + } + else: + prefix = 32 + if find("@@{serviceIp}@@") == "ipv6": + prefix = 128 + payload["policies"][0]["policyAction"]["rerouteParams"][0]["serviceIp"] = { + find("@@{serviceIp}@@"): { + "value": "@@{serviceIp}@@", + "prefixLength": prefix, + } + } + + +def add_forwardParams(): + prefix = 32 + if find("@@{forwardIp}@@") == "ipv6": + prefix = 128 + payload["policies"][0]["policyAction"]["nexthopIpAddress"] = { + find("@@{forwardIp}@@"): {"value": "@@{forwardIp}@@", "prefixLength": prefix} + } + + +def add_policy(): + # Variables: + # source_addressType = ANY, EXTERNAL, SUBNET(custom) + # destination_addressType = ANY, EXTERNAL, SUBNET(custom) + # actionType = PERMIT, DENY, REROUTE, FORWARD + # source_subnet_ip / destination_subnet_ip = + # isBidirectional = YES/NO + # protocolType = ANY | PROTOCOL_NUMBER | TCP | UDP | ICMP + + if "@@{source_addressType}@@".strip() != "": + source_addressType = "@@{source_addressType}@@" + if source_addressType == "CUSTOM": + source_addressType = "SUBNET" + if "@@{destination_addressType}@@".strip() != "": + destination_addressType = "@@{destination_addressType}@@" + if destination_addressType == "CUSTOM": + destination_addressType = "SUBNET" + + payload["policies"] = [{}] + payload["policies"][0]["policyMatch"] = { + "source": add_subnet(source_addressType, "@@{source_subnet_ip}@@"), + "destination": add_subnet( + destination_addressType, "@@{destination_subnet_ip}@@" + ), + "protocolType": "@@{protocolType}@@", + } + + if "@@{protocolType}@@" != "ANY": + add_protocolParameters() + + if "@@{actionType}@@".strip() != "": + payload["policies"][0]["policyAction"] = {"actionType": "@@{actionType}@@"} + if "@@{actionType}@@" in ["REROUTE"]: + add_rerouteParams() + if "@@{actionType}@@" in ["FORWARD"]: + add_forwardParams() + + if "@@{isBidirectional}@@".strip() == "Yes": + payload["policies"][0]["isBidirectional"] = True + else: + payload["policies"][0]["isBidirectional"] = False + + +def add_basic_details(): + payload["vpcExtId"] = get_vpc_ext_id("@@{vpcname}@@".strip().split(":")[0]) + if "@@{policy_name}@@".strip() != "": + payload["name"] = "@@{policy_name}@@".strip() + if "@@{priority}@@".strip() != "": + payload["priority"] = int("@@{priority}@@".strip()) + if "@@{description}@@" != "": + payload["description"] = "@@{description}@@" + + +def create_payload(): + add_basic_details() + add_policy() + + +def wait(task_uuid, timeout=1800): + max_count = timeout / 10 + count = 0 + task_status_response = None + while count <= max_count: + task_status_response = session.get( + "https://{}:{}/api/prism/{}/config/tasks/{}".format( + PC_IP, PC_PORT, "@@{prism_api_version}@@", task_uuid + ), + ) + if task_status_response.status_code != 200: + raise Exception( + f"failed to get task, got response {task_status_response.json()}" + ) + print(f"task status is {task_status_response.json()['data']['status']}") + if task_status_response.json()["data"]["status"] in {"QUEUED", "RUNNING"}: + count += 1 + sleep(10) + else: + count = 0 + break + print(f"task_status = {task_status_response.json()['data']['status']}") + if count != 0: + raise Exception("timed out waiting for task to complete") + + +def update(): + create_payload() + print(f"payload: {payload}") + get_extid_etag = get_policy_extId() + update_routing_policy_response = session.put( + "https://{}:{}/api/networking/{}/config/routing-policies/{}".format( + PC_IP, PC_PORT, "@@{flow_virtual_network_api_version}@@", get_extid_etag[0] + ), + json=payload, + headers={ + "Content-Type": "application/json", + "If-Match": get_extid_etag[1], + "NTNX-Request-Id": str(uuid.uuid4()), + }, + ) + print(f"create response: {update_routing_policy_response.json()}") + update_routing_policy_response.raise_for_status() + task_uuid = update_routing_policy_response.json()["data"]["extId"] + print(f"task_uuid = {task_uuid}") + wait(task_uuid) + + +update() diff --git a/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Update_variable_vpcname_Task_SampleTask.py b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Update_variable_vpcname_Task_SampleTask.py new file mode 100644 index 000000000..8af71cb6f --- /dev/null +++ b/examples/NutanixPaaS_provider/scripts/ResourceType_VirtualNetworkPolicies_Action_Update_variable_vpcname_Task_SampleTask.py @@ -0,0 +1,68 @@ +import requests + +PC_USERNAME = "@@{username}@@" +PC_PASSWORD = "@@{password}@@" +PC_IP = "@@{pc_server}@@" +PC_PORT = "@@{pc_port}@@" + + +def get_vpc_name(extId): + response = requests.get( + "https://{}:{}/api/networking/{}/config/vpcs/{}".format( + PC_IP, PC_PORT, "v4.0.b1", extId + ), + auth=(PC_USERNAME, PC_PASSWORD), + params={}, + verify=False, + ) + name = response.json()["data"]["name"] + return name + + +def list_policies(params): + headers = { + "accept": "application/json", + } + response = requests.get( + "https://{}:{}/api/networking/{}/config/routing-policies".format( + PC_IP, PC_PORT, "v4.0.b1" + ), + auth=(PC_USERNAME, PC_PASSWORD), + params=params, + headers=headers, + verify=False, + ) + # print(f"response: {response.json()}") + response.raise_for_status() + tmp = [] + vpclist = {} + if "data" in response.json(): + data = response.json()["data"] + for d in data: + name = d["name"] + priority = d["priority"] + if d["vpcExtId"] not in vpclist.keys(): + vpclist[d["vpcExtId"]] = get_vpc_name(d["vpcExtId"]) + protocolType = d["policies"][0]["policyMatch"]["protocolType"] + actionType = d["policies"][0]["policyAction"]["actionType"] + tmp.append( + vpclist[d["vpcExtId"]] + + ":" + + name + + ":" + + str(priority) + + ":" + + protocolType + + ":" + + actionType + ) + + print(f"{','.join(tmp)}") + + +params = { + "$page": 0, + "$limit": 50, + "$orderby": "priority", +} +list_policies(params) diff --git a/examples/Runbooks/runbook_http_response_mapping.py b/examples/Runbooks/runbook_http_response_mapping.py new file mode 100644 index 000000000..578590b4c --- /dev/null +++ b/examples/Runbooks/runbook_http_response_mapping.py @@ -0,0 +1,43 @@ +""" +Calm DSL HTTP Runbook +""" +import os +import json +from calm.dsl.runbooks import * +from calm.dsl.runbooks import ( + CalmEndpoint as Endpoint, + RunbookTask as CalmTask, + RunbookVariable as CalmVariable, +) +from calm.dsl.builtins import CalmTask as CalmVarTask, Metadata + +PCEndpoint = Endpoint.HTTP("https://localhost:9440/api/nutanix/v3") + + +# Runbook +@runbook +def DslHTTPTaskResponseCodeRange(endpoints=[PCEndpoint]): + CalmTask.HTTP.post( + name="Task 1", + relative_url="/endpoints/list", + headers={"Authorization": "Bearer @@{calm_jwt}@@"}, + content_type="application/json", + verify=False, + body=json.dumps({}), + response_code_status_map=[ + HTTPResponseHandle.ResponseCode( + code=200, + code_ranges=[{"start_code": 200, "end_code": 200}], + status=HTTPResponseHandle.TASK_STATUS.Success, + ) + ], + target=ref(endpoints[0]), + ) + + +def main(): + print(runbook_json(DslHTTPTaskResponseCodeRange)) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/Runbooks/runbook_status_mapping.py b/examples/Runbooks/runbook_status_mapping.py new file mode 100644 index 000000000..7afe3da1a --- /dev/null +++ b/examples/Runbooks/runbook_status_mapping.py @@ -0,0 +1,63 @@ +""" +Calm DSL VM Operation Runbook + +""" +import os +import json + +from calm.dsl.builtins import * +from calm.dsl.runbooks import runbook, runbook_json +from calm.dsl.runbooks import ( + CalmEndpoint as Endpoint, + RunbookTask as CalmTask, + RunbookVariable as CalmVariable, + StatusHandle, +) + + +CRED_USERNAME = read_local_file(".tests/runbook_tests/username") +CRED_PASSWORD = read_local_file(".tests/runbook_tests/password") +VM_IP = read_local_file(".tests/runbook_tests/vm_ip") + +Cred = basic_cred(CRED_USERNAME, CRED_PASSWORD, name="endpoint_cred") +DslLinuxEndpoint = Endpoint.Linux.ip([VM_IP], cred=Cred) + +# Runbook +@runbook +def DslStatusMap(endpoints=[DslLinuxEndpoint]): + CalmTask.Exec.ssh( + name="Task 1", + script="echo ('Hello')", + target=ref(endpoints[0]), + status_map_list=[ + StatusHandle.Mapping.task_status( + values=[ + StatusHandle.Status.TaskFailure + ], + result=StatusHandle.Result.Warning, + ) + ], + ) + CalmTask.Exec.escript.py3( + name="Task 2", + script="print ('Hello')", + status_map_list=[ + StatusHandle.Mapping.exit_code( + values=[ + {"start_code": 5, "end_code": 5}, + {"start_code": 10, "end_code": 15}, + {"start_code": 40, "end_code": 40}, + ], + result=StatusHandle.Result.Warning, + ) + ], + ) + + +def main(): + print(runbook_json(DslStatusMap)) + + +if __name__ == "__main__": + main() + diff --git a/examples/custom_providers/AWSProvider/aws_provider_account.py b/examples/custom_providers/AWSProvider/aws_provider_account.py new file mode 100644 index 000000000..3230677dc --- /dev/null +++ b/examples/custom_providers/AWSProvider/aws_provider_account.py @@ -0,0 +1,23 @@ +# flake8: noqa +# pylint: skip-file +from calm.dsl.builtins import Account, AccountResources, Ref, read_local_file + +# Provider with name "DSL_AWSProvider" should already exist +PROVIDER_NAME = "DSL_AWSProvider" + +CloudProvider_AWSProvider_account_variable_secret_access_key = read_local_file( + "CloudProvider_AWSProvider_account_variable_secret_access_key" +) + + +class HelloAccount(Account): + """Account corresponding to DSL_AWSProvider""" + + type = PROVIDER_NAME + resources = AccountResources.CustomProvider( + provider=Ref.Provider(PROVIDER_NAME), + variable_dict={ # Keys of this dict are variable names from the list of provider's [auth_schema + endpoint_schema] + "access_key_id": "AKIA2LDLYW6I5ZSKVMAJ", + "secret_access_key": CloudProvider_AWSProvider_account_variable_secret_access_key, + }, + ) diff --git a/examples/custom_providers/AWSProvider/provider.py b/examples/custom_providers/AWSProvider/provider.py new file mode 100644 index 000000000..df3c84a9e --- /dev/null +++ b/examples/custom_providers/AWSProvider/provider.py @@ -0,0 +1,230 @@ +# flake8: noqa +# pylint: skip-file +import os # no_qa + +from calm.dsl.builtins import ( + read_local_file, + CalmVariable, + ResourceType, + action, + CloudProvider, + ProviderEndpointSchema, + ProviderTestAccount, +) +from calm.dsl.builtins.models.task import ProviderTask as CalmTask + +# Secret Variables + +CloudProvider_AWSProvider_auth_schema_secret_access_key = read_local_file( + "CloudProvider_AWSProvider_auth_schema_secret_access_key" +) +CloudProvider_AWSProvider_test_account_variable_secret_access_key = read_local_file( + "CloudProvider_AWSProvider_test_account_variable_secret_access_key" +) + +# Credentials + +# ResourceTypes + + +class S3Bucket(ResourceType): + """ + S3 Bucket resource type. + Attributes: + name (str): The name of the S3 Bucket. + resource_kind (str): The kind of resource, which is "Storage" for S3 Bucket. + schemas (list): List of schemas associated with the S3 Bucket. + variables (list): List of variables associated with the S3 Bucket. + """ + + name = "S3 Bucket" + + resource_kind = "Storage" + + schemas = [] + + variables = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="rt_var", + ), + ] + + @action + def Create(type="resource_type_create"): + """ + Creates a new bucket with the specified parameters. + + Args: + type (str, optional): The type of resource to create. Defaults to "resource_type_create". + + Returns: + None + """ + + # Input Variables + bucket_name = CalmVariable.Simple( + "bucket-463783", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + location = CalmVariable.WithOptions( + ["us-east-1", "us-west-2"], + label="", + default="us-west-2", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + ACL = CalmVariable.Simple( + "public-read", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + + # Output Variables + outputs = [ + CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="ARN", + ), + ] + + CalmTask.SetVariable.escript.py3( + name="Create new Bucket", + filename=os.path.join( + "scripts", "ResourceType_S3Bucket_Action_Create_Task_CreatenewBucket.py" + ), + variables=["ARN"], + ) + + @action + def Delete(type="resource_type_delete"): + """ + Deletes a bucket. + + Args: + type (str, optional): The type of deletion. Defaults to "resource_type_delete". + + Returns: + None + """ + + # Input Variables + bucket_name = CalmVariable.Simple( + "", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) + + CalmTask.SetVariable.escript.py3( + name="Delete Bucket", + filename=os.path.join( + "scripts", "ResourceType_S3Bucket_Action_Delete_Task_DeleteBucket.py" + ), + variables=[], + ) + + +class DSL_AWSProvider(CloudProvider): + """ + AWS Provider. + Attributes: + name (str): The name of the S3 Bucket. + resource_kind (str): The kind of resource, which is "Storage" for S3 Bucket. + auth_schema_variables (list): List of schemas associated with the S3 Bucket. + variables (list): List of variables associated with the S3 Bucket. + endpoint_schema (ProviderEndpointSchema): Schema for the AWS Provider. + test_account (ProviderTestAccount): Test account for the AWS Provider. + resource_types (list): List of resource types associated with the AWS Provider. + """ + + infra_type = "cloud" + + auth_schema_variables = [ + CalmVariable.Simple( + "", + label="Access Key ID", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + name="access_key_id", + ), + CalmVariable.Simple.Secret( + CloudProvider_AWSProvider_auth_schema_secret_access_key, + label="Secret Access Key", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + name="secret_access_key", + ), + ] + + variables = [] + + endpoint_schema = ProviderEndpointSchema(type="AWS", variables=[]) + + test_account = ProviderTestAccount( + name="dsl_aws_test_account", + variables=[ + CalmVariable.Simple( + "AKIA2LDLYW6I7KAY7QWB", + label="Access Key ID", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + name="access_key_id", + ), + CalmVariable.Simple.Secret( + CloudProvider_AWSProvider_test_account_variable_secret_access_key, + label="Secret Access Key", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="", + name="secret_access_key", + ), + ], + ) + + resource_types = [S3Bucket] + + @action + def Verify(type="provider"): + """ + Verify the credentials for the AWS provider. + Args: + type (str, optional): The type of verification. Defaults to "provider". + """ + + # Input Variables + CalmTask.Exec.escript.py3( + name="VerifyCredentials", + filename=os.path.join( + "scripts", + "CloudProvider_AWSProvider_Action_Verify_Task_VerifyCredentials.py", + ), + ) diff --git a/examples/custom_providers/AWSProvider/scripts/CloudProvider_AWSProvider_Action_Verify_Task_VerifyCredentials.py b/examples/custom_providers/AWSProvider/scripts/CloudProvider_AWSProvider_Action_Verify_Task_VerifyCredentials.py new file mode 100644 index 000000000..e399d6d9e --- /dev/null +++ b/examples/custom_providers/AWSProvider/scripts/CloudProvider_AWSProvider_Action_Verify_Task_VerifyCredentials.py @@ -0,0 +1,44 @@ +# flake8: noqa +# pylint: skip-file +# type: ignore + +""" +Note: The provided methods in this script are generic and meant to serve as a starting point for credential verification. They can be customized to fit specific use cases and requirements +""" + +""" + +Script Verify AWS credentials by making an sts service call using the boto3 library. It takes following parameters as macros defined in test-account/ provider. + +Parameters: +`access_key_id`: The AWS access key ID. +`secret_access_key`: The AWS secret access key. +Process: + +Creates an sts client with the provided credentials and calls get_caller_identity. Checks for client errors to determine the validity of the credentials. + +""" + + +import boto3 + +try: + client = boto3.client( + "sts", + aws_access_key_id="@@{access_key_id}@@", + aws_secret_access_key="@@{secret_access_key}@@", + ) + client.get_caller_identity() + print("AWS credentials are valid") + exit(0) +except client.exceptions.ClientError as e: + error_code = e.response["Error"]["Code"] + if error_code == "InvalidClientTokenId": + print("Invalid AWS access key ID.") + elif error_code == "SignatureDoesNotMatch": + print("Invalid AWS secret access key.") + else: + print(f"AWS credentials are invalid. Error: {str(e)}") +except Exception as e: + print(f"Script execution failed with error: {e}") +exit(1) diff --git a/examples/custom_providers/AWSProvider/scripts/ResourceType_S3Bucket_Action_Create_Task_CreatenewBucket.py b/examples/custom_providers/AWSProvider/scripts/ResourceType_S3Bucket_Action_Create_Task_CreatenewBucket.py new file mode 100644 index 000000000..513941be7 --- /dev/null +++ b/examples/custom_providers/AWSProvider/scripts/ResourceType_S3Bucket_Action_Create_Task_CreatenewBucket.py @@ -0,0 +1,33 @@ +# flake8: noqa +# pylint: skip-file +# type: ignore + +# script +import boto3 + +# Create bucket +region = "@@{location}@@" +bucket_name = "@@{bucket_name}@@" +ACL = "@@{ACL}@@" + +if region is None: + s3_client = boto3.client( + "s3", + aws_access_key_id="@@{access_key_id}@@", + aws_secret_access_key="@@{secret_access_key}@@", + ) + response = s3_client.create_bucket(Bucket=bucket_name) +else: + s3_client = boto3.client( + "s3", + region_name=region, + aws_access_key_id="@@{access_key_id}@@", + aws_secret_access_key="@@{secret_access_key}@@", + ) + location = {"LocationConstraint": region} + response = s3_client.create_bucket( + Bucket=bucket_name, CreateBucketConfiguration=location + ) + +bucket_arn = response["Location"] +print("ARN={}".format(bucket_arn)) diff --git a/examples/custom_providers/AWSProvider/scripts/ResourceType_S3Bucket_Action_Delete_Task_DeleteBucket.py b/examples/custom_providers/AWSProvider/scripts/ResourceType_S3Bucket_Action_Delete_Task_DeleteBucket.py new file mode 100644 index 000000000..f4b243239 --- /dev/null +++ b/examples/custom_providers/AWSProvider/scripts/ResourceType_S3Bucket_Action_Delete_Task_DeleteBucket.py @@ -0,0 +1,20 @@ +# flake8: noqa +# pylint: skip-file +# type: ignore + +# script + +# Delete bucket + +import boto3 + +bucket_name = "@@{bucket_name}@@" + +s3_client = boto3.client( + "s3", + aws_access_key_id="@@{access_key_id}@@", + aws_secret_access_key="@@{secret_access_key}@@", +) +response = s3_client.delete_bucket(Bucket=bucket_name) + +print(response) diff --git a/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/runbook.py b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/runbook.py new file mode 100644 index 000000000..571cf3b9d --- /dev/null +++ b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/runbook.py @@ -0,0 +1,530 @@ +# flake8: noqa +# pylint: skip-file +# type: ignore + +import os +from calm.dsl.runbooks import * +from calm.dsl.runbooks import ( + CalmEndpoint as Endpoint, + RunbookTask as CalmTask, + RunbookVariable as CalmVariable, +) +from calm.dsl.builtins import CalmTask as CalmVarTask, Metadata + +# Runbook + + +@runbook +def dsl_vm_create_workflow(): + + rb_vm_name = CalmVariable.Simple( + "MyNewVM", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) # noqa + rb_vlan_id = CalmVariable.Simple( + "1234", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) # noqa + rb_subnet_name = CalmVariable.Simple( + "MyNewSubnet", + label="", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + ) # noqa + + CalmTask.ResourceTypeAction( + "Create Subnet", + Ref.Resource_Type(name="Subnet", provider_name="MyNutanixProvider"), + Ref.ResourceTypeAction( + name="Create Nutanix IPAM Subnet", + resource_type_name="Subnet", + provider_name="MyNutanixProvider", + ), + account_ref=Ref.Account(name="my_nutanix_account"), + inarg_list=[ + CalmVariable.Simple( + "", + label="Description (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + name="description", + ), + CalmVariable.Simple( + "", + label="Boot File Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=False, + description="", + name="boot_file_name", + ), + CalmVariable.Simple( + "", + label="TFTP Server Name (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + name="tftp_server_name", + ), + CalmVariable.Simple( + "", + label="Domain Search List CSV", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter domain search list - ,", + name="domain_search_list", + ), + CalmVariable.Simple( + "", + label="Domain Name (Optional)", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + name="domain_name", + ), + CalmVariable.Simple( + "10.40.64.15,10.40.64.16", + label="DNS List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter comma separated dns servers eg , ", + name="domain_name_server_list", + ), + CalmVariable.Simple( + "10.44.19.126", + label="DHCP Server Address (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + name="dhcp_server_address", + ), + CalmVariable.Simple( + '"10.44.19.66 10.44.19.125"', + label="IP Pool List (Optional)", + is_mandatory=False, + is_hidden=False, + runtime=True, + description='Enter pool list in this format eg." ", " " ', + name="pool_list", + ), + CalmVariable.Simple( + "10.44.19.65", + label="Default Gateway IP", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="", + name="default_gateway_ip", + ), + CalmVariable.Simple( + "10.44.19.64/26", + label="Subnet IP Prefix", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Enter subnet IP with prefix eg 10.10.10.10/24", + name="subnet_ip", + ), + CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "Task_Create Subnet_variable_virtual_switch_uuid_Task_SampleTask.py", + ), + ), + label="Virtual Switch UUID", + is_mandatory=True, + is_hidden=False, + description="", + value="vs0:a16ead9e-7e38-4384-96f1-463ad44440b6", + name="virtual_switch_uuid", + ), + CalmVariable.Simple( + "@@{rb_vlan_id}@@", + label="VLAN ID", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Enter vlan_id", + name="vlan_id", + ), + CalmVariable.Simple( + "@@{rb_subnet_name}@@", + label="Subet Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Enter name of subnet", + name="subnet_name", + ), + CalmVariable.WithOptions.FromTask( + CalmVarTask.Exec.escript.py3( + name="", + filename=os.path.join( + "scripts", + "Task_Create Subnet_variable_pe_cluster_uuid_Task_SampleTask.py", + ), + ), + label="PE Cluster UUID", + is_mandatory=True, + is_hidden=False, + description="Please select cluster", + value="auto_cluster_prod_3603422bb3bb:00061d74-f560-a7e1-0c0a-ac1f6b35f919", + name="pe_cluster_uuid", + ), + ], + output_variables={"create_subnet_status": "task_status"}, + ) + + CalmTask.Exec.escript.py3( + name="Create subnet status", + filename=os.path.join( + "scripts", "_Runbook_vm_create_workflow_Task_Createsubnetstatus.py" + ), + ) + + CalmTask.ResourceTypeAction( + "Create VM", + Ref.Resource_Type(name="VM", provider_name="MyNutanixProvider"), + Ref.ResourceTypeAction( + name="Create", + resource_type_name="VM", + provider_name="MyNutanixProvider", + ), + account_ref=Ref.Account(name="my_nutanix_account"), + inarg_list=[ + CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete", + name="wait", + ), + CalmVariable.Simple.multiline( + "", + label="Categories", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Comma separated names of categories ", + name="categories", + ), + CalmVariable.Simple.multiline( + "", + label="Cloud init", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Cloud init scripts used when creating the VM", + name="cloud_init", + ), + CalmVariable.Simple.multiline( + "", + label="Sys Prep", + regex="^(.|\\n)*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Sys prep scripts used when creating the VM", + name="sys_prep", + ), + CalmVariable.Simple( + "", + label="Subnet", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the subnet to create the VM", + name="subnet", + ), + CalmVariable.Simple( + "", + label="ISO Image", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the iso image needed to create the CD-ROM", + name="iso_image", + ), + CalmVariable.Simple( + "", + label="Disk Image", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Name of the disk image needed to create the disk", + name="disk_image", + ), + CalmVariable.Simple( + "", + label="Storage Container", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="This reference is for disk level storage container preference. This preference specifies the storage container to which this disk belongs", + name="storage_container", + ), + CalmVariable.Simple.int( + "", + label="Size of the disk in Bytes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Size of the disk in Bytes\n\n", + name="disk_size_in_bytes", + ), + CalmVariable.Simple.int( + "1", + label="Number of vCPU sockets", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Number of vCPU sockets\n\n", + name="num_of_sockets", + ), + CalmVariable.Simple.int( + "2", + label="Number of cores per socket", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Number of cores per socket", + name="num_cores_per_socket", + ), + CalmVariable.Simple.int( + "1", + label="Number of threads per core", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Number of threads per core", + name="num_threads_per_core", + ), + CalmVariable.Simple.int( + "2000000000", + label="Memory size in bytes", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Memory size in bytes", + name="memory_size_bytes", + ), + CalmVariable.Simple( + "auto_cluster_prod_3603422bb3bb", + label="Cluster", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Reference to a cluster\n\n", + name="cluster", + ), + CalmVariable.Simple( + "", + label="VM description", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="VM description\n\n", + name="description", + ), + CalmVariable.Simple( + "@@{rb_vm_name}@@", + label="VM Name", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the VM to be created", + name="vm_name", + ), + ], + output_variables={"create_vm_status": "task_status"}, + ) + + CalmTask.Exec.escript.py3( + name="Create VM status", + filename=os.path.join( + "scripts", "_Runbook_vm_create_workflow_Task_CreateVMstatus.py" + ), + ) + + CalmTask.ResourceTypeAction( + "List VMs", + Ref.Resource_Type(name="VM", provider_name="MyNutanixProvider"), + Ref.ResourceTypeAction( + name="List", + resource_type_name="VM", + provider_name="MyNutanixProvider", + ), + account_ref=Ref.Account(name="my_nutanix_account"), + inarg_list=[ + CalmVariable.Simple.int( + "0", + label="Page", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the page number of the result set. It must be a positive integer between 0 and the maximum number of pages that are available for that resource. Any number out of this range might lead to no results", + name="page", + ), + CalmVariable.Simple.int( + "50", + label="Limit", + regex="^[\\d]*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that specifies the total number of records returned in the result set. Must be a positive integer between 1 and 100. Any number out of this range will lead to a validation error. If the limit is not provided, a default value of 50 records will be returned in the result set.", + name="limit", + ), + CalmVariable.Simple( + "name eq '@@{rb_vm_name}@@'", + label="Filter", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to filter a collection of resources. The expression specified with $filter is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response. Expression specified with the $filter must conform to the OData V4.01 URL conventions. For example, filter '$filter=name eq 'karbon-ntnx-1.0' would filter the result on cluster name 'karbon-ntnx1.0', filter '$filter=startswith(name, 'C')' would filter on cluster name starting with 'C'.", + name="filter", + ), + CalmVariable.Simple( + "name", + label="Order By", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to specify the sort criteria for the returned list of objects. Resources can be sorted in ascending order using asc or descending order using desc. If asc or desc are not specified, the resources will be sorted in ascending order by default. For example, '$orderby=templateName desc' would get all templates sorted by templateName in descending order", + name="orderby", + ), + CalmVariable.Simple( + "", + label="Select", + is_mandatory=False, + is_hidden=False, + runtime=True, + description="A URL query parameter that allows clients to request a specific set of properties for each entity or complex type. Expression specified with the $select must conform to the OData V4.01 URL conventions. If a $select expression consists of a single select item that is an asterisk (i.e., *), then all properties on the matching resource will be returned", + name="select", + ), + ], + output_variables={"output_vms": "vms"}, + ) + + CalmTask.SetVariable.escript.py3( + name="Extract ID of new VM", + filename=os.path.join( + "scripts", "_Runbook_vm_create_workflow_Task_ExtractIDofnewVM.py" + ), + variables=["vm_ext_id"], + ) + + CalmTask.ResourceTypeAction( + "Power on VM", + Ref.Resource_Type(name="VM", provider_name="MyNutanixProvider"), + Ref.ResourceTypeAction( + name="Perform Operation", + resource_type_name="VM", + provider_name="MyNutanixProvider", + ), + account_ref=Ref.Account(name="my_nutanix_account"), + inarg_list=[ + CalmVariable.WithOptions( + ["Yes", "No"], + label="Wait", + default="Yes", + regex="^.*$", + validate_regex=False, + is_mandatory=False, + is_hidden=False, + runtime=True, + description="Whether to wait for the operation to complete", + name="wait", + ), + CalmVariable.WithOptions( + [ + "reboot", + "shutdown", + "guest-reboot", + "guest-shutdown", + "power-on", + "power-off", + "power-cycle", + "reset", + ], + label="Action", + default="power-on", + is_mandatory=True, + is_hidden=False, + runtime=True, + description="Name of the action that needs to be performed", + name="action", + ), + CalmVariable.Simple( + "@@{vm_ext_id}@@", + label="VM ExtID", + regex="^.*$", + validate_regex=False, + is_mandatory=True, + is_hidden=False, + runtime=True, + description="The globally unique identifier of a VM", + name="vm_extId", + ), + ], + output_variables={"power_status": "task_status"}, + ) + + CalmTask.Exec.escript.py3( + name="Power on VM status", + filename=os.path.join( + "scripts", "_Runbook_vm_create_workflow_Task_PoweronVMstatus.py" + ), + ) + + +class RunbookMetadata(Metadata): + project = Ref.Project("default") diff --git a/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/Task_Create Subnet_variable_pe_cluster_uuid_Task_SampleTask.py b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/Task_Create Subnet_variable_pe_cluster_uuid_Task_SampleTask.py new file mode 100644 index 000000000..60b110dfc --- /dev/null +++ b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/Task_Create Subnet_variable_pe_cluster_uuid_Task_SampleTask.py @@ -0,0 +1,27 @@ +import requests + +def apiCall(url): + pc_user = '@@{username}@@' + pc_pass = '@@{password}@@' + r = requests.post(api, json=payload, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + +#get pe cluster uuid +api = 'https://@@{pc_server}@@:@@{pc_port}@@/api/nutanix/v3/clusters/list' +payload = { + "kind": "cluster" +} +r = apiCall(api) + +tmp=[] +pe_cluster_uuid = [ cname["metadata"]['uuid'] for cname in r['entities'] if cname['spec']['name'] != "Unnamed"] +for cname in r['entities']: + if cname['spec']['name'] == "Unnamed" : + continue + clustername=cname['spec']['name'] + clusteruuid=cname["metadata"]['uuid'] + tmp.append(clustername+':'+clusteruuid) + +#print("pe_cluster_uuid={}".format(pe_cluster_uuid[0])) +print(','.join(tmp)) \ No newline at end of file diff --git a/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/Task_Create Subnet_variable_virtual_switch_uuid_Task_SampleTask.py b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/Task_Create Subnet_variable_virtual_switch_uuid_Task_SampleTask.py new file mode 100644 index 000000000..402fd7d11 --- /dev/null +++ b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/Task_Create Subnet_variable_virtual_switch_uuid_Task_SampleTask.py @@ -0,0 +1,23 @@ +import requests + +def apiCall(url): + pc_user = '@@{username}@@' + pc_pass = '@@{password}@@' + r = requests.get(url, headers=headers, auth=(pc_user, pc_pass), verify=False) + r = json.loads(r.content) + return r + +#get virtual switch uuid +api = 'https://@@{pc_server}@@:@@{pc_port}@@/api/networking/v4.0.b1/config/virtual-switches' +headers = { + 'x-cluster-id': '@@{pe_cluster_uuid}@@'.strip().split(':')[1], + 'accept': 'application/json', +} + +r = apiCall(api) + +name=r['data'][0]['name'] +uuid=r['data'][0]['extId'] +#print("virtual_switch={}".format(name+':'+uuid)) +print(name+':'+uuid) + diff --git a/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_CreateVMstatus.py b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_CreateVMstatus.py new file mode 100644 index 000000000..7fcd92182 --- /dev/null +++ b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_CreateVMstatus.py @@ -0,0 +1 @@ +print("@@{create_vm_status}@@") diff --git a/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_Createsubnetstatus.py b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_Createsubnetstatus.py new file mode 100644 index 000000000..e1e8ca2ac --- /dev/null +++ b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_Createsubnetstatus.py @@ -0,0 +1 @@ +print("@@{create_subnet_status}@@") diff --git a/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_ExtractIDofnewVM.py b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_ExtractIDofnewVM.py new file mode 100644 index 000000000..e178f7f71 --- /dev/null +++ b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_ExtractIDofnewVM.py @@ -0,0 +1,5 @@ +vms = json.loads(@@{output_vms}@@) + +extId = vms[0]['extId'] + +print(f"vm_ext_id={extId}") \ No newline at end of file diff --git a/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_PoweronVMstatus.py b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_PoweronVMstatus.py new file mode 100644 index 000000000..bd825fd59 --- /dev/null +++ b/examples/custom_providers/VM_create_runbook_using_NutanixPaaS/scripts/_Runbook_vm_create_workflow_Task_PoweronVMstatus.py @@ -0,0 +1 @@ +print("@@{power_status}@@") diff --git a/examples/custom_providers/scripts/verify_script.py b/examples/custom_providers/scripts/verify_script.py new file mode 100644 index 000000000..133da5af2 --- /dev/null +++ b/examples/custom_providers/scripts/verify_script.py @@ -0,0 +1,6 @@ +# Dummy script to verify credentials of custom provider accounts + +print("Account credentials being validated:") +print("Username: @@{username}@@, Password: @@{password}@@") +print("Making a dummy request to authenticate to the below provider endpoint") +print("Server IP: @@{server_ip}@@, Port: @@{port_number}@@") diff --git a/examples/custom_providers/test_custom_provider_account.py b/examples/custom_providers/test_custom_provider_account.py new file mode 100644 index 000000000..c4a1209f5 --- /dev/null +++ b/examples/custom_providers/test_custom_provider_account.py @@ -0,0 +1,20 @@ +from calm.dsl.builtins import Account, AccountResources, Ref + +# Provider with name "HelloProvider" should already exist +PROVIDER_NAME = "HelloProvider" + + +class HelloAccount(Account): + """Account corresponding to HelloProvider""" + + type = PROVIDER_NAME + resources = AccountResources.CustomProvider( + provider=Ref.Provider(PROVIDER_NAME), + variable_dict={ # Keys of this dict are variable names from the list of provider's [auth_schema + endpoint_schema] + "server_ip": "10.10.01.231", + "port_number": "9440", + "username": "uzumaki.naruto", + "password": "rasengannnn", + "provider_var": "new_val", + }, + ) diff --git a/examples/custom_providers/test_provider.py b/examples/custom_providers/test_provider.py new file mode 100644 index 000000000..625895637 --- /dev/null +++ b/examples/custom_providers/test_provider.py @@ -0,0 +1,121 @@ +""" +Sample Calm DSL for Hello provider +""" + +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER, RESOURCE_TYPE +from calm.dsl.runbooks import RunbookTask as Task +from calm.dsl.builtins import ( + CalmVariable, + CloudProvider, + action, + ResourceType, + ProviderEndpointSchema, + ProviderTestAccount, +) + + +class HelloResourceType(ResourceType): + """Sample ResourceType""" + + # Variables + variables = [ + CalmVariable.Simple.string(name="resource_type_var", value="value"), + ] + + # Schemas + schemas = [ + CalmVariable.Simple.string(name="resource_kind", value="compute"), + ] + + # Actions + @action + def Create(type=RESOURCE_TYPE.ACTION_TYPE.CREATE): + """Create Action for HelloResourceType""" + input_var = CalmVariable.Simple.string("default_val", is_mandatory=True) + outputs = [CalmVariable.Simple.string(name="output_var", value="")] + Task.Exec.escript( + name="Create Resource", + script="print ('Creating an instance of HelloResourceType');print('@@{input_var}@@')", + ) + Task.SetVariable.escript( + name="Set Outputs", + variables=["output_var"], + script="print ('output_var = out_val')", + ) + Task.Exec.escript(name="Verify Outputs", script="print('@@{output_var}@@')") + + @action + def Delete(type=RESOURCE_TYPE.ACTION_TYPE.DELETE): + """Delete Action for HelloResourceType""" + Task.Exec.escript( + name="Delete Resource", + script="print ('Deleting an instance of HelloResourceType')", + ) + + @action + def List(type=RESOURCE_TYPE.ACTION_TYPE.LIST): + """List Action for HelloResourceType""" + outputs = [CalmVariable.Simple.string(name="resource_ids", value="")] + Task.SetVariable.escript( + name="List Resources", + variables=["resource_ids"], + script="print (\"resource_ids = ['resource1', 'resource2']\")", + ) + + +class HelloProvider(CloudProvider): + """Sample provider for Hello""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Resource Types to be managed under this Provider + resource_types = [HelloResourceType] + + # Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value="", is_mandatory=True), + CalmVariable.Simple.Secret.string(name="password", value="", is_mandatory=True), + ] + + # Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string( + name="server_ip", value="1.1.1.1", is_mandatory=True + ), + CalmVariable.Simple.int(name="port_number", value="443", is_mandatory=True), + ], + ) + + # Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] + + test_account = ProviderTestAccount( + name="TestHelloAccount", + description="Used for test executions", + variables=[ + CalmVariable.Simple.string( + name="server_ip", value="10.10.10.10", is_mandatory=True + ), + CalmVariable.Simple.int( + name="port_number", value="9440", is_mandatory=True + ), + CalmVariable.Simple.string( + name="username", value="root", is_mandatory=True + ), + CalmVariable.Simple.Secret.string( + name="password", value="iamasecret", is_mandatory=True + ), + ], + ) + + @action + def Verify(): + """Verify action for Provider""" + Task.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py") + Task.Exec.escript( + name="PrintSuccessMessage", script="print ('Successfully Authenticated')" + ) diff --git a/examples/custom_providers/test_runbook.py b/examples/custom_providers/test_runbook.py new file mode 100644 index 000000000..9f0462355 --- /dev/null +++ b/examples/custom_providers/test_runbook.py @@ -0,0 +1,30 @@ +from calm.dsl.builtins import Ref, CalmVariable +from calm.dsl.runbooks import RunbookTask as Task, RunbookVariable, runbook + +# Provider with name "HelloProvider" & ResourceType with name "HelloResourceType" should already exist +PROVIDER_NAME = "HelloProvider" +RESOURCE_TYPE_NAME = "HelloResourceType" + + +@runbook +def Hello(): + runbook_var = RunbookVariable.Simple.string( + value="user_val", is_mandatory=True, runtime=True + ) + Task.ResourceTypeAction( + "CustomProviderTask", + Ref.Resource_Type(name=RESOURCE_TYPE_NAME, provider_name=PROVIDER_NAME), + account_ref=Ref.Account(name="HelloAccount"), + action_ref=Ref.ResourceTypeAction( + name="Create", + resource_type_name=RESOURCE_TYPE_NAME, + provider_name=PROVIDER_NAME, + ), + inarg_list=[ + CalmVariable.Simple.string( + name="input_var", value="@@{runbook_var}@@", is_mandatory=True + ) + ], + output_variables={"myoutput": "output_var"}, + ) + Task.Exec.escript(name="Verify mapped outputs", script="print('@@{myoutput}@@')") diff --git a/metadata.json b/metadata.json index e33f01c6d..e36f13c0e 100644 --- a/metadata.json +++ b/metadata.json @@ -1,3 +1,3 @@ { - "version": "3.8.1" + "version": "4.0.0" } \ No newline at end of file diff --git a/release-notes/4.0.0/README.md b/release-notes/4.0.0/README.md new file mode 100644 index 000000000..ef0dc5793 --- /dev/null +++ b/release-notes/4.0.0/README.md @@ -0,0 +1,34 @@ + +# Major Feats + +1. Cloud Provider Framework in Self-Service: Added support to configure cloud providers to provision and manage any workloads on the supported private and public cloud. The configuration involves defining the schema, resource types, actions, and so on. You can then consume the configured cloud provider in a runbook task. For more details go through following pages: + - [Getting started on Providers](https://www.nutanix.dev/docs/self-service-dsl/getting-started/providers/) + - [Understanding Provider model in DSL](https://www.nutanix.dev/docs/self-service-dsl/models/Provider/) + - [Resource Type model in DSL](https://www.nutanix.dev/docs/self-service-dsl/models/ResourceType/) + - [Provider Task model in DSL](https://www.nutanix.dev/docs/self-service-dsl/models/Task/providertask/) + +# Improvements + +- Error Handling in Runbooks and Blueprints: Added `status_map_list` param at Task level which will enable to continue with the execution of the runbook or blueprint workflow on task failure, instead of failing the entire workflow based on an individual task status. The failed tasks are marked with a Warning status to proceed to the subsequent task in the workflow. For more details read [here](https://www.nutanix.dev/docs/self-service-dsl/models/Task/runbooktask/#error-handling-in-runbooktask) + +- Output Variables at the Runbook Level: Added support to add output variables at the Runbook level so that the task variables that is set as output in individual tasks (such as HTTP, Set Variable and so on) are +available for viewing and for external consumption. For more details read [here](https://www.nutanix.dev/docs/self-service-dsl/getting-started/runbooks/#output-variables-in-runbook) + +- Clone a runbook: Added `calm clone runbook` commmand to clone a runbook in Self-Service. For more details go through `Cloning Runbook` section [here](https://www.nutanix.dev/docs/self-service-dsl/getting-started/runbooks/#cloning-runbook) + +- Publish Blueprint to the Marketplace Without Platform-Dependent Configuration: Added `--without-platform-data/-wp` flag in `calm publish bp` command to clear all the platform-dependent fields in Blueprint VM configuration before publishing the blueprint to the marketplace. Clearing the platform-dependent fields allows Self-Service to populate VM configuration fields from the VM configurations of the environment you select during the launch of the Marketplace blueprint. For more details read about publishing without platform dependent fields [here](https://www.nutanix.dev/docs/self-service-dsl/getting-started/marketplace_item/#publishing-blueprint-to-marketplace-manager-without-platform-dependent-fields) + +- `status_mapping` param used for response code mapping in the HTTP Task will be deprecated: Use `response_code_status_map` param for mapping HTTP response codes to task statuses. For more details read how to use `response_code_status_map` in [CalmTask](https://www.nutanix.dev/docs/self-service-dsl/models/Task/calmtask/#response-code-range-mapping-in-http-calmtask) and [RunbookTask](https://www.nutanix.dev/docs/self-service-dsl/models/Task/runbooktask/#response-code-range-mapping-in-http-runbooktask). + +- Removed use of centos disk image from blueprint generated from `calm init bp` command. From now on, an empty placeholder will be generated in blueprint. Fill valid disk image according to your usecase. For more details read [here](https://www.nutanix.dev/docs/self-service-dsl/tutorial/rocky_image_in_blueprint/) + +- Updated `calm describe app` command to display more details about applications of AHV, VMware, AWS, Azure, GCP providers. + +- [#229](https://github.com/nutanix/calm-dsl/issues/229) Added `windev` command in DSL Makefile. Use `make windev` to build DSL on Windows machine. + +# Bug Fixes + +- Block blueprint launch if blueprint has snapshot configs but no protection policy data when user passes `--ignore_runtime_variables/-i` flag + +- Fix missing hyphen(-) in endpoint name while decompiling a bluperint or runbook. + diff --git a/release_config.json b/release_config.json index f47086dc3..90f4106a9 100644 --- a/release_config.json +++ b/release_config.json @@ -2,7 +2,7 @@ "releases": [ { "product": "calm-dsl", - "version": "3.8.1", + "version": "4.0.0", "exclude": [ "" ], diff --git a/tests/3_2_0/project/test_projects_env.py b/tests/3_2_0/project/test_projects_env.py index 4731e14bd..67b72a882 100644 --- a/tests/3_2_0/project/test_projects_env.py +++ b/tests/3_2_0/project/test_projects_env.py @@ -67,6 +67,7 @@ LV(CALM_VERSION) < LV("3.2.0"), reason="Tests are for env changes introduced in 3.2.0", ) +@pytest.mark.quotas class TestProjectEnv: def setup_method(self): """Method to instantiate variable for projects to be deleted""" diff --git a/tests/ahv_vm_overlay_subnet/test_overlay_subnet_blueprint.json b/tests/ahv_vm_overlay_subnet/test_overlay_subnet_blueprint.json index cc04b64ec..eb0c9354b 100644 --- a/tests/ahv_vm_overlay_subnet/test_overlay_subnet_blueprint.json +++ b/tests/ahv_vm_overlay_subnet/test_overlay_subnet_blueprint.json @@ -38,6 +38,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -71,6 +72,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -104,6 +106,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -137,6 +140,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -170,6 +174,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -203,6 +208,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -247,6 +253,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -273,6 +280,7 @@ "script_type": "sh", "script": "echo \"Hello\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -290,6 +298,7 @@ "script_type": "sh", "script": "echo \"Hello again\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -328,6 +337,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -355,6 +365,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -411,8 +422,8 @@ }, "cluster_reference": { "kind": "cluster", - "name": "auto_cluster_prod_4fee9603c584", - "uuid": "0005e1e4-a1d9-6e40-3d2a-00e0ed87fc48" + "name": "auto_cluster_prod_364563e22ade", + "uuid": "00061bf1-a3c2-2890-32b9-ac1f6b6f97aa" }, "resources": { "nic_list": [ @@ -422,14 +433,14 @@ "subnet_reference": { "kind": "subnet", "name": "vpc_subnet_1", - "uuid": "4fd66b65-d9a3-4b00-80cb-59fc148519df" + "uuid": "07027cb4-92f3-4a14-a689-f5060027f525" }, "network_function_chain_reference": null, "mac_address": "", "ip_endpoint_list": [], "vpc_reference": { "name": "vpc_name_1", - "uuid": "4d479b14-cc11-4344-9beb-269ac4a21c29", + "uuid": "49466fab-0237-441d-be30-2fe7edd51a09", "kind": "vpc" } } @@ -438,7 +449,7 @@ "num_sockets": 2, "memory_size_mib": 4096, "power_state": "ON", - "account_uuid": "24b46e0f-ef08-4e63-b25d-68cea9af5720", + "account_uuid": "e653631b-2880-4a07-8321-f0527f671de5", "gpu_list": [], "disk_list": [ { @@ -500,6 +511,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -522,6 +534,7 @@ "script_type": "static_py3", "script": "print ('Pre Create task runs before VM is created')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -555,6 +568,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -577,6 +591,7 @@ "script_type": "static_py3", "script": "print ('Post delete task runs after VM is deleted')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -652,49 +667,6 @@ "restore_config_list": [] } ], - "client_attrs": { - "None": { - "Service": { - "HelloService": { - "dsl_name": "HelloService", - "Action": {} - } - }, - "Package": { - "centos_disk": { - "dsl_name": "centos_disk", - "Action": {} - }, - "HelloPackage": { - "dsl_name": "HelloPackage", - "Action": {} - } - }, - "Deployment": { - "HelloDeployment": { - "dsl_name": "HelloDeployment", - "Action": {} - } - }, - "Profile": { - "HelloProfile": { - "dsl_name": "HelloProfile", - "Action": {} - } - }, - "Substrate": { - "HelloSubstrate": { - "dsl_name": "HelloSubstrate", - "Action": {}, - "AhvVm": { - "HelloVm": { - "dsl_name": "HelloVm" - } - } - } - } - } - }, "default_credential_local_reference": { "kind": "app_credential", "name": "Centos" diff --git a/tests/ahv_vm_overlay_subnet/test_overlay_subnet_bp.py b/tests/ahv_vm_overlay_subnet/test_overlay_subnet_bp.py index 80cff0bd7..433305f0c 100644 --- a/tests/ahv_vm_overlay_subnet/test_overlay_subnet_bp.py +++ b/tests/ahv_vm_overlay_subnet/test_overlay_subnet_bp.py @@ -13,6 +13,8 @@ from calm.dsl.cli import main as cli from calm.dsl.log import get_logging_handle from tests.cli.runtime_helpers.ahv.editable_params import DSL_CONFIG +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp # Setting the recursion limit to max for sys.setrecursionlimit(100000) @@ -181,4 +183,10 @@ def test_compile(self): "uuid" ] + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json) + + remove_output_variables_from_bp(known_json) + remove_output_variables_from_bp(generated_json) + assert sorted(known_json.items()) == sorted(generated_json.items()) diff --git a/tests/api_interface/test_apps.py b/tests/api_interface/test_apps.py index 25e211ebc..5245bc140 100644 --- a/tests/api_interface/test_apps.py +++ b/tests/api_interface/test_apps.py @@ -10,11 +10,14 @@ from tests.api_interface.entity_spec.existing_vm_bp import ( ExistingVMBlueprint as Blueprint, ) +from tests.utils import Application as ApplicationHelper LOG = get_logging_handle(__name__) class TestApps: + app_helper = ApplicationHelper() + def test_apps_list(self): client = get_api_client() @@ -194,7 +197,7 @@ def test_apps_api(self): "action {} is not completed in 5 minutes".format(action_name) ) # let's wait for few seconds before delete - time.sleep(5) + self.app_helper._wait_for_non_busy_state(app_name) LOG.info("Deleting application {}".format(app_name)) res, err = client.application.delete(app_uuid) if err: diff --git a/tests/api_interface/test_runbooks/test_http_task.py b/tests/api_interface/test_runbooks/test_http_task.py index 81b464b6a..ff4270d93 100644 --- a/tests/api_interface/test_runbooks/test_http_task.py +++ b/tests/api_interface/test_runbooks/test_http_task.py @@ -293,9 +293,6 @@ def test_http_incorrect_response_code(self): (HTTPTaskWithIncorrectAuth, "AUTHENTICATION_REQUIRED"), ], ) - @pytest.mark.skip( - reason="ENG-626272, v3 pc api returns diff payload between release versions" - ) def test_http_failure_scenarios(self, Helper): """test_http_task_failure_status_code_check, test_unsupported_payload_json, diff --git a/tests/api_interface/test_runbooks/test_vm_actions.py b/tests/api_interface/test_runbooks/test_vm_actions.py index df90a632c..3c084c072 100644 --- a/tests/api_interface/test_runbooks/test_vm_actions.py +++ b/tests/api_interface/test_runbooks/test_vm_actions.py @@ -66,7 +66,10 @@ def test_power_off_action(self, Runbook, warning_msg): ) print(">> Runbook Run state: {}\n{}".format(state, reasons)) - assert state == RUNLOG.STATUS.ERROR + if LV(CALM_VERSION) >= LV("4.0.0"): + assert state == RUNLOG.STATUS.FAILURE + else: + assert state == RUNLOG.STATUS.ERROR res, err = client.runbook.list_runlogs(runlog_uuid) if err: @@ -83,7 +86,10 @@ def test_power_off_action(self, Runbook, warning_msg): for reason in entity["status"]["reason_list"]: reasons += reason assert warning_msg in reasons - assert entity["status"]["state"] == RUNLOG.STATUS.ERROR + if LV(CALM_VERSION) >= LV("4.0.0"): + assert entity["status"]["state"] == RUNLOG.STATUS.FAILURE + else: + assert entity["status"]["state"] == RUNLOG.STATUS.ERROR elif entity["status"]["type"] == "task_runlog" and runlog_uuid in entity[ "status" ].get("machine_name", ""): diff --git a/tests/api_interface/test_runbooks/test_vm_endpoints_failures.py b/tests/api_interface/test_runbooks/test_vm_endpoints_failures.py index f5b85dd4e..b186428ac 100644 --- a/tests/api_interface/test_runbooks/test_vm_endpoints_failures.py +++ b/tests/api_interface/test_runbooks/test_vm_endpoints_failures.py @@ -51,7 +51,10 @@ def test_warnings_on_vm_endpoint(self, Runbook, warning_msg): res_json = upload_runbook( client, rb_name, Runbook, return_error_response=True ) - assert res_json["code"] == 403 + if LV(CALM_VERSION) >= LV("4.0.0"): + assert res_json["code"] == 422 + else: + assert res_json["code"] == 403 assert res_json["message_list"][0]["reason"] == "ACCESS_DENIED" return rb = upload_runbook(client, rb_name, Runbook) @@ -81,7 +84,10 @@ def test_warnings_on_vm_endpoint(self, Runbook, warning_msg): ) print(">> Runbook Run state: {}\n{}".format(state, reasons)) - assert state == RUNLOG.STATUS.ERROR + if LV(CALM_VERSION) >= LV("4.0.0"): + assert state == RUNLOG.STATUS.FAILURE + else: + assert state == RUNLOG.STATUS.ERROR # Finding the trl id for the shell and escript task (all runlogs for multiple IPs) escript_tasks = [] @@ -100,7 +106,10 @@ def test_warnings_on_vm_endpoint(self, Runbook, warning_msg): for reason in entity["status"]["reason_list"]: reasons += reason assert warning_msg in reasons - assert entity["status"]["state"] == RUNLOG.STATUS.ERROR + if LV(CALM_VERSION) >= LV("4.0.0"): + assert entity["status"]["state"] == RUNLOG.STATUS.FAILURE + else: + assert entity["status"]["state"] == RUNLOG.STATUS.ERROR elif ( entity["status"]["type"] == "task_runlog" and entity["status"]["task_reference"]["name"] == "EscriptTask" @@ -153,7 +162,10 @@ def test_failures_on_vm_endpoint(self, Runbook, warning_msg): res_json = upload_runbook( client, rb_name, Runbook, return_error_response=True ) - assert res_json["code"] == 403 + if LV(CALM_VERSION) >= LV("4.0.0"): + assert res_json["code"] == 422 + else: + assert res_json["code"] == 403 assert res_json["message_list"][0]["reason"] == "ACCESS_DENIED" return @@ -189,7 +201,10 @@ def test_failures_on_vm_endpoint(self, Runbook, warning_msg): ) print(">> Runbook Run state: {}\n{}".format(state, reasons)) - assert state == RUNLOG.STATUS.ERROR + if LV(CALM_VERSION) >= LV("4.0.0"): + assert state == RUNLOG.STATUS.FAILURE + else: + assert state == RUNLOG.STATUS.ERROR # Finding the trl id for the shell and escript task (all runlogs for multiple IPs) escript_tasks = [] @@ -208,7 +223,10 @@ def test_failures_on_vm_endpoint(self, Runbook, warning_msg): for reason in entity["status"]["reason_list"]: reasons += reason assert warning_msg in reasons - assert entity["status"]["state"] == RUNLOG.STATUS.ERROR + if LV(CALM_VERSION) >= LV("4.0.0"): + assert entity["status"]["state"] == RUNLOG.STATUS.FAILURE + else: + assert entity["status"]["state"] == RUNLOG.STATUS.ERROR elif ( entity["status"]["type"] == "task_runlog" and entity["status"]["task_reference"]["name"] == "EscriptTask" diff --git a/tests/cli/endpoints/http_endpoint.py b/tests/cli/endpoints/http_endpoint.py new file mode 100644 index 000000000..45a1cbfae --- /dev/null +++ b/tests/cli/endpoints/http_endpoint.py @@ -0,0 +1,16 @@ +import json +from calm.dsl.runbooks import read_local_file +from calm.dsl.runbooks import CalmEndpoint as Endpoint +from calm.dsl.builtins.models.metadata import Metadata +from calm.dsl.builtins.models.calm_ref import Ref + +DSL_CONFIG = json.loads(read_local_file(".tests/config.json")) + +DEFAULT_PROJECT = DSL_CONFIG["PROJECTS"]["PROJECT1"]["NAME"] + + +HttpEndpoint = Endpoint.HTTP(url="https://jsonplaceholder.typicode.com") + + +class EndpointMetadata(Metadata): + project = Ref.Project(DEFAULT_PROJECT) diff --git a/tests/cli/runtime_helpers/ahv/app_edit_blueprint.json b/tests/cli/runtime_helpers/ahv/app_edit_blueprint.json index 03a928147..c0db304d6 100644 --- a/tests/cli/runtime_helpers/ahv/app_edit_blueprint.json +++ b/tests/cli/runtime_helpers/ahv/app_edit_blueprint.json @@ -1,732 +1,748 @@ { - "type": "USER", - "service_definition_list": [ - { - "name": "AhvService", - "description": "Sample mysql service", - "port_list": [], - "singleton": false, - "tier": "", - "depends_on_list": [], - "variable_list": [ + "type": "USER", + "service_definition_list": [ { - "name": "ENV", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "DEV", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - } - ], - "action_list": [ - { - "name": "action_create", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___create___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___create___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___create___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } + "name": "AhvService", + "description": "Sample mysql service", + "port_list": [], + "singleton": false, + "tier": "", + "depends_on_list": [], + "variable_list": [ + { + "name": "ENV", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "DEV", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } ], - "variable_list": [] - } - }, - { - "name": "action_start", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___start___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___start___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___start___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" + "action_list": [ + { + "name": "action_create", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___create___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___create___dag" + }, + "task_definition_list": [ + { + "name": "AhvService___create___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + } }, - "attrs": { - "edges": [] + { + "name": "action_start", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___start___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___start___dag" + }, + "task_definition_list": [ + { + "name": "AhvService___start___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + } }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - { - "name": "action_stop", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___stop___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___stop___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___stop___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" + { + "name": "action_stop", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___stop___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___stop___dag" + }, + "task_definition_list": [ + { + "name": "AhvService___stop___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + } }, - "attrs": { - "edges": [] + { + "name": "action_delete", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___delete___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___delete___dag" + }, + "task_definition_list": [ + { + "name": "AhvService___delete___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + } }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - { - "name": "action_delete", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___delete___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___delete___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___delete___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] + { + "name": "action_restart", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___restart___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___restart___dag" + }, + "task_definition_list": [ + { + "name": "AhvService___restart___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + } }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } + { + "name": "action_soft_delete", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___soft_delete___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___soft_delete___dag" + }, + "task_definition_list": [ + { + "name": "AhvService___soft_delete___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + } + } ], - "variable_list": [] - } - }, + "container_spec": {} + } + ], + "package_definition_list": [ { - "name": "action_restart", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___restart___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___restart___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___restart___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] + "name": "AhvPackage", + "description": "Example package with variables, install tasks and link to service", + "type": "CUSTOM", + "options": { + "install_runbook": { + "name": "Runbook_for_Package_AhvPackage_action_install", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "DAG_Task_for_Package_AhvPackage_action_install" + }, + "task_definition_list": [ + { + "name": "DAG_Task_for_Package_AhvPackage_action_install", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - { - "name": "action_soft_delete", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___soft_delete___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___soft_delete___dag" + "uninstall_runbook": { + "name": "Runbook_for_Package_AhvPackage_action_uninstall", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "DAG_Task_for_Package_AhvPackage_action_uninstall" + }, + "task_definition_list": [ + { + "name": "DAG_Task_for_Package_AhvPackage_action_uninstall", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + } }, - "task_definition_list": [ - { - "name": "AhvService___soft_delete___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } + "service_local_reference_list": [ + { + "kind": "app_service", + "name": "AhvService" + } ], - "variable_list": [] - } - } - ], - "container_spec": {} - } - ], - "package_definition_list": [ - { - "name": "AhvPackage", - "description": "Example package with variables, install tasks and link to service", - "type": "CUSTOM", - "options": { - "install_runbook": { - "name": "Runbook_for_Package_AhvPackage_action_install", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "DAG_Task_for_Package_AhvPackage_action_install" - }, - "task_definition_list": [ - { - "name": "DAG_Task_for_Package_AhvPackage_action_install", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - }, - "uninstall_runbook": { - "name": "Runbook_for_Package_AhvPackage_action_uninstall", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "DAG_Task_for_Package_AhvPackage_action_uninstall" - }, - "task_definition_list": [ - { - "name": "DAG_Task_for_Package_AhvPackage_action_uninstall", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - "service_local_reference_list": [ - { - "kind": "app_service", - "name": "AhvService" + "variable_list": [ + { + "name": "foo", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "bar", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "version": "" } - ], - "variable_list": [ + ], + "published_service_definition_list": [], + "substrate_definition_list": [ { - "name": "foo", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "bar", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - } - ], - "version": "" - } - ], - "published_service_definition_list": [], - "substrate_definition_list": [ - { - "name": "AhvSubstrate", - "description": "AHV VM config given by reading a spec file", - "type": "AHV_VM", - "os_type": "Linux", - "create_spec": { - "name": "MyAhvVm", - "categories": { - "AppFamily": "Backup", - "AppType": "Default" - }, - "resources": { - "nic_list": [ - { - "network_function_nic_type": "INGRESS", - "nic_type": "NORMAL_NIC", - "subnet_reference": { - "kind": "subnet", - "name": "vlan1211", - "uuid": "48728e67-d4af-46ca-a80e-aa571b18209f" - }, - "network_function_chain_reference": null, - "mac_address": "", - "ip_endpoint_list": [] - }, - { - "network_function_nic_type": "INGRESS", - "nic_type": "NORMAL_NIC", - "subnet_reference": { - "kind": "subnet", - "name": "vlan1211", - "uuid": "48728e67-d4af-46ca-a80e-aa571b18209f" - }, - "network_function_chain_reference": null, - "mac_address": "", - "ip_endpoint_list": [] - }, - { - "network_function_nic_type": "INGRESS", - "nic_type": "NORMAL_NIC", - "subnet_reference": { - "kind": "subnet", - "name": "vlan1211", - "uuid": "48728e67-d4af-46ca-a80e-aa571b18209f" - }, - "network_function_chain_reference": null, - "mac_address": "", - "ip_endpoint_list": [] - } - ], - "num_vcpus_per_socket": 1, - "num_sockets": 1, - "memory_size_mib": 4096, - "power_state": "ON", - "account_uuid": "cccf2e43-ecc4-4c5c-bb58-862fef428395", - "gpu_list": [], - "disk_list": [ - { - "data_source_reference": { - "kind": "image", - "name": "CentOS-7-cloudinit", - "uuid": "f3bc3369-c056-4a2c-8308-90d1b76d455a" - }, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 0, - "adapter_type": "SCSI" - } - }, - "disk_size_mib": 0 - }, - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 0, - "adapter_type": "PCI" + "name": "AhvSubstrate", + "description": "AHV VM config given by reading a spec file", + "type": "AHV_VM", + "os_type": "Linux", + "create_spec": { + "name": "MyAhvVm", + "categories": { + "AppFamily": "Backup", + "AppType": "Default" + }, + "resources": { + "nic_list": [ + { + "network_function_nic_type": "INGRESS", + "nic_type": "NORMAL_NIC", + "subnet_reference": { + "kind": "subnet", + "name": "vlan1211", + "uuid": "c1a401fb-5724-4b0a-bfbb-be86e6793ebe" + }, + "network_function_chain_reference": null, + "mac_address": "", + "ip_endpoint_list": [] + }, + { + "network_function_nic_type": "INGRESS", + "nic_type": "NORMAL_NIC", + "subnet_reference": { + "kind": "subnet", + "name": "vlan1211", + "uuid": "c1a401fb-5724-4b0a-bfbb-be86e6793ebe" + }, + "network_function_chain_reference": null, + "mac_address": "", + "ip_endpoint_list": [] + }, + { + "network_function_nic_type": "INGRESS", + "nic_type": "NORMAL_NIC", + "subnet_reference": { + "kind": "subnet", + "name": "vlan1211", + "uuid": "c1a401fb-5724-4b0a-bfbb-be86e6793ebe" + }, + "network_function_chain_reference": null, + "mac_address": "", + "ip_endpoint_list": [] + } + ], + "num_vcpus_per_socket": 1, + "num_sockets": 1, + "memory_size_mib": 4096, + "power_state": "ON", + "account_uuid": null, + "gpu_list": [], + "disk_list": [ + { + "data_source_reference": { + "kind": "image", + "name": "CentOS-7-cloudinit", + "uuid": null + }, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 0, + "adapter_type": "SCSI" + } + }, + "disk_size_mib": 0 + }, + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 0, + "adapter_type": "PCI" + } + }, + "disk_size_mib": 4096 + }, + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 1, + "adapter_type": "PCI" + } + }, + "disk_size_mib": 6144 + }, + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 2, + "adapter_type": "PCI" + } + }, + "disk_size_mib": 8192 + } + ], + "guest_customization": null, + "serial_port_list": [ + { + "index": 0, + "is_connected": false + }, + { + "index": 1, + "is_connected": false + }, + { + "index": 2, + "is_connected": true + }, + { + "index": 3, + "is_connected": true + } + ], + "boot_config": { + "boot_device": { + "disk_address": { + "device_index": 0, + "adapter_type": "SCSI" + } + } + } } - }, - "disk_size_mib": 4096 }, - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 1, - "adapter_type": "PCI" + "variable_list": [], + "action_list": [], + "readiness_probe": { + "connection_type": "SSH", + "connection_port": 22, + "connection_protocol": "", + "timeout_secs": "60", + "delay_secs": "60", + "retries": "5", + "address": "@@{platform.status.resources.nic_list[0].ip_endpoint_list[0].ip}@@", + "disable_readiness_probe": true, + "login_credential_local_reference": { + "kind": "app_credential", + "name": "default cred" } - }, - "disk_size_mib": 6144 }, - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 2, - "adapter_type": "PCI" + "editables": { + "create_spec": { + "resources": { + "num_vcpus_per_socket": true, + "num_sockets": true, + "memory_size_mib": true, + "boot_config": true, + "guest_customization": true, + "disk_list": { + "0": { + "data_source_reference": true, + "disk_size_mib": true + }, + "1": { + "data_source_reference": true, + "device_properties": { + "device_type": true, + "disk_address": { + "adapter_type": true + } + }, + "disk_size_mib": true + } + }, + "nic_list": { + "0": { + "subnet_reference": true + } + }, + "serial_port_list": { + "0": { + "is_connected": true + }, + "1": { + "is_connected": true + } + } + }, + "name": true, + "categories": true + }, + "readiness_probe": { + "connection_port": true, + "retries": true } - }, - "disk_size_mib": 8192 } - ], - "guest_customization": null, - "serial_port_list": [ - { - "index": 0, - "is_connected": false - }, - { - "index": 1, - "is_connected": false - }, - { - "index": 2, - "is_connected": true - }, - { - "index": 3, - "is_connected": true - } - ], - "boot_config": { - "boot_device": { - "disk_address": { - "device_index": 0, - "adapter_type": "SCSI" - } - } - } - } - }, - "variable_list": [], - "action_list": [], - "readiness_probe": { - "connection_type": "SSH", - "connection_port": 22, - "connection_protocol": "", - "timeout_secs": "60", - "delay_secs": "60", - "retries": "5", - "address": "@@{platform.status.resources.nic_list[0].ip_endpoint_list[0].ip}@@", - "disable_readiness_probe": true, - "login_credential_local_reference": { - "kind": "app_credential", - "name": "default cred" } - }, - "editables": { - "create_spec": { - "resources": { - "num_vcpus_per_socket": true, - "num_sockets": true, - "memory_size_mib": true, - "boot_config": true, - "guest_customization": true, - "disk_list": { - "0": { - "data_source_reference": true, - "disk_size_mib": true - }, - "1": { - "data_source_reference": true, - "device_properties": { - "device_type": true, - "disk_address": { - "adapter_type": true - } + ], + "credential_definition_list": [ + { + "name": "default cred", + "description": "", + "type": "PASSWORD", + "username": "root", + "secret": { + "attrs": { + "is_secret_modified": true }, - "disk_size_mib": true - } + "value": "passwd" }, - "nic_list": { - "0": { - "subnet_reference": true - } - }, - "serial_port_list": { - "0": { - "is_connected": true - }, - "1": { - "is_connected": true - } + "editables": { + "username": true, + "secret": true } - }, - "name": true, - "categories": true - }, - "readiness_probe": { - "connection_port": true, - "retries": true } - } - } - ], - "credential_definition_list": [ - { - "name": "default cred", - "description": "", - "type": "PASSWORD", - "username": "root", - "secret": { - "attrs": { - "is_secret_modified": true - }, - "value": "passwd" - }, - "editables": { - "username": true, - "secret": true - } - } - ], - "app_profile_list": [ - { - "name": "DefaultProfile", - "deployment_create_list": [ - { - "published_service_local_reference_list": [], - "package_local_reference_list": [ - { - "kind": "app_package", - "name": "AhvPackage" - } - ], - "substrate_local_reference": { - "kind": "app_substrate", - "name": "AhvSubstrate" - }, - "depends_on_list": [], - "variable_list": [], - "action_list": [], - "min_replicas": "1", - "default_replicas": "", - "max_replicas": "1", - "type": "GREENFIELD", - "name": "AhvDeployment", - "options": {}, - "description": "Sample deployment pulling in service and substrate references", - "editables": { - "min_replicas": true, - "default_replicas": true, - "max_replicas": true - } - } - ], - "variable_list": [ - { - "name": "nameserver", - "description": "", - "type": "LOCAL", - "label": "Local DNS resolver", - "attrs": {}, - "val_type": "STRING", - "value": "10.40.64.15", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "foo1", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "bar1", - "data_type": "BASE", - "editables": { - "value": true - }, - "is_hidden": false, - "is_mandatory": false - }, + ], + "app_profile_list": [ { - "name": "foo2", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "bar2", - "data_type": "BASE", - "editables": { - "value": true - }, - "is_hidden": false, - "is_mandatory": false - } - ], - "patch_list": [ - { - "name": "patch_update", - "description": "", - "type": "PATCH", - "variable_list": [], - "config_reference_list": [], - "attrs_list": [ - { - "target_any_local_reference": { - "kind": "app_blueprint_deployment", - "name": "AhvDeployment" - }, - "data": { - "type": "nutanix", - "nic_delete_allowed": true, - "categories_delete_allowed": true, - "categories_add_allowed": true, - "disk_delete_allowed": true, - "num_sockets_ruleset": { - "type": "", - "operation": "equal", - "editable": true, - "value": "2", - "max_value": 10, - "min_value": 1 - }, - "memory_size_mib_ruleset": { - "type": "", - "operation": "equal", - "editable": true, - "value": "2048", - "max_value": 10240, - "min_value": 1024 - }, - "num_vcpus_per_socket_ruleset": { - "type": "", - "operation": "equal", - "editable": true, - "value": "2", - "max_value": 10, - "min_value": 1 - }, - "pre_defined_disk_list": [ - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 0, - "adapter_type": "PCI" - } + "name": "DefaultProfile", + "deployment_create_list": [ + { + "published_service_local_reference_list": [], + "package_local_reference_list": [ + { + "kind": "app_package", + "name": "AhvPackage" + } + ], + "substrate_local_reference": { + "kind": "app_substrate", + "name": "AhvSubstrate" }, - "disk_size_mib": { - "editable": true, - "operation": "modify", - "value": "3072", - "min_value": 1024, - "max_value": 10240 - }, - "operation": "modify" - }, - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 1, - "adapter_type": "PCI" - } - }, - "disk_size_mib": { - "editable": true, - "operation": "modify", - "value": "5120", - "min_value": 1024, - "max_value": 10240 + "depends_on_list": [], + "variable_list": [], + "action_list": [], + "min_replicas": "1", + "default_replicas": "", + "max_replicas": "1", + "type": "GREENFIELD", + "name": "AhvDeployment", + "options": {}, + "description": "Sample deployment pulling in service and substrate references", + "editables": { + "min_replicas": true, + "default_replicas": true, + "max_replicas": true + } + } + ], + "variable_list": [ + { + "name": "nameserver", + "description": "", + "type": "LOCAL", + "label": "Local DNS resolver", + "attrs": {}, + "val_type": "STRING", + "value": "10.40.64.15", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "foo1", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "bar1", + "data_type": "BASE", + "editables": { + "value": true }, - "operation": "modify" - } - ], - "pre_defined_nic_list": [ - { - "network_function_nic_type": "INGRESS", - "nic_type": "NORMAL_NIC", - "subnet_reference": { - "kind": "subnet", - "name": "vlan1211", - "uuid": "48728e67-d4af-46ca-a80e-aa571b18209f" + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "foo2", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "bar2", + "data_type": "BASE", + "editables": { + "value": true }, - "network_function_chain_reference": null, - "mac_address": "", - "ip_endpoint_list": [], - "operation": "add", - "editable": true, - "identifier": "A1" - } - ], - "pre_defined_categories": [ - { - "operation": "add", - "value": "TemplateType:Vm" - }, - { - "operation": "delete", - "value": "AppFamily:Demo" - } - ] - }, - "uuid": "76686b0e-eed5-4dae-a791-a2fe2ebfcc21" - } - ] + "is_hidden": false, + "is_mandatory": false + } + ], + "patch_list": [ + { + "name": "patch_update", + "description": "", + "type": "PATCH", + "variable_list": [], + "config_reference_list": [], + "attrs_list": [ + { + "target_any_local_reference": { + "kind": "app_blueprint_deployment", + "name": "AhvDeployment" + }, + "data": { + "type": "nutanix", + "nic_delete_allowed": true, + "categories_delete_allowed": true, + "categories_add_allowed": true, + "disk_delete_allowed": true, + "num_sockets_ruleset": { + "type": "", + "operation": "equal", + "editable": true, + "value": "2", + "max_value": 10, + "min_value": 1 + }, + "memory_size_mib_ruleset": { + "type": "", + "operation": "equal", + "editable": true, + "value": "2048", + "max_value": 10240, + "min_value": 1024 + }, + "num_vcpus_per_socket_ruleset": { + "type": "", + "operation": "equal", + "editable": true, + "value": "2", + "max_value": 10, + "min_value": 1 + }, + "pre_defined_disk_list": [ + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 0, + "adapter_type": "PCI" + } + }, + "disk_size_mib": { + "editable": true, + "operation": "modify", + "value": "3072", + "min_value": 1024, + "max_value": 10240 + }, + "operation": "modify" + }, + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 1, + "adapter_type": "PCI" + } + }, + "disk_size_mib": { + "editable": true, + "operation": "modify", + "value": "5120", + "min_value": 1024, + "max_value": 10240 + }, + "operation": "modify" + } + ], + "pre_defined_nic_list": [ + { + "network_function_nic_type": "INGRESS", + "nic_type": "NORMAL_NIC", + "subnet_reference": { + "kind": "subnet", + "name": "vlan1211", + "uuid": "c1a401fb-5724-4b0a-bfbb-be86e6793ebe" + }, + "network_function_chain_reference": null, + "mac_address": "", + "ip_endpoint_list": [], + "operation": "add", + "editable": true, + "identifier": "A1" + } + ], + "pre_defined_categories": [ + { + "operation": "add", + "value": "TemplateType:Vm" + }, + { + "operation": "delete", + "value": "AppFamily:Demo" + } + ] + }, + "uuid": null + } + ] + } + ], + "action_list": [], + "snapshot_config_list": [], + "restore_config_list": [] } - ], - "action_list": [], - "snapshot_config_list": [], - "restore_config_list": [] + ], + "client_attrs": {}, + "default_credential_local_reference": { + "kind": "app_credential", + "name": "default cred" } - ], - "client_attrs": {}, - "default_credential_local_reference": { - "kind": "app_credential", - "name": "default cred" - } } \ No newline at end of file diff --git a/tests/cli/runtime_helpers/ahv/app_edit_overlay_blueprint.json b/tests/cli/runtime_helpers/ahv/app_edit_overlay_blueprint.json index 8ac30e98a..5a50a320a 100644 --- a/tests/cli/runtime_helpers/ahv/app_edit_overlay_blueprint.json +++ b/tests/cli/runtime_helpers/ahv/app_edit_overlay_blueprint.json @@ -1,760 +1,767 @@ - { - "type": "USER", - "service_definition_list": [ +{ + "type": "USER", + "service_definition_list": [ + { + "name": "AhvService", + "description": "Sample mysql service", + "port_list": [], + "singleton": false, + "tier": "", + "depends_on_list": [], + "variable_list": [ + { + "name": "ENV", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "DEV", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "action_list": [ + { + "name": "action_create", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___create___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___create___dag" + }, + "task_definition_list": [ { - "name": "AhvService", - "description": "Sample mysql service", - "port_list": [], - "singleton": false, - "tier": "", - "depends_on_list": [], - "variable_list": [ - { - "name": "ENV", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "DEV", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - } - ], - "action_list": [ - { - "name": "action_create", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___create___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___create___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___create___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - { - "name": "action_start", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___start___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___start___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___start___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - { - "name": "action_stop", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___stop___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___stop___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___stop___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - { - "name": "action_delete", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___delete___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___delete___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___delete___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - { - "name": "action_restart", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___restart___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___restart___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___restart___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - { - "name": "action_soft_delete", - "description": "", - "type": "system", - "critical": true, - "runbook": { - "name": "AhvService___soft_delete___runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "AhvService___soft_delete___dag" - }, - "task_definition_list": [ - { - "name": "AhvService___soft_delete___dag", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - } - ], - "container_spec": {} + "name": "AhvService___create___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" } - ], - "package_definition_list": [ + ], + "variable_list": [], + "output_variables": [] + } + }, + { + "name": "action_start", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___start___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___start___dag" + }, + "task_definition_list": [ { - "name": "AhvPackage", - "description": "Example package with variables, install tasks and link to service", - "type": "CUSTOM", - "options": { - "install_runbook": { - "name": "Runbook_for_Package_AhvPackage_action_install", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "DAG_Task_for_Package_AhvPackage_action_install" - }, - "task_definition_list": [ - { - "name": "DAG_Task_for_Package_AhvPackage_action_install", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - }, - "uninstall_runbook": { - "name": "Runbook_for_Package_AhvPackage_action_uninstall", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "DAG_Task_for_Package_AhvPackage_action_uninstall" - }, - "task_definition_list": [ - { - "name": "DAG_Task_for_Package_AhvPackage_action_uninstall", - "description": "", - "type": "DAG", - "target_any_local_reference": { - "kind": "app_service", - "name": "AhvService" - }, - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } - }, - "service_local_reference_list": [ - { - "kind": "app_service", - "name": "AhvService" - } - ], - "variable_list": [ - { - "name": "foo", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "bar", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - } - ], - "version": "" + "name": "AhvService___start___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" } - ], - "published_service_definition_list": [], - "substrate_definition_list": [ + ], + "variable_list": [], + "output_variables": [] + } + }, + { + "name": "action_stop", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___stop___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___stop___dag" + }, + "task_definition_list": [ { - "name": "AhvSubstrate", - "description": "AHV VM config given by reading a spec file", - "type": "AHV_VM", - "os_type": "Linux", - "create_spec": { - "name": "MyAhvVm", - "categories": { - "AppFamily": "Backup", - "AppType": "Default" - }, - "cluster_reference": { - "kind": "cluster", - "name": "auto_cluster_prod_4fee9603c584", - "uuid": "0005e1e4-a1d9-6e40-3d2a-00e0ed87fc48" - }, - "resources": { - "nic_list": [ - { - "network_function_nic_type": "INGRESS", - "nic_type": "NORMAL_NIC", - "subnet_reference": { - "kind": "subnet", - "name": "vpc_subnet_1", - "uuid": "4fd66b65-d9a3-4b00-80cb-59fc148519df" - }, - "network_function_chain_reference": null, - "mac_address": "", - "ip_endpoint_list": [], - "vpc_reference": { - "name": "vpc_name_1", - "uuid": "4d479b14-cc11-4344-9beb-269ac4a21c29", - "kind": "vpc" - } - }, - { - "network_function_nic_type": "INGRESS", - "nic_type": "NORMAL_NIC", - "subnet_reference": { - "kind": "subnet", - "name": "vpc_subnet_1", - "uuid": "4fd66b65-d9a3-4b00-80cb-59fc148519df" - }, - "network_function_chain_reference": null, - "mac_address": "", - "ip_endpoint_list": [], - "vpc_reference": { - "name": "vpc_name_1", - "uuid": "4d479b14-cc11-4344-9beb-269ac4a21c29", - "kind": "vpc" - } - }, - { - "network_function_nic_type": "INGRESS", - "nic_type": "NORMAL_NIC", - "subnet_reference": { - "kind": "subnet", - "name": "vpc_subnet_1", - "uuid": "4fd66b65-d9a3-4b00-80cb-59fc148519df" - }, - "network_function_chain_reference": null, - "mac_address": "", - "ip_endpoint_list": [], - "vpc_reference": { - "name": "vpc_name_1", - "uuid": "4d479b14-cc11-4344-9beb-269ac4a21c29", - "kind": "vpc" - } - } - ], - "num_vcpus_per_socket": 1, - "num_sockets": 1, - "memory_size_mib": 4096, - "power_state": "ON", - "account_uuid": "24b46e0f-ef08-4e63-b25d-68cea9af5720", - "gpu_list": [], - "disk_list": [ - { - "data_source_reference": { - "kind": "image", - "name": "CentOS-7-cloudinit", - "uuid": "d679e320-c7f3-4831-8275-e6511e894316" - }, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 0, - "adapter_type": "SCSI" - } - }, - "disk_size_mib": 0 - }, - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 0, - "adapter_type": "PCI" - } - }, - "disk_size_mib": 4096 - }, - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 1, - "adapter_type": "PCI" - } - }, - "disk_size_mib": 6144 - }, - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 2, - "adapter_type": "PCI" - } - }, - "disk_size_mib": 8192 - } - ], - "guest_customization": null, - "serial_port_list": [ - { - "index": 0, - "is_connected": false - }, - { - "index": 1, - "is_connected": false - }, - { - "index": 2, - "is_connected": true - }, - { - "index": 3, - "is_connected": true - } - ], - "boot_config": { - "boot_device": { - "disk_address": { - "device_index": 0, - "adapter_type": "SCSI" - } - } - } - } - }, - "variable_list": [], - "action_list": [], - "readiness_probe": { - "connection_type": "SSH", - "connection_port": 22, - "connection_protocol": "", - "timeout_secs": "60", - "delay_secs": "60", - "retries": "5", - "address": "@@{platform.status.resources.nic_list[0].ip_endpoint_list[0].ip}@@", - "disable_readiness_probe": true, - "login_credential_local_reference": { - "kind": "app_credential", - "name": "default cred" - } - }, - "editables": { - "create_spec": { - "cluster_reference": true, - "resources": { - "num_vcpus_per_socket": true, - "num_sockets": true, - "memory_size_mib": true, - "boot_config": true, - "guest_customization": true, - "disk_list": { - "0": { - "data_source_reference": true, - "disk_size_mib": true - }, - "1": { - "data_source_reference": true, - "device_properties": { - "device_type": true, - "disk_address": { - "adapter_type": true - } - }, - "disk_size_mib": true - } - }, - "nic_list": { - "0": { - "subnet_reference": true, - "vpc_reference": true - } - }, - "serial_port_list": { - "0": { - "is_connected": true - }, - "1": { - "is_connected": true - } - } - }, - "name": true, - "categories": true - }, - "readiness_probe": { - "connection_port": true, - "retries": true - } - } + "name": "AhvService___stop___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" } - ], - "credential_definition_list": [ + ], + "variable_list": [], + "output_variables": [] + } + }, + { + "name": "action_delete", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___delete___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___delete___dag" + }, + "task_definition_list": [ { - "name": "default cred", - "description": "", - "type": "PASSWORD", - "username": "root", - "secret": { - "attrs": { - "is_secret_modified": true - }, - "value": "passwd" - }, - "cred_class": "static", - "editables": { - "username": true, - "secret": true - } + "name": "AhvService___delete___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" } - ], - "app_profile_list": [ + ], + "variable_list": [], + "output_variables": [] + } + }, + { + "name": "action_restart", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___restart___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___restart___dag" + }, + "task_definition_list": [ { - "name": "DefaultProfile", - "deployment_create_list": [ - { - "published_service_local_reference_list": [], - "package_local_reference_list": [ - { - "kind": "app_package", - "name": "AhvPackage" - } - ], - "substrate_local_reference": { - "kind": "app_substrate", - "name": "AhvSubstrate" - }, - "depends_on_list": [], - "variable_list": [], - "action_list": [], - "min_replicas": "1", - "default_replicas": "", - "max_replicas": "1", - "type": "GREENFIELD", - "name": "AhvDeployment", - "options": {}, - "description": "Sample deployment pulling in service and substrate references", - "editables": { - "min_replicas": true, - "default_replicas": true, - "max_replicas": true - } - } - ], - "variable_list": [ - { - "name": "nameserver", - "description": "", - "type": "LOCAL", - "label": "Local DNS resolver", - "attrs": {}, - "val_type": "STRING", - "value": "10.40.64.15", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "foo1", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "bar1", - "data_type": "BASE", - "editables": { - "value": true - }, - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "foo2", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "bar2", - "data_type": "BASE", - "editables": { - "value": true - }, - "is_hidden": false, - "is_mandatory": false - } - ], - "patch_list": [ - { - "name": "patch_update", - "description": "", - "type": "PATCH", - "variable_list": [], - "config_reference_list": [], - "attrs_list": [ - { - "target_any_local_reference": { - "kind": "app_blueprint_deployment", - "name": "AhvDeployment" - }, - "data": { - "type": "nutanix", - "nic_delete_allowed": true, - "categories_delete_allowed": true, - "categories_add_allowed": true, - "disk_delete_allowed": true, - "num_sockets_ruleset": { - "type": "", - "operation": "equal", - "editable": true, - "value": "2", - "max_value": 10, - "min_value": 1 - }, - "memory_size_mib_ruleset": { - "type": "", - "operation": "equal", - "editable": true, - "value": "2048", - "max_value": 10240, - "min_value": 1024 - }, - "num_vcpus_per_socket_ruleset": { - "type": "", - "operation": "equal", - "editable": true, - "value": "2", - "max_value": 10, - "min_value": 1 - }, - "pre_defined_disk_list": [ - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 0, - "adapter_type": "PCI" - } - }, - "disk_size_mib": { - "editable": true, - "operation": "modify", - "value": "3072", - "min_value": 1024, - "max_value": 10240 - }, - "operation": "modify" - }, - { - "data_source_reference": null, - "device_properties": { - "device_type": "DISK", - "disk_address": { - "device_index": 1, - "adapter_type": "PCI" - } - }, - "disk_size_mib": { - "editable": true, - "operation": "modify", - "value": "5120", - "min_value": 1024, - "max_value": 10240 - }, - "operation": "modify" - } - ], - "pre_defined_nic_list": [ - { - "network_function_nic_type": "INGRESS", - "nic_type": "NORMAL_NIC", - "subnet_reference": { - "kind": "subnet", - "name": "vpc_subnet_1", - "uuid": "4fd66b65-d9a3-4b00-80cb-59fc148519df" - }, - "network_function_chain_reference": null, - "mac_address": "", - "ip_endpoint_list": [], - "vpc_reference": { - "name": "vpc_name_1", - "uuid": "4d479b14-cc11-4344-9beb-269ac4a21c29", - "kind": "vpc" - }, - "operation": "add", - "editable": true, - "identifier": "A1" - } - ], - "pre_defined_categories": [ - { - "operation": "add", - "value": "TemplateType:Vm" - }, - { - "operation": "delete", - "value": "AppFamily:Demo" - } - ] - }, - "uuid": "283114bb-44e6-4823-af48-81c9d16426f8" - } - ] - } - ], - "action_list": [], - "snapshot_config_list": [], - "restore_config_list": [] + "name": "AhvService___restart___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" } + ], + "variable_list": [], + "output_variables": [] + } + }, + { + "name": "action_soft_delete", + "description": "", + "type": "system", + "critical": true, + "runbook": { + "name": "AhvService___soft_delete___runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "AhvService___soft_delete___dag" + }, + "task_definition_list": [ + { + "name": "AhvService___soft_delete___dag", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + } + } + ], + "container_spec": {} + } + ], + "package_definition_list": [ + { + "name": "AhvPackage", + "description": "Example package with variables, install tasks and link to service", + "type": "CUSTOM", + "options": { + "install_runbook": { + "name": "Runbook_for_Package_AhvPackage_action_install", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "DAG_Task_for_Package_AhvPackage_action_install" + }, + "task_definition_list": [ + { + "name": "DAG_Task_for_Package_AhvPackage_action_install", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variables": [] + }, + "uninstall_runbook": { + "name": "Runbook_for_Package_AhvPackage_action_uninstall", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "DAG_Task_for_Package_AhvPackage_action_uninstall" + }, + "task_definition_list": [ + { + "name": "DAG_Task_for_Package_AhvPackage_action_uninstall", + "description": "", + "type": "DAG", + "target_any_local_reference": { + "kind": "app_service", + "name": "AhvService" + }, + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } ], - "client_attrs": {}, - "default_credential_local_reference": { - "kind": "app_credential", - "name": "default cred" + "variable_list": [], + "output_variables": [] + } + }, + "service_local_reference_list": [ + { + "kind": "app_service", + "name": "AhvService" + } + ], + "variable_list": [ + { + "name": "foo", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "bar", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "version": "" + } + ], + "published_service_definition_list": [], + "substrate_definition_list": [ + { + "name": "AhvSubstrate", + "description": "AHV VM config given by reading a spec file", + "type": "AHV_VM", + "os_type": "Linux", + "create_spec": { + "name": "MyAhvVm", + "categories": { + "AppFamily": "Backup", + "AppType": "Default" + }, + "cluster_reference": { + "kind": "cluster", + "name": "auto_cluster_prod_364563e22ade" + }, + "resources": { + "nic_list": [ + { + "network_function_nic_type": "INGRESS", + "nic_type": "NORMAL_NIC", + "subnet_reference": { + "kind": "subnet", + "name": "vpc_subnet_1" + }, + "network_function_chain_reference": null, + "mac_address": "", + "ip_endpoint_list": [], + "vpc_reference": { + "name": "vpc_name_1", + "kind": "vpc" + } + }, + { + "network_function_nic_type": "INGRESS", + "nic_type": "NORMAL_NIC", + "subnet_reference": { + "kind": "subnet", + "name": "vpc_subnet_1" + }, + "network_function_chain_reference": null, + "mac_address": "", + "ip_endpoint_list": [], + "vpc_reference": { + "name": "vpc_name_1", + "kind": "vpc" + } + }, + { + "network_function_nic_type": "INGRESS", + "nic_type": "NORMAL_NIC", + "subnet_reference": { + "kind": "subnet", + "name": "vpc_subnet_1" + }, + "network_function_chain_reference": null, + "mac_address": "", + "ip_endpoint_list": [], + "vpc_reference": { + "name": "vpc_name_1", + "kind": "vpc" + } + } + ], + "num_vcpus_per_socket": 1, + "num_sockets": 1, + "memory_size_mib": 4096, + "power_state": "ON", + "account_uuid": null, + "gpu_list": [], + "disk_list": [ + { + "data_source_reference": { + "kind": "image", + "name": "CentOS-7-cloudinit", + "uuid": null + }, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 0, + "adapter_type": "SCSI" + } + }, + "disk_size_mib": 0 + }, + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 0, + "adapter_type": "PCI" + } + }, + "disk_size_mib": 4096 + }, + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 1, + "adapter_type": "PCI" + } + }, + "disk_size_mib": 6144 + }, + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 2, + "adapter_type": "PCI" + } + }, + "disk_size_mib": 8192 + } + ], + "guest_customization": null, + "serial_port_list": [ + { + "index": 0, + "is_connected": false + }, + { + "index": 1, + "is_connected": false + }, + { + "index": 2, + "is_connected": true + }, + { + "index": 3, + "is_connected": true + } + ], + "boot_config": { + "boot_device": { + "disk_address": { + "device_index": 0, + "adapter_type": "SCSI" + } + } } + } + }, + "variable_list": [], + "action_list": [], + "readiness_probe": { + "connection_type": "SSH", + "connection_port": 22, + "connection_protocol": "", + "timeout_secs": "60", + "delay_secs": "60", + "retries": "5", + "address": "@@{platform.status.resources.nic_list[0].ip_endpoint_list[0].ip}@@", + "disable_readiness_probe": true, + "login_credential_local_reference": { + "kind": "app_credential", + "name": "default cred" + } + }, + "editables": { + "create_spec": { + "cluster_reference": true, + "resources": { + "num_vcpus_per_socket": true, + "num_sockets": true, + "memory_size_mib": true, + "boot_config": true, + "guest_customization": true, + "disk_list": { + "0": { + "data_source_reference": true, + "disk_size_mib": true + }, + "1": { + "data_source_reference": true, + "device_properties": { + "device_type": true, + "disk_address": { + "adapter_type": true + } + }, + "disk_size_mib": true + } + }, + "nic_list": { + "0": { + "subnet_reference": true, + "vpc_reference": true + } + }, + "serial_port_list": { + "0": { + "is_connected": true + }, + "1": { + "is_connected": true + } + } + }, + "name": true, + "categories": true + }, + "readiness_probe": { + "connection_port": true, + "retries": true + } + } + } + ], + "credential_definition_list": [ + { + "name": "default cred", + "description": "", + "type": "PASSWORD", + "username": "root", + "secret": { + "attrs": { + "is_secret_modified": true + }, + "value": "passwd" + }, + "cred_class": "static", + "editables": { + "username": true, + "secret": true } + } + ], + "app_profile_list": [ + { + "name": "DefaultProfile", + "deployment_create_list": [ + { + "published_service_local_reference_list": [], + "package_local_reference_list": [ + { + "kind": "app_package", + "name": "AhvPackage" + } + ], + "substrate_local_reference": { + "kind": "app_substrate", + "name": "AhvSubstrate" + }, + "depends_on_list": [], + "variable_list": [], + "action_list": [], + "min_replicas": "1", + "default_replicas": "", + "max_replicas": "1", + "type": "GREENFIELD", + "name": "AhvDeployment", + "options": {}, + "description": "Sample deployment pulling in service and substrate references", + "editables": { + "min_replicas": true, + "default_replicas": true, + "max_replicas": true + } + } + ], + "variable_list": [ + { + "name": "nameserver", + "description": "", + "type": "LOCAL", + "label": "Local DNS resolver", + "attrs": {}, + "val_type": "STRING", + "value": "10.40.64.15", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "foo1", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "bar1", + "data_type": "BASE", + "editables": { + "value": true + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "foo2", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "bar2", + "data_type": "BASE", + "editables": { + "value": true + }, + "is_hidden": false, + "is_mandatory": false + } + ], + "patch_list": [ + { + "name": "patch_update", + "description": "", + "type": "PATCH", + "variable_list": [], + "config_reference_list": [], + "attrs_list": [ + { + "target_any_local_reference": { + "kind": "app_blueprint_deployment", + "name": "AhvDeployment" + }, + "data": { + "type": "nutanix", + "nic_delete_allowed": true, + "categories_delete_allowed": true, + "categories_add_allowed": true, + "disk_delete_allowed": true, + "num_sockets_ruleset": { + "type": "", + "operation": "equal", + "editable": true, + "value": "2", + "max_value": 10, + "min_value": 1 + }, + "memory_size_mib_ruleset": { + "type": "", + "operation": "equal", + "editable": true, + "value": "2048", + "max_value": 10240, + "min_value": 1024 + }, + "num_vcpus_per_socket_ruleset": { + "type": "", + "operation": "equal", + "editable": true, + "value": "2", + "max_value": 10, + "min_value": 1 + }, + "pre_defined_disk_list": [ + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 0, + "adapter_type": "PCI" + } + }, + "disk_size_mib": { + "editable": true, + "operation": "modify", + "value": "3072", + "min_value": 1024, + "max_value": 10240 + }, + "operation": "modify" + }, + { + "data_source_reference": null, + "device_properties": { + "device_type": "DISK", + "disk_address": { + "device_index": 1, + "adapter_type": "PCI" + } + }, + "disk_size_mib": { + "editable": true, + "operation": "modify", + "value": "5120", + "min_value": 1024, + "max_value": 10240 + }, + "operation": "modify" + } + ], + "pre_defined_nic_list": [ + { + "network_function_nic_type": "INGRESS", + "nic_type": "NORMAL_NIC", + "subnet_reference": { + "kind": "subnet", + "name": "vpc_subnet_1" + }, + "network_function_chain_reference": null, + "mac_address": "", + "ip_endpoint_list": [], + "vpc_reference": { + "name": "vpc_name_1", + "kind": "vpc" + }, + "operation": "add", + "editable": true, + "identifier": "A1" + } + ], + "pre_defined_categories": [ + { + "operation": "add", + "value": "TemplateType:Vm" + }, + { + "operation": "delete", + "value": "AppFamily:Demo" + } + ] + }, + "uuid": null + } + ] + } + ], + "action_list": [], + "snapshot_config_list": [], + "restore_config_list": [] + } + ], + "client_attrs": {}, + "default_credential_local_reference": { + "kind": "app_credential", + "name": "default cred" + } +} diff --git a/tests/cli/runtime_helpers/ahv/test_app_edit_json.py b/tests/cli/runtime_helpers/ahv/test_app_edit_json.py index 0c0c98e64..58b17f83c 100644 --- a/tests/cli/runtime_helpers/ahv/test_app_edit_json.py +++ b/tests/cli/runtime_helpers/ahv/test_app_edit_json.py @@ -5,6 +5,8 @@ from calm.dsl.builtins import read_local_file from calm.dsl.store import Version from distutils.version import LooseVersion as LV +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp CRED_USERNAME = read_local_file(".tests/username") CRED_PASSWORD = read_local_file(".tests/password") @@ -59,6 +61,12 @@ def test_json(): for cred in known_json["credential_definition_list"]: cred["cred_class"] = "static" + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json) + + remove_output_variables_from_bp(known_json) + remove_output_variables_from_bp(generated_json) + assert sorted(known_json.items()) == sorted( generated_json.items() ), "Known Json: {}\nGen Json: {}".format(known_json.items(), generated_json.items()) diff --git a/tests/cli/runtime_helpers/ahv/test_app_edit_overlay_json.py b/tests/cli/runtime_helpers/ahv/test_app_edit_overlay_json.py index a71c3bfd3..4f9dbb7aa 100644 --- a/tests/cli/runtime_helpers/ahv/test_app_edit_overlay_json.py +++ b/tests/cli/runtime_helpers/ahv/test_app_edit_overlay_json.py @@ -11,6 +11,8 @@ ) from calm.dsl.store import Version from distutils.version import LooseVersion as LV +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp from tests.utils import get_local_az_overlay_details_from_dsl_config CRED_USERNAME = read_local_file(".tests/username") @@ -130,6 +132,12 @@ def test_json(self): for cred in known_json["credential_definition_list"]: cred["cred_class"] = "static" + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json) + + remove_output_variables_from_bp(known_json) + remove_output_variables_from_bp(generated_json) + assert sorted(known_json.items()) == sorted( generated_json.items() ), "Known Json: {}\nGen Json: {}".format( diff --git a/tests/cli/test_acp_commands.py b/tests/cli/test_acp_commands.py index 6f39bc670..bd5599ca2 100644 --- a/tests/cli/test_acp_commands.py +++ b/tests/cli/test_acp_commands.py @@ -19,6 +19,7 @@ DSL_PROJECT_PATH = "tests/project/test_project_in_pc.py" +@pytest.mark.quotas class TestACPCommands: def setup_method(self): diff --git a/tests/cli/test_policy_commands.py b/tests/cli/test_policy_commands.py index d54bdb16f..d642a55cc 100644 --- a/tests/cli/test_policy_commands.py +++ b/tests/cli/test_policy_commands.py @@ -50,6 +50,7 @@ reason="Approval policy is supported from 3.5.0", ) @pytest.mark.parallel_executable +@pytest.mark.approval_policy class TestPolicyCommands: @classmethod def setup_class(cls): diff --git a/tests/cli/test_project_commands.py b/tests/cli/test_project_commands.py index ff31fbfb0..bdca03c6b 100644 --- a/tests/cli/test_project_commands.py +++ b/tests/cli/test_project_commands.py @@ -33,6 +33,7 @@ CALM_VERSION = Version.get_version("Calm") +@pytest.mark.quotas class TestProjectCommands: def setup_method(self): """ "Reset the context changes""" diff --git a/tests/cli/test_scheduler_commands.py b/tests/cli/test_scheduler_commands.py index 82b800e00..a5093e37d 100644 --- a/tests/cli/test_scheduler_commands.py +++ b/tests/cli/test_scheduler_commands.py @@ -87,6 +87,10 @@ def test_job_create_app_action(self, dsl_file): # Launch Blueprint bps.launch_blueprint_simple(bp_name, app_name=app_name, patch_editables=False) + # wait for app to provision completely + # TODO: add helper for app to be in provisioning state + time.sleep(180) + jobname = "test_job_scheduler" + suffix() file_replace( dsl_file, @@ -364,7 +368,7 @@ def test_job_describe(self, dsl_file): assert result.get("resources").get("state") == "ACTIVE" client = get_api_client() - job_get_res = scheduler.get_job(client, job_name, all=True) + job_get_res = scheduler.get_job(client, job_name) res, err = client.job.read(job_get_res["metadata"]["uuid"]) job_response = res.json() LOG.info(job_response) @@ -464,8 +468,9 @@ def test_recurring_rb_job_with_no_expiration(self, dsl_file, dsl_runbook_file): result = scheduler.create_job_command(dsl_file, jobname, None, False) assert result.get("resources").get("state") == "ACTIVE" + time.sleep(10) client = get_api_client() - job_get_res = scheduler.get_job(client, jobname, all=True) + job_get_res = scheduler.get_job(client, jobname) res, err = client.job.read(job_get_res["metadata"]["uuid"]) job_response = res.json() LOG.info(job_response) @@ -501,6 +506,10 @@ def test_recurring_app_job_with_no_expiration(self, dsl_file): # Launch Blueprint bps.launch_blueprint_simple(bp_name, app_name=app_name, patch_editables=False) + # wait for app to provision completely + # TODO: add helper for app to be in provisioning state + time.sleep(180) + jobname = "test_job_scheduler" + suffix() file_replace( dsl_file, @@ -510,8 +519,9 @@ def test_recurring_app_job_with_no_expiration(self, dsl_file): result = scheduler.create_job_command(dsl_file, jobname, None, False) assert result.get("resources").get("state") == "ACTIVE" + time.sleep(10) client = get_api_client() - job_get_res = scheduler.get_job(client, jobname, all=True) + job_get_res = scheduler.get_job(client, jobname) res, err = client.job.read(job_get_res["metadata"]["uuid"]) job_response = res.json() LOG.info(job_response) diff --git a/tests/cli/test_utils.py b/tests/cli/test_utils.py new file mode 100644 index 000000000..14d653139 --- /dev/null +++ b/tests/cli/test_utils.py @@ -0,0 +1,121 @@ +import os +import pytest +import copy +import json + +from calm.dsl.cli.utils import insert_uuid + + +class TestInsertUuid: + def test_insert_uuid_with_same_action_and_task_names(self): + json_file = "action_with_same_task_name.json" + dir_path = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(dir_path + "/utils_payload", json_file) + action = json.loads(open(file_path).read()) + + name_uuid_map = {} + action_list_with_uuid = copy.deepcopy(action) + entity_key = "" + + insert_uuid(action, name_uuid_map, action_list_with_uuid, entity_key) + + assert ( + action_list_with_uuid["uuid"] + != action_list_with_uuid["runbook"]["task_definition_list"][1]["uuid"] + ) + assert ( + action_list_with_uuid["runbook"]["task_definition_list"][1]["uuid"] + == action_list_with_uuid["runbook"]["task_definition_list"][0][ + "child_tasks_local_reference_list" + ][0]["uuid"] + ) + + def test_insert_uuid_with_same_variable_names(self): + json_file = "action_task_with_same_variable_names.json" + dir_path = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(dir_path + "/utils_payload", json_file) + action = json.loads(open(file_path).read()) + + name_uuid_map = {} + action_list_with_uuid = copy.deepcopy(action) + entity_key = "" + + insert_uuid(action, name_uuid_map, action_list_with_uuid, entity_key) + + assert ( + action_list_with_uuid["runbook"]["variable_list"][0]["uuid"] + != action_list_with_uuid["runbook"]["task_definition_list"][1][ + "variable_list" + ][0]["uuid"] + ) + assert ( + action_list_with_uuid["runbook"]["variable_list"][0]["value"] + == "action_var1_value" + ) + assert ( + action_list_with_uuid["runbook"]["task_definition_list"][1][ + "variable_list" + ][0]["value"] + == "action_var2_value" + ) + + def test_insert_uuid_with_same_header_names(self): + json_file = "action_task_with_same_header_names.json" + dir_path = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(dir_path + "/utils_payload", json_file) + action = json.loads(open(file_path).read()) + + name_uuid_map = {} + action_list_with_uuid = copy.deepcopy(action) + entity_key = "" + + insert_uuid(action, name_uuid_map, action_list_with_uuid, entity_key) + + assert ( + action_list_with_uuid["runbook"]["variable_list"][0]["options"]["attrs"][ + "headers" + ][0]["uuid"] + != action_list_with_uuid["runbook"]["variable_list"][1]["options"]["attrs"][ + "headers" + ][0]["uuid"] + ) + + def test_insert_uuid_with_same_input_output_variable_names(self): + json_file = "action_task_with_same_input_output_variable_names.json" + dir_path = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(dir_path + "/utils_payload", json_file) + action = json.loads(open(file_path).read()) + + name_uuid_map = {} + action_list_with_uuid = copy.deepcopy(action) + entity_key = "" + + insert_uuid(action, name_uuid_map, action_list_with_uuid, entity_key) + + assert ( + action_list_with_uuid["runbook"]["variable_list"][0]["uuid"] + != action_list_with_uuid["runbook"]["output_variable_list"][0]["uuid"] + ) + assert ( + action_list_with_uuid["runbook"]["variable_list"][0]["value"] + != action_list_with_uuid["runbook"]["output_variable_list"][0]["value"] + ) + + def test_insert_uuid_with_action_having_multiple_tasks(self): + json_file = "action_task_with_multiple_tasks.json" + dir_path = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(dir_path + "/utils_payload", json_file) + action = json.loads(open(file_path).read()) + + name_uuid_map = {} + action_list_with_uuid = copy.deepcopy(action) + entity_key = "" + + insert_uuid(action, name_uuid_map, action_list_with_uuid, entity_key) + + assert ( + "uuid" + in action_list_with_uuid["runbook"]["task_definition_list"][0]["attrs"][ + "edges" + ][0] + ), "UUID is missing in edges" diff --git a/tests/cli/utils_payload/action_task_with_multiple_tasks.json b/tests/cli/utils_payload/action_task_with_multiple_tasks.json new file mode 100644 index 000000000..16117945e --- /dev/null +++ b/tests/cli/utils_payload/action_task_with_multiple_tasks.json @@ -0,0 +1,196 @@ +{ + "name": "TestUtilsAction5", + "description": "List Action for TestUtilsResourceType", + "type": "resource_type_generic", + "critical": false, + "runbook": { + "name": "TestUtilsResourceType_TestUtilsAction5_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "TestUtilsResourceType_TestUtilsAction5_dag" + }, + "task_definition_list": [ + { + "name": "TestUtilsResourceType_TestUtilsAction5_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [ + { + "from_task_reference": { + "kind": "app_task", + "name": "TestUtilsTask5_parent" + }, + "to_task_reference": { + "kind": "app_task", + "name": "TestUtilsTask5_child" + } + } + ] + }, + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "TestUtilsTask5" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "TestUtilsTask5_parent", + "description": "", + "type": "EXEC", + "attrs": { + "script_type": "static_py5", + "script": "print ('Successfully Authenticated Parent')" + }, + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "TestUtilsTask5_child", + "description": "", + "type": "EXEC", + "attrs": { + "script_type": "static_py5", + "script": "print ('Successfully Authenticated Child')" + }, + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [ + { + "val_type": "STRING", + "is_mandatory": false, + "description": "http_string_get_var description", + "data_type": "LIST", + "value": "", + "label": "http_string_get_var_label", + "attrs": {}, + "editables": { + "value": true + }, + "is_hidden": false, + "type": "HTTP_LOCAL", + "options": { + "type": "HTTP", + "attrs": { + "type": "HTTP", + "retry_interval": 10, + "method": "GET", + "expected_response_params": [ + { + "status": "SUCCESS", + "code": 200 + } + ], + "retry_count": 1, + "content_type": "application/json", + "tls_verify": true, + "headers": [ + { + "name": "Content-Type", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "application/json", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "response_paths": { + "http_string_get_var": "$.title" + }, + "connection_timeout": 120, + "authentication": { + "auth_type": "basic", + "basic_auth": { + "username": "root", + "password": { + "attrs": { + "is_secret_modified": false + } + } + } + }, + "url": "https://jsonplaceholder.typicode.com/posts/1" + } + }, + "name": "http_string_get_var_1" + }, + { + "val_type": "STRING", + "is_mandatory": false, + "description": "http_string_get_var_2 description", + "data_type": "LIST", + "value": "", + "label": "http_string_get_var_2_label", + "attrs": {}, + "editables": { + "value": true + }, + "is_hidden": false, + "type": "HTTP_LOCAL", + "options": { + "type": "HTTP", + "attrs": { + "type": "HTTP", + "retry_interval": 10, + "method": "GET", + "expected_response_params": [ + { + "status": "SUCCESS", + "code": 200 + } + ], + "retry_count": 1, + "content_type": "application/json", + "tls_verify": true, + "headers": [ + { + "name": "Content-Type", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "application/json", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "response_paths": { + "http_string_get_var": "$.title" + }, + "connection_timeout": 120, + "authentication": { + "auth_type": "basic", + "basic_auth": { + "username": "root", + "password": { + "attrs": { + "is_secret_modified": false + } + } + } + }, + "url": "https://jsonplaceholder.typicode.com/posts/1" + } + }, + "name": "http_string_get_var_2" + } + ] + } +} \ No newline at end of file diff --git a/tests/cli/utils_payload/action_task_with_same_header_names.json b/tests/cli/utils_payload/action_task_with_same_header_names.json new file mode 100644 index 000000000..d5974cea0 --- /dev/null +++ b/tests/cli/utils_payload/action_task_with_same_header_names.json @@ -0,0 +1,172 @@ +{ + "name": "TestUtilsAction3", + "description": "List Action for TestUtilsResourceType", + "type": "resource_type_generic", + "critical": false, + "runbook": { + "name": "TestUtilsResourceType_TestUtilsAction3_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "TestUtilsResourceType_TestUtilsAction3_dag" + }, + "task_definition_list": [ + { + "name": "TestUtilsResourceType_TestUtilsAction3_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [] + }, + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "TestUtilsTask3" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "TestUtilsTask3", + "description": "", + "type": "EXEC", + "attrs": { + "script_type": "static_py3", + "script": "print ('Successfully Authenticated')" + }, + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [ + { + "val_type": "STRING", + "is_mandatory": false, + "description": "http_string_get_var description", + "data_type": "LIST", + "value": "", + "label": "http_string_get_var_label", + "attrs": {}, + "editables": { + "value": true + }, + "is_hidden": false, + "type": "HTTP_LOCAL", + "options": { + "type": "HTTP", + "attrs": { + "type": "HTTP", + "retry_interval": 10, + "method": "GET", + "expected_response_params": [ + { + "status": "SUCCESS", + "code": 200 + } + ], + "retry_count": 1, + "content_type": "application/json", + "tls_verify": true, + "headers": [ + { + "name": "Content-Type", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "application/json", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "response_paths": { + "http_string_get_var": "$.title" + }, + "connection_timeout": 120, + "authentication": { + "auth_type": "basic", + "basic_auth": { + "username": "root", + "password": { + "attrs": { + "is_secret_modified": false + } + } + } + }, + "url": "https://jsonplaceholder.typicode.com/posts/1" + } + }, + "name": "http_string_get_var_1" + }, + { + "val_type": "STRING", + "is_mandatory": false, + "description": "http_string_get_var_2 description", + "data_type": "LIST", + "value": "", + "label": "http_string_get_var_2_label", + "attrs": {}, + "editables": { + "value": true + }, + "is_hidden": false, + "type": "HTTP_LOCAL", + "options": { + "type": "HTTP", + "attrs": { + "type": "HTTP", + "retry_interval": 10, + "method": "GET", + "expected_response_params": [ + { + "status": "SUCCESS", + "code": 200 + } + ], + "retry_count": 1, + "content_type": "application/json", + "tls_verify": true, + "headers": [ + { + "name": "Content-Type", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "application/json", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "response_paths": { + "http_string_get_var": "$.title" + }, + "connection_timeout": 120, + "authentication": { + "auth_type": "basic", + "basic_auth": { + "username": "root", + "password": { + "attrs": { + "is_secret_modified": false + } + } + } + }, + "url": "https://jsonplaceholder.typicode.com/posts/1" + } + }, + "name": "http_string_get_var_2" + } + ] + } +} \ No newline at end of file diff --git a/tests/cli/utils_payload/action_task_with_same_input_output_variable_names.json b/tests/cli/utils_payload/action_task_with_same_input_output_variable_names.json new file mode 100644 index 000000000..2699a429a --- /dev/null +++ b/tests/cli/utils_payload/action_task_with_same_input_output_variable_names.json @@ -0,0 +1,77 @@ +{ + "name": "TestUtilsAction4", + "description": "List Action for TestUtilsResourceType", + "type": "resource_type_generic", + "critical": false, + "runbook": { + "name": "TestUtilsResourceType_TestUtilsAction4_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "TestUtilsResourceType_TestUtilsAction4_dag" + }, + "task_definition_list": [ + { + "name": "TestUtilsResourceType_TestUtilsAction4_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [] + }, + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "TestUtilsTask4" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "Set Outputs", + "description": "", + "type": "SET_VARIABLE", + "attrs": { + "script_type": "static_py3", + "script": "print ('input_output_var = out_val')", + "eval_variables": [ + "input_output_var" + ] + }, + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [ + { + "name": "input_output_var", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "input_value", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "output_variable_list": [ + { + "name": "input_output_var", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "output_value", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ] + } +} \ No newline at end of file diff --git a/tests/cli/utils_payload/action_task_with_same_variable_names.json b/tests/cli/utils_payload/action_task_with_same_variable_names.json new file mode 100644 index 000000000..403abd426 --- /dev/null +++ b/tests/cli/utils_payload/action_task_with_same_variable_names.json @@ -0,0 +1,73 @@ +{ + "name": "TestUtilsAction2", + "description": "List Action for TestUtilsResourceType", + "type": "resource_type_generic", + "critical": false, + "runbook": { + "name": "TestUtilsResourceType_TestUtilsAction2_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "TestUtilsResourceType_TestUtilsAction2_dag" + }, + "task_definition_list": [ + { + "name": "TestUtilsResourceType_TestUtilsAction2_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [] + }, + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "TestUtilsTask2" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "TestUtilsTask2", + "description": "", + "type": "EXEC", + "attrs": { + "script_type": "static_py3", + "script": "print ('Successfully Authenticated')" + }, + "child_tasks_local_reference_list": [], + "variable_list": [ + { + "name": "action_var1", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "action_var2_value", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [ + { + "name": "action_var1", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "action_var1_value", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ] + } +} \ No newline at end of file diff --git a/tests/cli/utils_payload/action_with_same_task_name.json b/tests/cli/utils_payload/action_with_same_task_name.json new file mode 100644 index 000000000..e29326ac1 --- /dev/null +++ b/tests/cli/utils_payload/action_with_same_task_name.json @@ -0,0 +1,60 @@ +{ + "name": "TestUtilsAction1", + "description": "List Action for TestUtilsResourceType", + "type": "resource_type_generic", + "critical": false, + "runbook": { + "name": "TestUtilsResourceType_TestUtilsAction1_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "TestUtilsResourceType_TestUtilsAction1_dag" + }, + "task_definition_list": [ + { + "name": "TestUtilsResourceType_TestUtilsAction1_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [] + }, + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "TestUtilsAction1" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "TestUtilsAction1", + "description": "", + "type": "EXEC", + "attrs": { + "script_type": "static_py3", + "script": "print ('Successfully Authenticated')" + }, + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [ + { + "name": "action_var1", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "action_var1_value", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ] + } +} \ No newline at end of file diff --git a/tests/decompile/test_decompile_with_encrypted_secrets.py b/tests/decompile/test_decompile_with_encrypted_secrets.py index dc6bd201e..46ccb80c8 100644 --- a/tests/decompile/test_decompile_with_encrypted_secrets.py +++ b/tests/decompile/test_decompile_with_encrypted_secrets.py @@ -10,6 +10,7 @@ from calm.dsl.config import get_context from calm.dsl.log import get_logging_handle from calm.dsl.decompile import init_decompile_context +from tests.helper.output_variables_helper import remove_output_variables_from_bp LOG = get_logging_handle(__name__) @@ -203,6 +204,8 @@ def test_bp_payload_with_encrypted_secrets(self): self.remove_secret_values(bp_json["spec"]["resources"]) self.remove_secret_values(decompiled_bp_json["spec"]["resources"]) + remove_output_variables_from_bp(bp_json) + remove_output_variables_from_bp(decompiled_bp_json) assert bp_json == decompiled_bp_json LOG.info("Success") diff --git a/tests/escript/scripts/escript_kubernetes.py b/tests/escript/scripts/escript_kubernetes.py index 0dc891a89..38bda26ed 100644 --- a/tests/escript/scripts/escript_kubernetes.py +++ b/tests/escript/scripts/escript_kubernetes.py @@ -10,4 +10,4 @@ k8client.Configuration.set_default(configuration) v1=k8client.CoreV1Api() nodes=v1.list_node(watch=False) -print(nodes.items[0].metadata.name == "master0") \ No newline at end of file +print(nodes.items[0].metadata.name == "localhost") \ No newline at end of file diff --git a/tests/example_accounts/json_payload/test_ndb_account.json b/tests/example_accounts/json_payload/test_ndb_account.json index 9974e6133..c1d19b26a 100644 --- a/tests/example_accounts/json_payload/test_ndb_account.json +++ b/tests/example_accounts/json_payload/test_ndb_account.json @@ -7,84 +7,81 @@ "data": { "provider_reference": { "kind": "provider", - "uuid": "" + "uuid": "", + "name": "NDB" }, "variable_list": [ { + "type": "LOCAL", + "name": "ndb__ndb_endpoint", + "uuid": "", + "description": "", "regex": {}, - "val_type": "STRING", + "options": {}, + "is_hidden": false, "is_mandatory": true, - "description": "", "data_type": "BASE", - "message_list": [], - "uuid": "", - "value": "ENDPOINT_VALUE", + "val_type": "STRING", "label": "Server IP", "attrs": {}, "editables": { "value": true }, - "is_hidden": false, - "type": "LOCAL", - "options": {}, - "name": "ndb__ndb_endpoint" + "value": "ENDPOINT_VALUE" }, { + "type": "LOCAL", + "name": "ndb__ndb_username", + "uuid": "", + "description": "", "regex": {}, - "val_type": "STRING", + "options": {}, + "is_hidden": false, "is_mandatory": true, - "description": "", "data_type": "BASE", - "message_list": [], - "uuid": "", - "value": "USERNAME_VALUE", + "val_type": "STRING", "label": "Username", "attrs": {}, "editables": { "value": true }, - "is_hidden": false, - "type": "LOCAL", - "options": {}, - "name": "ndb__ndb_username" + "value": "USERNAME_VALUE" }, { + "type": "SECRET", + "name": "ndb__ndb_password", + "uuid": "", + "description": "", "regex": {}, - "val_type": "STRING", + "options": {}, + "is_hidden": false, "is_mandatory": true, - "description": "", "data_type": "BASE", - "message_list": [], - "uuid": "", - "value": "PASSWORD_VALUE", + "val_type": "STRING", "label": "Password", "editables": { "value": true }, - "is_hidden": false, - "type": "SECRET", - "options": {}, - "name": "ndb__ndb_password", + "value": "PASSWORD_VALUE", "attrs": { "is_secret_modified": true } }, { + "type": "LOCAL", + "name": "ndb__insecure", + "uuid": "", + "description": "", "regex": {}, - "val_type": "BOOLEAN", + "options": {}, + "is_hidden": true, "is_mandatory": false, - "description": "", "data_type": "BASE", - "message_list": [], - "uuid": "", - "value": "true", + "val_type": "BOOLEAN", "label": "", "attrs": {}, "editables": {}, - "is_hidden": true, - "type": "LOCAL", - "options": {}, - "name": "ndb__insecure" + "value": "true" } ] }, @@ -100,4 +97,4 @@ "name": "test_ndb_123321", "uuid": "" } -} +} \ No newline at end of file diff --git a/tests/existing_vm_example/test_existing_vm_bp.json b/tests/existing_vm_example/test_existing_vm_bp.json index 1006eb85c..4e3e73516 100644 --- a/tests/existing_vm_example/test_existing_vm_bp.json +++ b/tests/existing_vm_example/test_existing_vm_bp.json @@ -71,13 +71,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -104,13 +106,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -137,13 +141,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -170,13 +176,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -203,13 +211,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -236,13 +246,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -286,13 +298,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -319,13 +333,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -352,13 +368,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -385,13 +403,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -418,13 +438,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -451,13 +473,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -495,6 +519,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -521,6 +546,7 @@ "script_type": "sh", "script": "echo \"Hello\"; sleep 10" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -542,6 +568,7 @@ "name": "default cred" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -573,7 +600,8 @@ "is_hidden": false, "is_mandatory": false } - ] + ], + "output_variables": [] } } ], @@ -605,6 +633,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -631,13 +660,15 @@ "name": "default cred" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_MySQLPackage_action_uninstall", @@ -658,13 +689,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -716,6 +749,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -738,13 +772,15 @@ "script_type": "sh", "script": "echo @@{foo}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_PHPPackage_action_uninstall", @@ -765,13 +801,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -834,6 +872,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -856,13 +895,15 @@ "script_type": "static_py3", "script": "print ('Hello!')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -1168,7 +1209,8 @@ "choices": [ "var13_val1", "var13_val2" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1194,7 +1236,8 @@ "choices": [ "0", "1" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1220,7 +1263,8 @@ "choices": [ "30/02/2019", "31/06/2019" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1246,7 +1290,8 @@ "choices": [ "22:35:00", "10:35:00" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1272,7 +1317,8 @@ "choices": [ "30/02/2019 - 22:35:00", "31/06/2019 - 10:35:00" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1294,7 +1340,8 @@ "choices": [ "x\ny", "a\nb" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1320,7 +1367,8 @@ "choices": [ "var19_val1", "var19_val2" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1346,7 +1394,8 @@ "choices": [ "0", "1" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1372,7 +1421,8 @@ "choices": [ "30/02/2019", "31/06/2019" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1398,7 +1448,8 @@ "choices": [ "22:35:00", "10:35:00" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1424,7 +1475,8 @@ "choices": [ "30/02/2019 - 22:35:00", "31/06/2019 - 10:35:00" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1446,7 +1498,8 @@ "choices": [ "x\ny", "a\nb" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1486,7 +1539,8 @@ "var25": "$.title" }, "type": "HTTP" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1513,7 +1567,8 @@ "script_type": "static_py3", "script": "print ('0')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1540,7 +1595,8 @@ "script_type": "static_py3", "script": "print ('30/02/2019')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1567,7 +1623,8 @@ "script_type": "static_py3", "script": "print ('22:35:00')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1594,7 +1651,8 @@ "script_type": "static_py3", "script": "print ('30/02/2019 - 22:35:00')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1617,7 +1675,8 @@ "script_type": "static_py3", "script": "print ('x\ny')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1657,7 +1716,8 @@ "var31": "$.title" }, "type": "HTTP" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1684,7 +1744,8 @@ "script_type": "static_py3", "script": "print ('0,1')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1711,7 +1772,8 @@ "script_type": "static_py3", "script": "print ('30/02/2019,31/06/2019')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1738,7 +1800,8 @@ "script_type": "static_py3", "script": "print ('22:35:00,10:35:00')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1765,7 +1828,8 @@ "script_type": "static_py3", "script": "print ('30/02/2019 - 22:35:00,31/06/2019 - 10:35:00')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1788,7 +1852,8 @@ "script_type": "static_py3", "script": "print ('var36=x\ny,a\nb')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1936,6 +2001,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -2006,6 +2072,7 @@ "script_type": "sh", "script": "echo \"Hello\"\nsleep 10\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2025,6 +2092,7 @@ "name": "PHPService_test_action_runbook" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2094,6 +2162,7 @@ "foo_title": "$.title" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2147,6 +2216,7 @@ "foo_title": "$.title" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2196,6 +2266,7 @@ "foo_title": "$.title" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2241,6 +2312,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2290,6 +2362,7 @@ "foo_title": "$.title" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2307,6 +2380,7 @@ "scaling_type": "SCALEOUT", "scaling_count": "1" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2323,6 +2397,7 @@ "attrs": { "interval_secs": 60 }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2340,6 +2415,7 @@ "scaling_type": "SCALEIN", "scaling_count": "1" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2357,6 +2433,7 @@ "script_type": "static_py3", "script": "print ('Hello World!')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2377,6 +2454,7 @@ "var1" ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2397,6 +2475,7 @@ "var2" ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2439,7 +2518,8 @@ "is_hidden": true, "is_mandatory": true } - ] + ], + "output_variables": [] } } ] diff --git a/tests/existing_vm_example/test_existing_vm_bp.py b/tests/existing_vm_example/test_existing_vm_bp.py index 336c05efc..7c5284528 100644 --- a/tests/existing_vm_example/test_existing_vm_bp.py +++ b/tests/existing_vm_example/test_existing_vm_bp.py @@ -14,6 +14,8 @@ from calm.dsl.builtins import provider_spec, read_local_file from calm.dsl.store import Version from distutils.version import LooseVersion as LV +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp DNS_SERVER = read_local_file(".tests/dns_server") @@ -518,4 +520,18 @@ def test_json(): if LV(CALM_VERSION) >= LV("3.4.0"): for cred in known_json["credential_definition_list"]: cred["cred_class"] = "static" + + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json) + + # remove exec_target_reference from options introduced in DSL 4.0.0 + if LV(CALM_VERSION) < LV("4.0.0"): + for profile in known_json.get("app_profile_list", []): + for variable in profile.get("variable_list", []): + if variable.get("options"): + variable["options"].pop("exec_target_reference", None) + + remove_output_variables_from_bp(known_json) + remove_output_variables_from_bp(generated_json) + assert generated_json == known_json diff --git a/tests/existing_vm_example_with_target_endpoint/test_existing_vm_bp.json b/tests/existing_vm_example_with_target_endpoint/test_existing_vm_bp.json index fe15d0152..274f4397e 100644 --- a/tests/existing_vm_example_with_target_endpoint/test_existing_vm_bp.json +++ b/tests/existing_vm_example_with_target_endpoint/test_existing_vm_bp.json @@ -71,13 +71,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -104,13 +106,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -137,13 +141,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -170,13 +176,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -203,13 +211,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -236,13 +246,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -286,13 +298,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -319,13 +333,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -352,13 +368,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -385,13 +403,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -418,13 +438,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -451,13 +473,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -495,6 +519,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -521,6 +546,7 @@ "script_type": "sh", "script": "echo \"Hello\"; sleep 10" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -542,6 +568,7 @@ "name": "default cred" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -573,7 +600,8 @@ "is_hidden": false, "is_mandatory": false } - ] + ], + "output_variables": [] } } ], @@ -605,6 +633,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -631,13 +660,15 @@ "name": "default cred" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_MySQLPackage_action_uninstall", @@ -658,13 +689,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -716,6 +749,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -738,13 +772,15 @@ "script_type": "sh", "script": "echo @@{foo}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_PHPPackage_action_uninstall", @@ -765,13 +801,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -845,6 +883,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -871,6 +910,7 @@ "script_type": "static_py3", "script": "print ('Hello!')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -892,13 +932,15 @@ "script_type": "sh", "script": "echo \"Hello\"\nsleep 10\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -931,6 +973,7 @@ }, "value": "password" }, + "cred_class": "static", "editables": {} } ], @@ -1203,7 +1246,8 @@ "choices": [ "var13_val1", "var13_val2" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1229,7 +1273,8 @@ "choices": [ "0", "1" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1255,7 +1300,8 @@ "choices": [ "30/02/2019", "31/06/2019" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1281,7 +1327,8 @@ "choices": [ "22:35:00", "10:35:00" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1307,7 +1354,8 @@ "choices": [ "30/02/2019 - 22:35:00", "31/06/2019 - 10:35:00" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1329,7 +1377,8 @@ "choices": [ "x\ny", "a\nb" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1355,7 +1404,8 @@ "choices": [ "var19_val1", "var19_val2" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1381,7 +1431,8 @@ "choices": [ "0", "1" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1407,7 +1458,8 @@ "choices": [ "30/02/2019", "31/06/2019" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1433,7 +1485,8 @@ "choices": [ "22:35:00", "10:35:00" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1459,7 +1512,8 @@ "choices": [ "30/02/2019 - 22:35:00", "31/06/2019 - 10:35:00" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1481,7 +1535,8 @@ "choices": [ "x\ny", "a\nb" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1521,7 +1576,8 @@ "var25": "$.title" }, "type": "HTTP" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1548,7 +1604,8 @@ "script_type": "static_py3", "script": "print ('0')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1575,7 +1632,8 @@ "script_type": "static_py3", "script": "print ('30/02/2019')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1602,7 +1660,8 @@ "script_type": "static_py3", "script": "print ('22:35:00')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1629,7 +1688,8 @@ "script_type": "static_py3", "script": "print ('30/02/2019 - 22:35:00')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1652,7 +1712,8 @@ "script_type": "static_py3", "script": "print ('x\ny')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1692,7 +1753,8 @@ "var31": "$.title" }, "type": "HTTP" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1719,7 +1781,8 @@ "script_type": "static_py3", "script": "print ('0,1')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1746,7 +1809,8 @@ "script_type": "static_py3", "script": "print ('30/02/2019,31/06/2019')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1773,7 +1837,8 @@ "script_type": "static_py3", "script": "print ('22:35:00,10:35:00')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1800,7 +1865,8 @@ "script_type": "static_py3", "script": "print ('30/02/2019 - 22:35:00,31/06/2019 - 10:35:00')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1823,7 +1889,8 @@ "script_type": "static_py3", "script": "print ('var36=x\ny,a\nb')", "type": "EXEC" - } + }, + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": false @@ -1991,6 +2058,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -2061,6 +2129,7 @@ "script_type": "sh", "script": "echo \"Hello\"\nsleep 10\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2080,6 +2149,7 @@ "name": "PHPService_test_action_runbook" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2149,6 +2219,7 @@ "foo_title": "$.title" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2202,6 +2273,7 @@ "foo_title": "$.title" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2251,6 +2323,7 @@ "foo_title": "$.title" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2296,6 +2369,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2345,6 +2419,7 @@ "foo_title": "$.title" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2362,6 +2437,7 @@ "script_type": "static_py3", "script": "print ('Hello World!')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2382,6 +2458,7 @@ "var1" ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2402,6 +2479,7 @@ "var2" ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2419,6 +2497,7 @@ "scaling_type": "SCALEOUT", "scaling_count": "1" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2435,6 +2514,7 @@ "attrs": { "interval_secs": 60 }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2452,6 +2532,7 @@ "scaling_type": "SCALEIN", "scaling_count": "1" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2494,7 +2575,8 @@ "is_hidden": true, "is_mandatory": true } - ] + ], + "output_variables": [] } } ] diff --git a/tests/existing_vm_example_with_target_endpoint/test_existing_vm_bp_with_endpoint_target_task.py b/tests/existing_vm_example_with_target_endpoint/test_existing_vm_bp_with_endpoint_target_task.py index cf7f2fecb..4da05c327 100644 --- a/tests/existing_vm_example_with_target_endpoint/test_existing_vm_bp_with_endpoint_target_task.py +++ b/tests/existing_vm_example_with_target_endpoint/test_existing_vm_bp_with_endpoint_target_task.py @@ -18,6 +18,8 @@ # for tcs from calm.dsl.store import Version from distutils.version import LooseVersion as LV +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp DSL_CONFIG = json.loads(read_local_file(".tests/config.json")) TEST_PC_IP = DSL_CONFIG["EXISTING_MACHINE"]["IP_1"] @@ -527,4 +529,17 @@ def test_json(): for cred in known_json["credential_definition_list"]: cred["cred_class"] = "static" + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json) + + # remove exec_target_reference from options introduced in DSL 4.0.0 + if LV(CALM_VERSION) < LV("4.0.0"): + for profile in known_json.get("app_profile_list", []): + for variable in profile.get("variable_list", []): + if variable.get("options"): + variable["options"].pop("exec_target_reference", None) + + remove_output_variables_from_bp(known_json) + remove_output_variables_from_bp(generated_json) + assert generated_json == known_json diff --git a/tests/helper/output_variables_helper.py b/tests/helper/output_variables_helper.py new file mode 100644 index 000000000..0487ff188 --- /dev/null +++ b/tests/helper/output_variables_helper.py @@ -0,0 +1,50 @@ +def remove_output_variables_from_bp(bp_json): + """ + This helper function is used to remove the output_variables/output_variable_list from the bp_json + + Args: + bp_json (dict): The bp_json dictionary + + Returns: + None + """ + + if "service_definition_list" in bp_json: + for service_definition in bp_json["service_definition_list"]: + for action in service_definition["action_list"]: + action["runbook"].pop("output_variables", None) + action["runbook"].pop("output_variable_list", None) + + if "package_definition_list" in bp_json: + for package_definition in bp_json["package_definition_list"]: + if "install_runbook" in package_definition["options"]: + package_definition["options"]["install_runbook"].pop( + "output_variables", None + ) + package_definition["options"]["install_runbook"].pop( + "output_variable_list", None + ) + if "uninstall_runbook" in package_definition["options"]: + package_definition["options"]["uninstall_runbook"].pop( + "output_variables", None + ) + package_definition["options"]["uninstall_runbook"].pop( + "output_variable_list", None + ) + + if "app_profile_list" in bp_json: + for app_profile in bp_json["app_profile_list"]: + for action in app_profile["action_list"]: + action["runbook"].pop("output_variables", None) + action["runbook"].pop("output_variable_list", None) + + if "substrate_definition_list" in bp_json: + for substrate in bp_json["substrate_definition_list"]: + for action in substrate["action_list"]: + action["runbook"].pop("output_variables", None) + action["runbook"].pop("output_variable_list", None) + + if "action_list" in bp_json: + for action in bp_json["action_list"]: + action["runbook"].pop("output_variables", None) + action["runbook"].pop("output_variable_list", None) diff --git a/tests/helper/status_map_helper.py b/tests/helper/status_map_helper.py new file mode 100644 index 000000000..0ee301e5a --- /dev/null +++ b/tests/helper/status_map_helper.py @@ -0,0 +1,38 @@ +def remove_status_map_from_bp(known_json): + """ + This helper function is used to remove the status_map_list from the known_json + for versions less than 3.9.0 + + Args: + known_json (dict): The known_json dictionary + + Returns: + None + """ + for service in known_json["service_definition_list"]: + for action in service["action_list"]: + for task in action["runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + + for app_profile in known_json["app_profile_list"]: + for action in app_profile["action_list"]: + for task in action["runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + + for substrate in known_json["substrate_definition_list"]: + for action in substrate["action_list"]: + for task in action["runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + + for package in known_json["package_definition_list"]: + if "install_runbook" in package["options"]: + for task in package["options"]["install_runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + if "uninstall_runbook" in package["options"]: + for task in package["options"]["uninstall_runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") diff --git a/tests/metadata/test_bp_metadata.py b/tests/metadata/test_bp_metadata.py index 6131c31c5..e54d37bc7 100644 --- a/tests/metadata/test_bp_metadata.py +++ b/tests/metadata/test_bp_metadata.py @@ -18,6 +18,7 @@ LOCAL_PROJECTNAME_FILE = "tests/metadata/blueprint/.local/project_name" +@pytest.mark.quotas class TestBlueprintMetadata: def setup_method(self): """Method to create project""" diff --git a/tests/metadata/test_runbook_metadata.py b/tests/metadata/test_runbook_metadata.py index ea7717f6a..5bcd7b7ed 100644 --- a/tests/metadata/test_runbook_metadata.py +++ b/tests/metadata/test_runbook_metadata.py @@ -18,6 +18,7 @@ LOCAL_PROJECTNAME_FILE = "tests/metadata/runbook/.local/project_name" +@pytest.mark.quotas class TestRunbookMetadata: def setup_method(self): """ diff --git a/tests/ndb_runbooks/nutanixdb_postgres_clone.json b/tests/ndb_runbooks/nutanixdb_postgres_clone.json index 4fc13fe68..55b0b8484 100644 --- a/tests/ndb_runbooks/nutanixdb_postgres_clone.json +++ b/tests/ndb_runbooks/nutanixdb_postgres_clone.json @@ -3,302 +3,305 @@ "credential_definition_list": [], "client_attrs": {}, "runbook": { - "name": "nutanixdb_postgres_clone_runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "nutanixdb_postgres_clone_dag" - }, - "task_definition_list": [ + "name": "nutanixdb_postgres_clone_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "nutanixdb_postgres_clone_dag" + }, + "task_definition_list": [ + { + "name": "nutanixdb_postgres_clone_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [ { - "name": "nutanixdb_postgres_clone_dag", - "description": "", - "type": "DAG", - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [ - { - "kind": "app_task", - "name": "postgres_clone_task_name" - } - ], - "variable_list": [], - "retries": "", - "timeout_secs": "" + "kind": "app_task", + "name": "postgres_clone_task_name" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "postgres_clone_task_name", + "description": "", + "type": "RT_OPERATION", + "attrs": { + "type": "RT_OPERATION", + "resource_type_reference": { + "uuid": "19c07696-473b-4c48-ba7d-133dff3f98a6", + "name": "Postgres Database Instance", + "kind": "resource_type" }, - { - "name": "postgres_clone_task_name", + "action_reference": { + "kind": "app_action", + "name": "Clone from Time Machine", + "uuid": "c0d02737-47d6-48fa-b7d7-991b532ae79a" + }, + "inarg_list": [ + { + "name": "clone_from_time_machine_nutanix_ndb_database__compute_profile_id", + "description": "", + "type": "LOCAL", + "label": "Compute Profile", + "attrs": {}, + "val_type": "STRING", + "value": "b17485e8-e063-469c-b8ca-522fb89dc16e", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__database_parameter_profile_id", + "description": "", + "type": "LOCAL", + "label": "Database Parameter Profile", + "attrs": {}, + "val_type": "STRING", + "value": "0d27ebd3-e017-49ab-ac0e-4e373d4a72c4", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__description", + "description": "", + "type": "LOCAL", + "label": "Description", + "attrs": {}, + "val_type": "STRING", + "value": "Sample description of db server", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__name", + "description": "", + "type": "LOCAL", + "label": "Instance Name", + "attrs": {}, + "val_type": "STRING", + "value": "post_inst_@@{calm_time}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__network_profile_id", + "description": "", + "type": "LOCAL", + "label": "Network Profile", + "attrs": {}, + "val_type": "STRING", + "value": "4d1e8576-03b4-45c5-b61f-94810d784019", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__node_count", + "description": "", + "type": "LOCAL", + "label": "Node Count", + "attrs": {}, + "val_type": "INT", + "value": "1", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__nodes__0__compute_profile_id", + "description": "", + "type": "LOCAL", + "label": "Compute Profile", + "attrs": {}, + "val_type": "STRING", + "value": "b17485e8-e063-469c-b8ca-522fb89dc16e", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__nodes__0__network_profile_id", + "description": "", + "type": "LOCAL", + "label": "Network Profile", + "attrs": {}, + "val_type": "STRING", + "value": "4d1e8576-03b4-45c5-b61f-94810d784019", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__nodes__0__nx_cluster_id", + "description": "", + "type": "LOCAL", + "label": "Nutanix Cluster", + "attrs": {}, + "val_type": "STRING", + "value": "@@{cluster_uuid}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__nodes__0__vm_name", + "description": "", + "type": "LOCAL", + "label": "Database Server VM Name", + "attrs": {}, + "val_type": "STRING", + "value": "new_db_@@{calm_time}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__nx_cluster_id", + "description": "", + "type": "LOCAL", + "label": "Nutanix Cluster", + "attrs": {}, + "val_type": "STRING", + "value": "@@{cluster_uuid}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__postgresql_info__0__db_password", + "description": "", + "type": "SECRET", + "label": "Password", + "attrs": {}, + "val_type": "STRING", + "value": "Nutanix.123", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__postgresql_info__0__post_clone_cmd", + "description": "", + "type": "LOCAL", + "label": "Post Clone Command", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__postgresql_info__0__pre_clone_cmd", + "description": "", + "type": "LOCAL", + "label": "Pre Clone Command", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__postgresql_info__0__vm_name", + "description": "", + "type": "LOCAL", + "label": "Database server VM Name", + "attrs": {}, + "val_type": "STRING", + "value": "new_db_@@{calm_time}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__snapshot_id", + "description": "", + "type": "LOCAL", + "label": "Snapshot", + "attrs": {}, + "val_type": "STRING", + "value": "@@{snapshot_uuid}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__ssh_public_key", + "description": "", + "type": "SECRET", + "label": "SSH Public Key for Node Access", + "attrs": {}, + "val_type": "STRING", + "value": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC5GGnkZH75LzRFxOh4LcM0owSLn+1nvxP8tcw2PeXmdi82N1Fe8RZ3m5SkJqxJO2BvC39McyovNFIXC2LN2HeXuvovB6C0v1PGNb7oq2I5QrnRRUp1Tm4OJQN9cNZZ8MYaAcd0LokodrXgwpUQ4zlDdmlza1sDGQxXg4Dxvh5N/Y+rMjYdFbRSYmzzqI5aSHty8Shg9rbrqebFhTVCvzyQJzE/0PS3WUnCAbhLovLI/sdnTyM0CAm+Y6ui+1EFQkyg2VkbjJ6Y2foFRPJufqrnQS6S/njUeD5US3W4r8nMOxRZutRlFoado/yR+9MOkGn57NhIkMhji8wTH6SVtq2m09P+3/1Z9P+rASIS0rmH80XwwUVhfSyJ/J5dN0Axu8Bfqa9T40VDRRsoVKs79BRFr/5XRayS/O6jfGw6biYKJLeU9vV7OxtjzIuMDlbnsshuCcGtNMfI9W73F9VlKfKdQx2n547KEx79DlEJg4mtoaxkguvDo/aTH+0IJTF2BMh2iqL23ie6BkRVjHfhwWFM2WDRdHhDLcuAYSPP/sTuOEZgkElzK+ODahAXoglgTkqJeq2MiJ3tAmi39k9EKuTDR2xn1BLo/B4dMq1jkQJwVfEP+jD4eRTrlhZ8ZycIZgeY4c5MGqUNW9WfFuKOHogWEWMbuM3C8LLUFB4T1H5yDQ== mitesh.madaan@nutanix.com", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__time_machine_id", + "description": "", + "type": "LOCAL", + "label": "Target Time Machine", + "attrs": {}, + "val_type": "STRING", + "value": "@@{timemachine_uuid}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__time_zone", + "description": "", + "type": "LOCAL", + "label": "Timezone", + "attrs": {}, + "val_type": "STRING", + "value": "UTC", + "data_type": "BASE", + "is_hidden": true, + "is_mandatory": false + }, + { + "name": "clone_from_time_machine_nutanix_ndb_database__vm_password", "description": "", - "type": "RT_OPERATION", - "attrs": { - "type": "RT_OPERATION", - "resource_type_reference": { - "uuid": "7d60e0a6-b021-4c22-b6a5-2fd311930709", - "name": "Postgres Database Instance", - "kind": "resource_type" - }, - "action_reference": { - "kind": "app_action", - "name": "Clone from Time Machine", - "uuid": "546269c6-fa7a-4f29-ad86-49035d725cd2" - }, - "inarg_list": [ - { - "name": "clone_from_time_machine_nutanix_ndb_database__name", - "description": "", - "type": "LOCAL", - "label": "Instance Name", - "attrs": {}, - "val_type": "STRING", - "value": "post_inst_@@{calm_time}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__nx_cluster_id", - "description": "", - "type": "LOCAL", - "label": "Nutanix Cluster", - "attrs": {}, - "val_type": "STRING", - "value": "@@{cluster_uuid}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__ssh_public_key", - "description": "", - "type": "SECRET", - "label": "SSH Public Key for Node Access", - "attrs": {}, - "val_type": "STRING", - "value": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC5GGnkZH75LzRFxOh4LcM0owSLn+1nvxP8tcw2PeXmdi82N1Fe8RZ3m5SkJqxJO2BvC39McyovNFIXC2LN2HeXuvovB6C0v1PGNb7oq2I5QrnRRUp1Tm4OJQN9cNZZ8MYaAcd0LokodrXgwpUQ4zlDdmlza1sDGQxXg4Dxvh5N/Y+rMjYdFbRSYmzzqI5aSHty8Shg9rbrqebFhTVCvzyQJzE/0PS3WUnCAbhLovLI/sdnTyM0CAm+Y6ui+1EFQkyg2VkbjJ6Y2foFRPJufqrnQS6S/njUeD5US3W4r8nMOxRZutRlFoado/yR+9MOkGn57NhIkMhji8wTH6SVtq2m09P+3/1Z9P+rASIS0rmH80XwwUVhfSyJ/J5dN0Axu8Bfqa9T40VDRRsoVKs79BRFr/5XRayS/O6jfGw6biYKJLeU9vV7OxtjzIuMDlbnsshuCcGtNMfI9W73F9VlKfKdQx2n547KEx79DlEJg4mtoaxkguvDo/aTH+0IJTF2BMh2iqL23ie6BkRVjHfhwWFM2WDRdHhDLcuAYSPP/sTuOEZgkElzK+ODahAXoglgTkqJeq2MiJ3tAmi39k9EKuTDR2xn1BLo/B4dMq1jkQJwVfEP+jD4eRTrlhZ8ZycIZgeY4c5MGqUNW9WfFuKOHogWEWMbuM3C8LLUFB4T1H5yDQ== mitesh.madaan@nutanix.com", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__node_count", - "description": "", - "type": "LOCAL", - "label": "Node Count", - "attrs": {}, - "val_type": "INT", - "value": "1", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__description", - "description": "", - "type": "LOCAL", - "label": "Description", - "attrs": {}, - "val_type": "STRING", - "value": "Sample description of db server", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__compute_profile_id", - "description": "", - "type": "LOCAL", - "label": "Compute Profile", - "attrs": {}, - "val_type": "STRING", - "value": "85b669f6-604e-41e1-b5d4-429f8fdc077d", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__network_profile_id", - "description": "", - "type": "LOCAL", - "label": "Network Profile", - "attrs": {}, - "val_type": "STRING", - "value": "c49bc35a-2adc-49fd-bf2e-0ccc430ea36d", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__nodes__0__vm_name", - "description": "", - "type": "LOCAL", - "label": "Database Server VM Name", - "attrs": {}, - "val_type": "STRING", - "value": "new_db_@@{calm_time}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__vm_password", - "description": "", - "type": "SECRET", - "label": "Password", - "attrs": {}, - "val_type": "STRING", - "value": "abc123", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__nodes__0__compute_profile_id", - "description": "", - "type": "LOCAL", - "label": "Compute Profile", - "attrs": {}, - "val_type": "STRING", - "value": "85b669f6-604e-41e1-b5d4-429f8fdc077d", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__nodes__0__network_profile_id", - "description": "", - "type": "LOCAL", - "label": "Network Profile", - "attrs": {}, - "val_type": "STRING", - "value": "c49bc35a-2adc-49fd-bf2e-0ccc430ea36d", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__nodes__0__nx_cluster_id", - "description": "", - "type": "LOCAL", - "label": "Nutanix Cluster", - "attrs": {}, - "val_type": "STRING", - "value": "eaa57d0a-8e0d-49e5-b19c-6d593f52937d", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__database_parameter_profile_id", - "description": "", - "type": "LOCAL", - "label": "Database Parameter Profile", - "attrs": {}, - "val_type": "STRING", - "value": "552d3408-fb9f-4cf3-9cfd-92b479635c4b", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__postgresql_info__0__vm_name", - "description": "", - "type": "LOCAL", - "label": "Database server VM Name", - "attrs": {}, - "val_type": "STRING", - "value": "new_db_@@{calm_time}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__postgresql_info__0__pre_clone_cmd", - "description": "", - "type": "LOCAL", - "label": "Pre Clone Command", - "attrs": {}, - "val_type": "STRING", - "value": "", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__postgresql_info__0__post_clone_cmd", - "description": "", - "type": "LOCAL", - "label": "Post Clone Command", - "attrs": {}, - "val_type": "STRING", - "value": "", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__postgresql_info__0__db_password", - "description": "", - "type": "SECRET", - "label": "Password", - "attrs": {}, - "val_type": "STRING", - "value": "Nutanix.123", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__time_machine_id", - "description": "", - "type": "LOCAL", - "label": "Target Time Machine", - "attrs": {}, - "val_type": "STRING", - "value": "@@{timemachine_uuid}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__snapshot_id", - "description": "", - "type": "LOCAL", - "label": "Snapshot", - "attrs": {}, - "val_type": "STRING", - "value": "@@{snapshot_uuid}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "clone_from_time_machine_nutanix_ndb_database__time_zone", - "description": "", - "type": "LOCAL", - "label": "Timezone", - "attrs": {}, - "val_type": "STRING", - "value": "UTC", - "data_type": "BASE", - "is_hidden": true, - "is_mandatory": false - } - ], - "output_variables": {}, - "tag": "Database", - "account_reference": { - "kind": "account", - "name": "ndb-account", - "uuid": "65d3ec1d-8044-4c5c-af50-881acb3c3a3d" - } - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" + "type": "SECRET", + "label": "Password", + "attrs": {}, + "val_type": "STRING", + "value": "abc123", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + } + ], + "output_variables": {}, + "tag": "Database", + "account_reference": { + "kind": "account", + "name": "dnd_era_secondary_account", + "uuid": "0bba3490-6677-49b3-a6d5-a67ef63883b0" } - ], - "variable_list": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variable_list": [] } } \ No newline at end of file diff --git a/tests/ndb_runbooks/nutanixdb_postgres_create.json b/tests/ndb_runbooks/nutanixdb_postgres_create.json index cf45fa6d7..8df9a52e5 100644 --- a/tests/ndb_runbooks/nutanixdb_postgres_create.json +++ b/tests/ndb_runbooks/nutanixdb_postgres_create.json @@ -1,699 +1,702 @@ { - "endpoint_definition_list": [], - "credential_definition_list": [], - "client_attrs": {}, - "runbook": { - "name": "nutanixdb_postgres_create_runbook", - "description": "", - "main_task_local_reference": { - "kind": "app_task", - "name": "nutanixdb_postgres_create_dag" - }, - "task_definition_list": [ - { - "name": "nutanixdb_postgres_create_dag", - "description": "", - "type": "DAG", - "attrs": { - "edges": [] - }, - "child_tasks_local_reference_list": [ - { - "kind": "app_task", - "name": "postgres_create_task_name" - } - ], - "variable_list": [], - "retries": "", - "timeout_secs": "" - }, - { - "name": "postgres_create_task_name", - "description": "", - "type": "RT_OPERATION", - "attrs": { - "type": "RT_OPERATION", - "resource_type_reference": { - "uuid": "722a4722-beb9-49ec-8229-80e552091a24", - "name": "Postgres Database Instance", - "kind": "resource_type" - }, - "action_reference": { - "kind": "app_action", - "name": "Create", - "uuid": "a0205b18-8340-465b-9c4c-588ff19bbea6" - }, - "inarg_list": [ - { - "name": "create_nutanix_ndb_database__vm_password", - "description": "", - "type": "SECRET", - "label": "Password", - "attrs": {}, - "val_type": "STRING", - "value": "abc123", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__softwareprofileid", - "description": "", - "type": "LOCAL", - "label": "Software Profile", - "attrs": {}, - "val_type": "STRING", - "value": "b2f90b89-e2df-430e-a870-0f0701410938", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__softwareprofileversionid", - "description": "", - "type": "LOCAL", - "label": "Software Profile Version", - "attrs": {}, - "val_type": "STRING", - "value": "9c602373-c232-4842-82c3-86950210df9f", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__computeprofileid", - "description": "", - "type": "LOCAL", - "label": "Compute Profile", - "attrs": {}, - "val_type": "STRING", - "value": "f12cfc09-84b6-41ff-95d9-83f0f9cba1d4", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__networkprofileid", - "description": "", - "type": "LOCAL", - "label": "Network Profile", - "attrs": {}, - "val_type": "STRING", - "value": "d169d0cb-bc9e-48e7-b773-c6ee9626251e", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__nxclusterid", - "description": "", - "type": "LOCAL", - "label": "Cluster", - "attrs": {}, - "val_type": "STRING", - "value": "@@{cluster_uuid}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__sshpublickey", - "description": "", - "type": "SECRET", - "label": "SSH Public Key for Node Access", - "attrs": {}, - "val_type": "STRING", - "value": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC5GGnkZH75LzRFxOh4LcM0owSLn+1nvxP8tcw2PeXmdi82N1Fe8RZ3m5SkJqxJO2BvC39McyovNFIXC2LN2HeXuvovB6C0v1PGNb7oq2I5QrnRRUp1Tm4OJQN9cNZZ8MYaAcd0LokodrXgwpUQ4zlDdmlza1sDGQxXg4Dxvh5N/Y+rMjYdFbRSYmzzqI5aSHty8Shg9rbrqebFhTVCvzyQJzE/0PS3WUnCAbhLovLI/sdnTyM0CAm+Y6ui+1EFQkyg2VkbjJ6Y2foFRPJufqrnQS6S/njUeD5US3W4r8nMOxRZutRlFoado/yR+9MOkGn57NhIkMhji8wTH6SVtq2m09P+3/1Z9P+rASIS0rmH80XwwUVhfSyJ/J5dN0Axu8Bfqa9T40VDRRsoVKs79BRFr/5XRayS/O6jfGw6biYKJLeU9vV7OxtjzIuMDlbnsshuCcGtNMfI9W73F9VlKfKdQx2n547KEx79DlEJg4mtoaxkguvDo/aTH+0IJTF2BMh2iqL23ie6BkRVjHfhwWFM2WDRdHhDLcuAYSPP/sTuOEZgkElzK+ODahAXoglgTkqJeq2MiJ3tAmi39k9EKuTDR2xn1BLo/B4dMq1jkQJwVfEP+jD4eRTrlhZ8ZycIZgeY4c5MGqUNW9WfFuKOHogWEWMbuM3C8LLUFB4T1H5yDQ== username@nutanix.com", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__nodes__0__vmname", - "description": "", - "type": "LOCAL", - "label": "Database Server VM Name", - "attrs": {}, - "val_type": "STRING", - "value": "new_db_@@{calm_time}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__nodes__0__networkprofileid", - "description": "", - "type": "LOCAL", - "label": "", - "attrs": {}, - "val_type": "STRING", - "value": "d169d0cb-bc9e-48e7-b773-c6ee9626251e", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__actionarguments__0__name", - "description": "", - "type": "INPUT", - "label": "", - "attrs": { - "is_internal": false, - "type": "", - "omit_on_empty": true - }, - "val_type": "STRING", - "value": "dbserver_description", - "data_type": "BASE", - "is_hidden": true, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__actionarguments__0__value", - "description": "", - "type": "INPUT", - "label": "Database Server Description", - "attrs": { - "is_internal": false, - "type": "", - "omit_on_empty": true - }, - "val_type": "STRING", - "value": "Sample description of db server", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__name", - "description": "", - "type": "LOCAL", - "label": "Instance Name", - "attrs": {}, - "val_type": "STRING", - "value": "post_inst_@@{calm_time}@@", - "data_type": "BASE", - "regex": { - "value": "^(|[a-zA-Z][A-Za-z0-9_.-]+)$", - "should_validate": true - }, - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__description", - "description": "", - "type": "LOCAL", - "label": "Description", - "attrs": {}, - "val_type": "STRING", - "value": "Sample description of postgres instances", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__dbparameterprofileid", - "description": "", - "type": "LOCAL", - "label": "Database Parameter Profile", - "attrs": {}, - "val_type": "STRING", - "value": "7be7f568-8285-4849-b661-a9f57070231d", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__postgresql_info__0__listener_port", - "description": "", - "type": "LOCAL", - "label": "Port", - "attrs": {}, - "val_type": "STRING", - "value": "5432", - "data_type": "BASE", - "regex": { - "value": "^(|[1-9][0-9]{2}|[1-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$", - "should_validate": true - }, - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__postgresql_info__0__database_size", - "description": "", - "type": "LOCAL", - "label": "Size (GiB)", - "attrs": {}, - "val_type": "STRING", - "value": "200", - "data_type": "BASE", - "regex": { - "value": "^(|([1-9][0-9]{1,5}|10{6}))$", - "should_validate": true - }, - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__postgresql_info__0__database_names", - "description": "", - "type": "LOCAL", - "label": "Name of Initial Database", - "attrs": {}, - "val_type": "STRING", - "value": "TEST_DB_01", - "data_type": "BASE", - "regex": { - "value": "^(|[a-zA-Z_$][\\w$]{0,62})$", - "should_validate": true - }, - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__postgresql_info__0__db_password", - "description": "", - "type": "SECRET", - "label": "Password", - "attrs": {}, - "val_type": "STRING", - "value": "DB_PASS", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__postgresql_info__0__pre_create_script", - "description": "", - "type": "LOCAL", - "label": "Pre Create Command", - "attrs": {}, - "val_type": "STRING", - "value": "", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__postgresql_info__0__post_create_script", - "description": "", - "type": "LOCAL", - "label": "Post Create Command", - "attrs": {}, - "val_type": "STRING", - "value": "", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__name", - "description": "", - "type": "LOCAL", - "label": "Name", - "attrs": {}, - "val_type": "STRING", - "value": "inst_@@{calm_time}@@_TM", - "data_type": "BASE", - "regex": { - "value": "^(|[a-zA-Z][\\w.-]*)$", - "should_validate": true - }, - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__description", - "description": "", - "type": "LOCAL", - "label": "Description", - "attrs": {}, - "val_type": "STRING", - "value": "This is time machine's description", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__slaid", - "description": "", - "type": "LOCAL", - "label": "SLA", - "attrs": {}, - "val_type": "STRING", - "value": "501361d9-db5e-47af-8102-ff9354b9bd81", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__snapshottimeofday__0__hours", - "description": "", - "type": "LOCAL", - "label": "Daily Snapshot at", - "attrs": {}, - "val_type": "INT", - "value": "12", - "data_type": "BASE", - "options": { - "type": "PREDEFINED", - "choices": [ - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - "17", - "18", - "19", - "20", - "21", - "22", - "23" - ] - }, - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__snapshottimeofday__0__minutes", - "description": "", - "type": "LOCAL", - "label": "Daily Snapshot at", - "attrs": {}, - "val_type": "INT", - "value": "0", - "data_type": "BASE", - "options": { - "type": "PREDEFINED", - "choices": [ - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - "17", - "18", - "19", - "20", - "21", - "22", - "23", - "24", - "25", - "26", - "27", - "28", - "29", - "30", - "31", - "32", - "33", - "34", - "35", - "36", - "37", - "38", - "39", - "40", - "41", - "42", - "43", - "44", - "45", - "46", - "47", - "48", - "49", - "50", - "51", - "52", - "53", - "54", - "55", - "56", - "57", - "58", - "59" - ] - }, - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__snapshottimeofday__0__seconds", - "description": "", - "type": "LOCAL", - "label": "Daily Snapshot at", - "attrs": {}, - "val_type": "INT", - "value": "0", - "data_type": "BASE", - "options": { - "type": "PREDEFINED", - "choices": [ - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - "17", - "18", - "19", - "20", - "21", - "22", - "23", - "24", - "25", - "26", - "27", - "28", - "29", - "30", - "31", - "32", - "33", - "34", - "35", - "36", - "37", - "38", - "39", - "40", - "41", - "42", - "43", - "44", - "45", - "46", - "47", - "48", - "49", - "50", - "51", - "52", - "53", - "54", - "55", - "56", - "57", - "58", - "59" - ] - }, - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__continuousschedule__0__logbackupinterval", - "description": "", - "type": "LOCAL", - "label": "Log Catch Up Every", - "attrs": {}, - "val_type": "INT", - "value": "60", - "data_type": "BASE", - "options": { - "type": "PREDEFINED", - "choices": [ - "15", - "30", - "60", - "90", - "120" - ] - }, - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__continuousschedule__0__snapshotsperday", - "description": "", - "type": "LOCAL", - "label": "Snapshots Per Day", - "attrs": {}, - "val_type": "INT", - "value": "1", - "data_type": "BASE", - "options": { - "type": "PREDEFINED", - "choices": [ - "1", - "2", - "3", - "4", - "5", - "6" - ] - }, - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__weeklyschedule__0__dayofweek", - "description": "", - "type": "LOCAL", - "label": "Weekly Snapshot on", - "attrs": {}, - "val_type": "STRING", - "value": "WEDNESDAY", - "data_type": "BASE", - "options": { - "type": "PREDEFINED", - "choices": [ - "MONDAY", - "TUESDAY", - "WEDNESDAY", - "THURSDAY", - "FRIDAY", - "SATURDAY", - "SUNDAY" - ] - }, - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__monthlyschedule__0__dayofmonth", - "description": "", - "type": "LOCAL", - "label": "Monthly Snapshot on the", - "attrs": {}, - "val_type": "INT", - "value": "17", - "data_type": "BASE", - "options": { - "type": "PREDEFINED", - "choices": [ - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - "17", - "18", - "19", - "20", - "21", - "22", - "23", - "24", - "25", - "26", - "27", - "28" - ] - }, - "is_hidden": false, - "is_mandatory": false - }, - { - "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__quartelyschedule__0__startmonth", - "description": "", - "type": "LOCAL", - "label": "Quarterly Snapshot in", - "attrs": {}, - "val_type": "STRING", - "value": "FEBRUARY", - "data_type": "BASE", - "options": { - "type": "PREDEFINED", - "choices": [ - "JANUARY", - "FEBRUARY", - "MARCH" - ] - }, - "is_hidden": false, - "is_mandatory": false - } - ], - "output_variables": { - "create_tag": "tags" - }, - "tag": "Database", - "account_reference": { - "kind": "account", - "name": "dnd_era_secondary_account", - "uuid": "12c995cb-3b82-47ef-9e4d-efd8af07e294" - } - }, - "child_tasks_local_reference_list": [], - "variable_list": [], - "retries": "", - "timeout_secs": "" - } - ], - "variable_list": [] - } -} \ No newline at end of file + "endpoint_definition_list": [], + "credential_definition_list": [], + "client_attrs": {}, + "runbook": { + "name": "nutanixdb_postgres_create_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "nutanixdb_postgres_create_dag" + }, + "task_definition_list": [ + { + "name": "nutanixdb_postgres_create_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "postgres_create_task_name" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "postgres_create_task_name", + "description": "", + "type": "RT_OPERATION", + "attrs": { + "type": "RT_OPERATION", + "resource_type_reference": { + "uuid": "19c07696-473b-4c48-ba7d-133dff3f98a6", + "name": "Postgres Database Instance", + "kind": "resource_type" + }, + "action_reference": { + "kind": "app_action", + "name": "Create", + "uuid": "5b492bb9-391e-41a9-a1d1-33e10f0a7f61" + }, + "inarg_list": [ + { + "name": "create_nutanix_ndb_database__actionarguments__0__name", + "description": "", + "type": "INPUT", + "label": "", + "attrs": { + "type": "", + "is_internal": false, + "omit_on_empty": true + }, + "val_type": "STRING", + "value": "dbserver_description", + "data_type": "BASE", + "is_hidden": true, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__actionarguments__0__value", + "description": "", + "type": "INPUT", + "label": "Database Server Description", + "attrs": { + "type": "", + "is_internal": false, + "omit_on_empty": true + }, + "val_type": "STRING", + "value": "Sample description of db server", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__computeprofileid", + "description": "", + "type": "LOCAL", + "label": "Compute Profile", + "attrs": {}, + "val_type": "STRING", + "value": "b17485e8-e063-469c-b8ca-522fb89dc16e", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__dbparameterprofileid", + "description": "", + "type": "LOCAL", + "label": "Database Parameter Profile", + "attrs": {}, + "val_type": "STRING", + "value": "0d27ebd3-e017-49ab-ac0e-4e373d4a72c4", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__description", + "description": "", + "type": "LOCAL", + "label": "Description", + "attrs": {}, + "val_type": "STRING", + "value": "Sample description of postgres instances", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__name", + "description": "", + "type": "LOCAL", + "label": "Instance Name", + "attrs": {}, + "val_type": "STRING", + "value": "post_inst_@@{calm_time}@@", + "data_type": "BASE", + "regex": { + "value": "^(|[a-zA-Z][A-Za-z0-9_.-]+)$", + "should_validate": true + }, + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__networkprofileid", + "description": "", + "type": "LOCAL", + "label": "Network Profile", + "attrs": {}, + "val_type": "STRING", + "value": "4d1e8576-03b4-45c5-b61f-94810d784019", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__nodes__0__networkprofileid", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "4d1e8576-03b4-45c5-b61f-94810d784019", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__nodes__0__vmname", + "description": "", + "type": "LOCAL", + "label": "Database Server VM Name", + "attrs": {}, + "val_type": "STRING", + "value": "new_db_@@{calm_time}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__nxclusterid", + "description": "", + "type": "LOCAL", + "label": "Cluster", + "attrs": {}, + "val_type": "STRING", + "value": "@@{cluster_uuid}@@", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__postgresql_info__0__database_names", + "description": "", + "type": "LOCAL", + "label": "Name of Initial Database", + "attrs": {}, + "val_type": "STRING", + "value": "TEST_DB_01", + "data_type": "BASE", + "regex": { + "value": "^(|[a-zA-Z_$][\\w$]{0,62})$", + "should_validate": true + }, + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__postgresql_info__0__database_size", + "description": "", + "type": "LOCAL", + "label": "Size (GiB)", + "attrs": {}, + "val_type": "STRING", + "value": "200", + "data_type": "BASE", + "regex": { + "value": "^(|([1-9][0-9]{1,5}|10{6}))$", + "should_validate": true + }, + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__postgresql_info__0__db_password", + "description": "", + "type": "SECRET", + "label": "Password", + "attrs": {}, + "val_type": "STRING", + "value": "DB_PASS", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__postgresql_info__0__listener_port", + "description": "", + "type": "LOCAL", + "label": "Port", + "attrs": {}, + "val_type": "STRING", + "value": "5432", + "data_type": "BASE", + "regex": { + "value": "^(|[1-9][0-9]{2}|[1-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$", + "should_validate": true + }, + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__postgresql_info__0__post_create_script", + "description": "", + "type": "LOCAL", + "label": "Post Create Command", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__postgresql_info__0__pre_create_script", + "description": "", + "type": "LOCAL", + "label": "Pre Create Command", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__softwareprofileid", + "description": "", + "type": "LOCAL", + "label": "Software Profile", + "attrs": {}, + "val_type": "STRING", + "value": "7a70b75c-368a-4402-bfa4-6ed918047bea", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__softwareprofileversionid", + "description": "", + "type": "LOCAL", + "label": "Software Profile Version", + "attrs": {}, + "val_type": "STRING", + "value": "bed563e6-ee08-418e-bb00-5a487a4191e0", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__sshpublickey", + "description": "", + "type": "SECRET", + "label": "SSH Public Key for Node Access", + "attrs": {}, + "val_type": "STRING", + "value": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQC5GGnkZH75LzRFxOh4LcM0owSLn+1nvxP8tcw2PeXmdi82N1Fe8RZ3m5SkJqxJO2BvC39McyovNFIXC2LN2HeXuvovB6C0v1PGNb7oq2I5QrnRRUp1Tm4OJQN9cNZZ8MYaAcd0LokodrXgwpUQ4zlDdmlza1sDGQxXg4Dxvh5N/Y+rMjYdFbRSYmzzqI5aSHty8Shg9rbrqebFhTVCvzyQJzE/0PS3WUnCAbhLovLI/sdnTyM0CAm+Y6ui+1EFQkyg2VkbjJ6Y2foFRPJufqrnQS6S/njUeD5US3W4r8nMOxRZutRlFoado/yR+9MOkGn57NhIkMhji8wTH6SVtq2m09P+3/1Z9P+rASIS0rmH80XwwUVhfSyJ/J5dN0Axu8Bfqa9T40VDRRsoVKs79BRFr/5XRayS/O6jfGw6biYKJLeU9vV7OxtjzIuMDlbnsshuCcGtNMfI9W73F9VlKfKdQx2n547KEx79DlEJg4mtoaxkguvDo/aTH+0IJTF2BMh2iqL23ie6BkRVjHfhwWFM2WDRdHhDLcuAYSPP/sTuOEZgkElzK+ODahAXoglgTkqJeq2MiJ3tAmi39k9EKuTDR2xn1BLo/B4dMq1jkQJwVfEP+jD4eRTrlhZ8ZycIZgeY4c5MGqUNW9WfFuKOHogWEWMbuM3C8LLUFB4T1H5yDQ== username@nutanix.com", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__description", + "description": "", + "type": "LOCAL", + "label": "Description", + "attrs": {}, + "val_type": "STRING", + "value": "This is time machine's description", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__name", + "description": "", + "type": "LOCAL", + "label": "Name", + "attrs": {}, + "val_type": "STRING", + "value": "inst_@@{calm_time}@@_TM", + "data_type": "BASE", + "regex": { + "value": "^(|[a-zA-Z][\\w.-]*)$", + "should_validate": true + }, + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__continuousschedule__0__logbackupinterval", + "description": "", + "type": "LOCAL", + "label": "Log Catch Up Every", + "attrs": {}, + "val_type": "INT", + "value": "60", + "data_type": "BASE", + "options": { + "type": "PREDEFINED", + "choices": [ + "15", + "30", + "60", + "90", + "120" + ] + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__continuousschedule__0__snapshotsperday", + "description": "", + "type": "LOCAL", + "label": "Snapshots Per Day", + "attrs": {}, + "val_type": "INT", + "value": "1", + "data_type": "BASE", + "options": { + "type": "PREDEFINED", + "choices": [ + "1", + "2", + "3", + "4", + "5", + "6" + ] + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__monthlyschedule__0__dayofmonth", + "description": "", + "type": "LOCAL", + "label": "Monthly Snapshot on the", + "attrs": {}, + "val_type": "INT", + "value": "17", + "data_type": "BASE", + "options": { + "type": "PREDEFINED", + "choices": [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28" + ] + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__quartelyschedule__0__startmonth", + "description": "", + "type": "LOCAL", + "label": "Quarterly Snapshot in", + "attrs": {}, + "val_type": "STRING", + "value": "FEBRUARY", + "data_type": "BASE", + "options": { + "type": "PREDEFINED", + "choices": [ + "JANUARY", + "FEBRUARY", + "MARCH" + ] + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__snapshottimeofday__0__hours", + "description": "", + "type": "LOCAL", + "label": "Daily Snapshot at", + "attrs": {}, + "val_type": "INT", + "value": "12", + "data_type": "BASE", + "options": { + "type": "PREDEFINED", + "choices": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23" + ] + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__snapshottimeofday__0__minutes", + "description": "", + "type": "LOCAL", + "label": "Daily Snapshot at", + "attrs": {}, + "val_type": "INT", + "value": "0", + "data_type": "BASE", + "options": { + "type": "PREDEFINED", + "choices": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "43", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "52", + "53", + "54", + "55", + "56", + "57", + "58", + "59" + ] + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__snapshottimeofday__0__seconds", + "description": "", + "type": "LOCAL", + "label": "Daily Snapshot at", + "attrs": {}, + "val_type": "INT", + "value": "0", + "data_type": "BASE", + "options": { + "type": "PREDEFINED", + "choices": [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "43", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "52", + "53", + "54", + "55", + "56", + "57", + "58", + "59" + ] + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__schedule__0__weeklyschedule__0__dayofweek", + "description": "", + "type": "LOCAL", + "label": "Weekly Snapshot on", + "attrs": {}, + "val_type": "STRING", + "value": "WEDNESDAY", + "data_type": "BASE", + "options": { + "type": "PREDEFINED", + "choices": [ + "MONDAY", + "TUESDAY", + "WEDNESDAY", + "THURSDAY", + "FRIDAY", + "SATURDAY", + "SUNDAY" + ] + }, + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "create_nutanix_ndb_database__timemachineinfo__0__slaid", + "description": "", + "type": "LOCAL", + "label": "SLA", + "attrs": {}, + "val_type": "STRING", + "value": "501361d9-db5e-47af-8102-ff9354b9bd81", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "create_nutanix_ndb_database__vm_password", + "description": "", + "type": "SECRET", + "label": "Password", + "attrs": {}, + "val_type": "STRING", + "value": "abc123", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "output_variables": { + "create_tag": "tags" + }, + "tag": "Database", + "account_reference": { + "kind": "account", + "name": "dnd_era_secondary_account", + "uuid": "0bba3490-6677-49b3-a6d5-a67ef63883b0" + } + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variable_list": [] + } +} diff --git a/tests/ndb_runbooks/nutanixdb_postgres_delete.json b/tests/ndb_runbooks/nutanixdb_postgres_delete.json index eeb9f53ff..907f1351a 100644 --- a/tests/ndb_runbooks/nutanixdb_postgres_delete.json +++ b/tests/ndb_runbooks/nutanixdb_postgres_delete.json @@ -17,6 +17,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -34,14 +35,14 @@ "attrs": { "type": "RT_OPERATION", "resource_type_reference": { - "uuid": "7d60e0a6-b021-4c22-b6a5-2fd311930709", + "uuid": "19c07696-473b-4c48-ba7d-133dff3f98a6", "name": "Postgres Database Instance", "kind": "resource_type" }, "action_reference": { "kind": "app_action", "name": "Delete", - "uuid": "71c95c88-f7ad-4854-b636-dd5f0b03064d" + "uuid": "112e4186-7981-4823-9b63-79800b6c155c" }, "inarg_list": [ { @@ -61,16 +62,18 @@ "tag": "Database", "account_reference": { "kind": "account", - "name": "ndb-account", - "uuid": "65d3ec1d-8044-4c5c-af50-881acb3c3a3d" + "name": "dnd_era_secondary_account", + "uuid": "0bba3490-6677-49b3-a6d5-a67ef63883b0" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variable_list": [] } -} \ No newline at end of file +} diff --git a/tests/ndb_runbooks/nutanixdb_postgres_restore.json b/tests/ndb_runbooks/nutanixdb_postgres_restore.json index ea26ac353..37451777e 100644 --- a/tests/ndb_runbooks/nutanixdb_postgres_restore.json +++ b/tests/ndb_runbooks/nutanixdb_postgres_restore.json @@ -17,6 +17,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -34,14 +35,14 @@ "attrs": { "type": "RT_OPERATION", "resource_type_reference": { - "uuid": "7d60e0a6-b021-4c22-b6a5-2fd311930709", + "uuid": "19c07696-473b-4c48-ba7d-133dff3f98a6", "name": "Postgres Database Instance", "kind": "resource_type" }, "action_reference": { "kind": "app_action", "name": "Restore from Time Machine", - "uuid": "e3bc4dcf-7149-43e0-9d48-79ab6ab4ef5e" + "uuid": "083fdf35-bbba-46a9-be14-f1a9d7b42031" }, "inarg_list": [ { @@ -57,31 +58,31 @@ "is_mandatory": true }, { - "name": "restore_from_time_machine_nutanix_ndb_database__user_pitr_timestamp", + "name": "restore_from_time_machine_nutanix_ndb_database__time_zone_pitr", "description": "", "type": "LOCAL", - "label": "Point in Time", + "label": "", "attrs": {}, "val_type": "STRING", - "value": "2023-02-12 10:01:40", + "value": "Asia/Kolkata", "data_type": "BASE", - "regex": { - "value": "^(|(\\d{4}-([0]\\d|[1][0-2])-([0-2]\\d|[3][0-1])\\s([0-1]\\d|[2][0-3]):[0-5]\\d:[0-5]\\d))$", - "should_validate": true - }, - "is_hidden": false, + "is_hidden": true, "is_mandatory": false }, { - "name": "restore_from_time_machine_nutanix_ndb_database__time_zone_pitr", + "name": "restore_from_time_machine_nutanix_ndb_database__user_pitr_timestamp", "description": "", "type": "LOCAL", - "label": "", + "label": "Point in Time", "attrs": {}, "val_type": "STRING", - "value": "Asia/Kolkata", + "value": "2023-02-12 10:01:40", "data_type": "BASE", - "is_hidden": true, + "regex": { + "value": "^(|(\\d{4}-([0]\\d|[1][0-2])-([0-2]\\d|[3][0-1])\\s([0-1]\\d|[2][0-3]):[0-5]\\d:[0-5]\\d))$", + "should_validate": true + }, + "is_hidden": false, "is_mandatory": false } ], @@ -92,16 +93,18 @@ "tag": "Database", "account_reference": { "kind": "account", - "name": "ndb-account", - "uuid": "65d3ec1d-8044-4c5c-af50-881acb3c3a3d" + "name": "dnd_era_secondary_account", + "uuid": "0bba3490-6677-49b3-a6d5-a67ef63883b0" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variable_list": [] } -} \ No newline at end of file +} diff --git a/tests/ndb_runbooks/nutanixdb_postgres_snapshot.json b/tests/ndb_runbooks/nutanixdb_postgres_snapshot.json index b9e372ecd..288c889e2 100644 --- a/tests/ndb_runbooks/nutanixdb_postgres_snapshot.json +++ b/tests/ndb_runbooks/nutanixdb_postgres_snapshot.json @@ -17,6 +17,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -34,16 +35,32 @@ "attrs": { "type": "RT_OPERATION", "resource_type_reference": { - "uuid": "722a4722-beb9-49ec-8229-80e552091a24", + "uuid": "19c07696-473b-4c48-ba7d-133dff3f98a6", "name": "Postgres Database Instance", "kind": "resource_type" }, "action_reference": { "kind": "app_action", "name": "Create Snapshot", - "uuid": "a0bbfae7-2643-4932-85bb-adafbf961bd2" + "uuid": "74aa193b-c019-4e73-a255-6f66f07a08db" }, "inarg_list": [ + { + "name": "create_snapshot_nutanix_ndb_database__is_removal_configured", + "description": "", + "type": "INPUT", + "label": "Removal Schedule", + "attrs": { + "type": "", + "is_internal": true, + "omit_on_empty": false + }, + "val_type": "BOOLEAN", + "value": "true", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, { "name": "create_snapshot_nutanix_ndb_database__name", "description": "", @@ -60,26 +77,14 @@ "is_hidden": false, "is_mandatory": false }, - { - "name": "create_snapshot_nutanix_ndb_database__time_machine_id", - "description": "", - "type": "LOCAL", - "label": "Target Time Machine", - "attrs": {}, - "val_type": "STRING", - "value": "@@{tm_uuid}@@", - "data_type": "BASE", - "is_hidden": false, - "is_mandatory": true - }, { "name": "create_snapshot_nutanix_ndb_database__remove_schedule_in_days", "description": "", "type": "INPUT", "label": "Remove in (number of days)", "attrs": { - "is_internal": false, "type": "", + "is_internal": false, "omit_on_empty": true }, "val_type": "STRING", @@ -93,20 +98,16 @@ "is_mandatory": false }, { - "name": "create_snapshot_nutanix_ndb_database__is_removal_configured", + "name": "create_snapshot_nutanix_ndb_database__time_machine_id", "description": "", - "type": "INPUT", - "label": "Removal Schedule", - "attrs": { - "is_internal": true, - "type": "", - "omit_on_empty": false - }, - "val_type": "BOOLEAN", - "value": "true", + "type": "LOCAL", + "label": "Target Time Machine", + "attrs": {}, + "val_type": "STRING", + "value": "@@{tm_uuid}@@", "data_type": "BASE", "is_hidden": false, - "is_mandatory": false + "is_mandatory": true } ], "output_variables": { @@ -116,15 +117,17 @@ "account_reference": { "kind": "account", "name": "dnd_era_secondary_account", - "uuid": "12c995cb-3b82-47ef-9e4d-efd8af07e294" + "uuid": "0bba3490-6677-49b3-a6d5-a67ef63883b0" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variable_list": [] } -} \ No newline at end of file +} diff --git a/tests/ndb_runbooks/test_runbook_json.py b/tests/ndb_runbooks/test_runbook_json.py index 670b01adc..8445b839f 100644 --- a/tests/ndb_runbooks/test_runbook_json.py +++ b/tests/ndb_runbooks/test_runbook_json.py @@ -4,7 +4,9 @@ import os import pytest import json +from distutils.version import LooseVersion as LV +from calm.dsl.store import Version from calm.dsl.runbooks import runbook_json from calm.dsl.builtins.models.utils import read_local_file from calm.dsl.constants import STRATOS @@ -152,6 +154,12 @@ def _test_compare_compile_result(Runbook, json_file, action_name): "inarg_list" ] = sorted(generated_inargs, key=lambda x: x["name"]) + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) < LV("3.9.0"): + for task in known_json["runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + # Update account name known_attrs["account_reference"]["name"] = ACCOUNTS[STRATOS.PROVIDER.NDB][0]["NAME"] diff --git a/tests/project/test_project_update.py b/tests/project/test_project_update.py index e844548fb..c12a9fa92 100644 --- a/tests/project/test_project_update.py +++ b/tests/project/test_project_update.py @@ -45,6 +45,7 @@ UPDATED_CLUSTER_UUID = [CLUSTER_UUID1, CLUSTER_UUID2] +@pytest.mark.quotas class TestProjectUpdate: def setup_method(self): """Method to create a project for testing the update project""" diff --git a/tests/runbook_decompile/test_rb_decompile.py b/tests/runbook_decompile/test_rb_decompile.py index 0133251e3..a0d3881cc 100644 --- a/tests/runbook_decompile/test_rb_decompile.py +++ b/tests/runbook_decompile/test_rb_decompile.py @@ -18,7 +18,7 @@ "vm_poweroff.json", "vm_restart.json", "task_tree_runbook.json", - "ndb_task.json", + # "ndb_task.json", "fetch_http_var.json", ] diff --git a/tests/sample_runbooks/http_task.py b/tests/sample_runbooks/http_task.py index 213adcec4..fb1f3e9ef 100644 --- a/tests/sample_runbooks/http_task.py +++ b/tests/sample_runbooks/http_task.py @@ -4,6 +4,7 @@ from calm.dsl.runbooks import read_local_file from calm.dsl.runbooks import runbook, runbook_json from calm.dsl.runbooks import RunbookTask as Task +from calm.dsl.runbooks import RunbookTask as Task, HTTPResponseHandle from calm.dsl.runbooks import CalmEndpoint as Endpoint AUTH_USERNAME = read_local_file(".tests/runbook_tests/auth_username") @@ -26,6 +27,31 @@ def DslHTTPTask(endpoints=[endpoint], default=False): response_paths={"ep_type": "$.spec.resources.type"}, target=endpoints[0], ) + Task.HTTP.get( + headers={"Content-Type": "application/json"}, + content_type="application/json", + response_code_status_map=[ + HTTPResponseHandle.ResponseCode( + code_ranges=[{"start_code": 200, "end_code": 200}], + status=HTTPResponseHandle.TASK_STATUS.Success, + ) + ], + response_paths={"ep_type": "$.spec.resources.type"}, + target=endpoints[0], + ) + Task.HTTP.get( + headers={"Content-Type": "application/json"}, + content_type="application/json", + response_code_status_map=[ + HTTPResponseHandle.ResponseCode( + code_ranges=[{"start_code": 200, "end_code": 200}], + status=HTTPResponseHandle.TASK_STATUS.Success, + code=200, + ) + ], + response_paths={"ep_type": "$.spec.resources.type"}, + target=endpoints[0], + ) def main(): diff --git a/tests/sample_runbooks/test_decision_task.json b/tests/sample_runbooks/test_decision_task.json index 78231feb9..3d1c49e35 100644 --- a/tests/sample_runbooks/test_decision_task.json +++ b/tests/sample_runbooks/test_decision_task.json @@ -17,6 +17,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -32,6 +33,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -50,6 +52,7 @@ "script_type": "static_py3", "script": "print('Decision Task is Successful')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -60,6 +63,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -78,6 +82,7 @@ "script_type": "static_py3", "script": "print('Decision Task Failed')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -103,12 +108,14 @@ "name": "DecisionTask_failure_meta_task" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } diff --git a/tests/sample_runbooks/test_existing_endpoint.json b/tests/sample_runbooks/test_existing_endpoint.json index 5b410b9e9..cd174deb9 100644 --- a/tests/sample_runbooks/test_existing_endpoint.json +++ b/tests/sample_runbooks/test_existing_endpoint.json @@ -17,6 +17,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -39,12 +40,14 @@ "script_type": "sh", "script": "echo \"hello\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } diff --git a/tests/sample_runbooks/test_inherit_target_runbook.json b/tests/sample_runbooks/test_inherit_target_runbook.json index 24b86ce26..ea1057540 100644 --- a/tests/sample_runbooks/test_inherit_target_runbook.json +++ b/tests/sample_runbooks/test_inherit_target_runbook.json @@ -17,6 +17,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -32,6 +33,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -50,6 +52,7 @@ "script_type": "static_py3", "script": "print('Decision Task is Successful')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -61,6 +64,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -79,6 +83,7 @@ "script_type": "sh", "script": "print 'Decision Task Failed'" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -105,12 +110,14 @@ "name": "DecisionTask_failure_meta_task" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } diff --git a/tests/sample_runbooks/test_parallel.json b/tests/sample_runbooks/test_parallel.json index 5fbd03140..23093357c 100644 --- a/tests/sample_runbooks/test_parallel.json +++ b/tests/sample_runbooks/test_parallel.json @@ -68,6 +68,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -106,6 +107,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -119,6 +121,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -132,6 +135,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -145,6 +149,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -158,6 +163,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -171,12 +177,14 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } diff --git a/tests/sample_runbooks/test_runbook_json.py b/tests/sample_runbooks/test_runbook_json.py index 3c4f27bd4..355fe5122 100644 --- a/tests/sample_runbooks/test_runbook_json.py +++ b/tests/sample_runbooks/test_runbook_json.py @@ -3,6 +3,8 @@ """ import os import pytest +import json +from distutils.version import LooseVersion as LV from distutils.version import LooseVersion as LV from calm.dsl.store import Version @@ -64,5 +66,19 @@ def _test_compare_compile_result(Runbook, json_file): generated_json = runbook_json(Runbook) known_json = open(file_path).read() - assert generated_json == known_json + known_json = json.loads(known_json) + generated_json = json.loads(generated_json) + + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) < LV("3.9.0"): + for task in known_json["runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + + known_json["runbook"].pop("output_variables", None) + known_json["runbook"].pop("output_variable_list", None) + generated_json["runbook"].pop("output_variables", None) + generated_json["runbook"].pop("output_variable_list", None) + + assert sorted(known_json.items()) == sorted(generated_json.items()) print("JSON compilation successful for {}".format(Runbook.action_name)) diff --git a/tests/sample_runbooks/test_runbook_variables.json b/tests/sample_runbooks/test_runbook_variables.json index a8d33f57d..71015ed61 100644 --- a/tests/sample_runbooks/test_runbook_variables.json +++ b/tests/sample_runbooks/test_runbook_variables.json @@ -17,6 +17,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -35,6 +36,7 @@ "script_type": "static_py3", "script": "\nprint(\"@@{var1}@@\")\nif \"@@{var1}@@\" == \"test\":\n print(\"yes\")\nelse:\n print(\"no\")\nprint(\"@@{var2}@@\")\nif \"@@{var2}@@\" == \"test\":\n print(\"yes\")\nelse:\n print(\"no\")\nprint(\"Hello @@{firstname}@@ @@{lastname}@@\")\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -96,6 +98,7 @@ "is_hidden": false, "is_mandatory": false } - ] + ], + "output_variables": [] } } diff --git a/tests/sample_runbooks/test_simple_runbook.json b/tests/sample_runbooks/test_simple_runbook.json index df6540e09..49d1b27e8 100644 --- a/tests/sample_runbooks/test_simple_runbook.json +++ b/tests/sample_runbooks/test_simple_runbook.json @@ -58,6 +58,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -92,6 +93,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -105,6 +107,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -118,6 +121,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -131,6 +135,7 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -144,12 +149,14 @@ "script_type": "static_py3", "script": "print(\"Start\")\nsleep(20)\nprint(\"End\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } diff --git a/tests/sample_runbooks/test_while_loop.json b/tests/sample_runbooks/test_while_loop.json index d75d8fec5..6dcc422a2 100644 --- a/tests/sample_runbooks/test_while_loop.json +++ b/tests/sample_runbooks/test_while_loop.json @@ -25,9 +25,20 @@ "kind": "app_task", "name": "WhileTask2" } + }, + { + "from_task_reference": { + "kind": "app_task", + "name": "WhileTask2" + }, + "to_task_reference": { + "kind": "app_task", + "name": "WhileTask" + } } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -36,6 +47,10 @@ { "kind": "app_task", "name": "WhileTask2" + }, + { + "kind": "app_task", + "name": "WhileTask" } ], "variable_list": [], @@ -47,6 +62,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -65,6 +81,7 @@ "script_type": "static_py3", "script": "print('Inside loop1 @@{loop_var}@@')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -79,6 +96,7 @@ "loop_variable": "loop_var", "exit_condition_type": "on_success" }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -94,6 +112,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -112,6 +131,7 @@ "script_type": "static_py3", "script": "print('Inside loop2 @@{iteration}@@')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -126,6 +146,7 @@ "loop_variable": "iteration", "exit_condition_type": "dont_care" }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -135,8 +156,67 @@ "variable_list": [], "retries": "", "timeout_secs": "" + }, + { + "name": "WhileTask_loop_meta_task", + "description": "", + "type": "META", + "attrs": {}, + "status_map_list": [], + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "Task1" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "Task1", + "description": "", + "type": "EXEC", + "attrs": { + "script_type": "static_py3", + "script": "print('Inside loop1 @@{loop_var}@@')" + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "WhileTask", + "description": "", + "type": "WHILE_LOOP", + "attrs": { + "iterations": "2", + "loop_variable": "loop_var", + "exit_condition_type": "on_success" + }, + "status_map_list": [ + { + "match_values": [ + "FAILURE" + ], + "type": "status", + "result_status": "WARNING" + } + ], + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "WhileTask_loop_meta_task" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } diff --git a/tests/sample_runbooks/vm_operations.py b/tests/sample_runbooks/vm_operations.py index 1a82225dc..c496f999d 100644 --- a/tests/sample_runbooks/vm_operations.py +++ b/tests/sample_runbooks/vm_operations.py @@ -5,7 +5,7 @@ from calm.dsl.runbooks import runbook, runbook_json from calm.dsl.runbooks import RunbookTask as Task -from calm.dsl.runbooks import CalmEndpoint as Endpoint, ref +from calm.dsl.runbooks import CalmEndpoint as Endpoint, ref, StatusHandle @runbook @@ -13,7 +13,14 @@ def DslVMOperationsRunbook(): "Runbook Service example" Task.VMPowerOff( - name="VM Power Off Task", target=ref(Endpoint.use_existing("VMEndpoint")) + name="VM Power Off Task", + target=ref(Endpoint.use_existing("VMEndpoint")), + status_map_list=[ + StatusHandle.Mapping.task_status( + values=[StatusHandle.Status.TaskFailure], + result=StatusHandle.Result.Warning, + ) + ], ) Task.VMPowerOn( name="VM Power On Task", target=ref(Endpoint.use_existing("VMEndpoint")) diff --git a/tests/sample_runbooks/while_loop.py b/tests/sample_runbooks/while_loop.py index 8970ea06e..154ece9b9 100644 --- a/tests/sample_runbooks/while_loop.py +++ b/tests/sample_runbooks/while_loop.py @@ -4,7 +4,7 @@ """ from calm.dsl.runbooks import runbook, runbook_json -from calm.dsl.runbooks import RunbookTask as Task, Status +from calm.dsl.runbooks import RunbookTask as Task, Status, StatusHandle @runbook @@ -26,6 +26,22 @@ def DslWhileLoopRunbook(): name="Task2", script="print('Inside loop2 @@{iteration}@@')" ) + with Task.Loop( + iterations=2, + name="WhileTask", + exit_condition=Status.SUCCESS, + loop_variable="loop_var", + status_map_list=[ + StatusHandle.Mapping.task_status( + values=[StatusHandle.Status.Failure], + result=StatusHandle.Result.Warning, + ) + ], + ): + Task.Exec.escript.py3( + name="Task1", script="print('Inside loop1 @@{loop_var}@@')" + ) + def main(): print(runbook_json(DslWhileLoopRunbook)) diff --git a/tests/scheduler/job_app_action_onetime.py b/tests/scheduler/job_app_action_onetime.py index e40ac6e9e..7120481b6 100644 --- a/tests/scheduler/job_app_action_onetime.py +++ b/tests/scheduler/job_app_action_onetime.py @@ -2,12 +2,12 @@ from calm.dsl.builtins import Job, JobScheduler -start_date = datetime.now() + timedelta(seconds=30) +start_date = datetime.now() + timedelta(seconds=120) start_date = ( str(start_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) -time_zone = "Asia/Kolkata" +time_zone = "UTC" APP_NAME = "job_app_action_onetime" diff --git a/tests/scheduler/job_app_action_recc.py b/tests/scheduler/job_app_action_recc.py index 7d19ff90b..3e06569e9 100644 --- a/tests/scheduler/job_app_action_recc.py +++ b/tests/scheduler/job_app_action_recc.py @@ -2,7 +2,7 @@ from calm.dsl.builtins import Job, JobScheduler -start_date = datetime.now() + timedelta(seconds=30) +start_date = datetime.now() + timedelta(seconds=120) start_date = ( str(start_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) @@ -12,7 +12,7 @@ str(expiry_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) -time_zone = "Asia/Kolkata" +time_zone = "UTC" APP_NAME = "job_app_action_recc" diff --git a/tests/scheduler/job_recurring_every_two_minute.py b/tests/scheduler/job_recurring_every_two_minute.py index 379737346..29ecf88f5 100644 --- a/tests/scheduler/job_recurring_every_two_minute.py +++ b/tests/scheduler/job_recurring_every_two_minute.py @@ -12,7 +12,7 @@ str(expiry_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) -time_zone = "Asia/Kolkata" +time_zone = "UTC" RUNBOOK_NAME = "job_recurring_every_two_minute" diff --git a/tests/scheduler/job_recurring_every_two_minute_decision_task.py b/tests/scheduler/job_recurring_every_two_minute_decision_task.py index ee9474da3..ef5abad35 100644 --- a/tests/scheduler/job_recurring_every_two_minute_decision_task.py +++ b/tests/scheduler/job_recurring_every_two_minute_decision_task.py @@ -12,7 +12,7 @@ str(expiry_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) -time_zone = "Asia/Kolkata" +time_zone = "UTC" RUNBOOK_NAME = "job_recurring_every_two_minute_decision_task" diff --git a/tests/scheduler/job_recurring_no_expiration_app_action.py b/tests/scheduler/job_recurring_no_expiration_app_action.py index 8ace067ea..c22ed1b3c 100644 --- a/tests/scheduler/job_recurring_no_expiration_app_action.py +++ b/tests/scheduler/job_recurring_no_expiration_app_action.py @@ -7,7 +7,7 @@ str(start_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) cron = "50 23 * * *" -time_zone = "Asia/Calcutta" +time_zone = "UTC" APP_NAME = "job_recurring_no_expiration_app_action" diff --git a/tests/scheduler/job_recurring_no_expiration_runbook.py b/tests/scheduler/job_recurring_no_expiration_runbook.py index 32fae8e63..90716a162 100644 --- a/tests/scheduler/job_recurring_no_expiration_runbook.py +++ b/tests/scheduler/job_recurring_no_expiration_runbook.py @@ -2,12 +2,12 @@ from calm.dsl.builtins import Job, JobScheduler -start_date = datetime.now() + timedelta(seconds=10) +start_date = datetime.now() + timedelta(seconds=120) start_date = ( str(start_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) cron = "50 23 * * *" -time_zone = "Asia/Calcutta" +time_zone = "UTC" RUNBOOK_NAME = "job_recurring_no_expiration_runbook" diff --git a/tests/scheduler/one_time_scheduler.py b/tests/scheduler/one_time_scheduler.py index 0614d2f10..cb0013539 100644 --- a/tests/scheduler/one_time_scheduler.py +++ b/tests/scheduler/one_time_scheduler.py @@ -6,7 +6,7 @@ start_date = ( str(start_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) -time_zone = "Asia/Kolkata" +time_zone = "UTC" RUNBOOK_NAME = "one_time_scheduler" diff --git a/tests/scheduler/one_time_scheduler_decision_task.py b/tests/scheduler/one_time_scheduler_decision_task.py index ea8c1397a..d9447a87a 100644 --- a/tests/scheduler/one_time_scheduler_decision_task.py +++ b/tests/scheduler/one_time_scheduler_decision_task.py @@ -2,12 +2,12 @@ from calm.dsl.builtins import Job, JobScheduler -start_date = datetime.now() + timedelta(seconds=10) +start_date = datetime.now() + timedelta(seconds=120) start_date = ( str(start_date.strftime("%Y-%m-%dT%H:%M:%SZ")).replace("T", " ").replace("Z", "") ) -time_zone = "Asia/Kolkata" +time_zone = "UTC" RUNBOOK_NAME = "one_time_scheduler_decision_task" diff --git a/tests/simple_blueprint/test_simple_blueprint.json b/tests/simple_blueprint/test_simple_blueprint.json index db36b7bd1..f5212e59e 100644 --- a/tests/simple_blueprint/test_simple_blueprint.json +++ b/tests/simple_blueprint/test_simple_blueprint.json @@ -35,7 +35,8 @@ "choices": [ "DEV", "PROD" - ] + ], + "exec_target_reference": {} }, "is_hidden": false, "is_mandatory": true @@ -67,6 +68,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -89,13 +91,15 @@ "script_type": "sh", "script": "echo 'Service start in ENV=@@{ENV}@@'" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -122,6 +126,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -144,13 +149,15 @@ "script_type": "sh", "script": "echo 'Service stop in ENV=@@{ENV}@@'" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -185,9 +192,20 @@ "kind": "app_task", "name": "Task5" } + }, + { + "from_task_reference": { + "kind": "app_task", + "name": "Task5" + }, + "to_task_reference": { + "kind": "app_task", + "name": "Task11" + } } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -196,6 +214,10 @@ { "kind": "app_task", "name": "Task5" + }, + { + "kind": "app_task", + "name": "Task11" } ], "variable_list": [], @@ -214,6 +236,7 @@ "script_type": "sh", "script": "echo \"Hello\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -231,6 +254,42 @@ "script_type": "sh", "script": "echo \"Hello again\"" }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "Task11", + "description": "", + "type": "HTTP", + "target_any_local_reference": { + "kind": "app_service", + "name": "MySQLDeploymentService" + }, + "exec_target_reference": { + "kind": "app_endpoint", + "name": "DND-Http-Endpoint" + }, + "attrs": { + "method": "GET", + "url": "", + "authentication": { + "auth_type": "none" + }, + "connection_timeout": 120, + "tls_verify": false, + "retry_count": 1, + "retry_interval": 10, + "expected_response_params": [ + { + "status": "SUCCESS", + "code": 200 + } + ] + }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -250,7 +309,8 @@ "is_hidden": false, "is_mandatory": false } - ] + ], + "output_variables": [] } }, { @@ -277,6 +337,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -303,6 +364,7 @@ "script_type": "sh", "script": "date" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -320,13 +382,15 @@ "script_type": "sh", "script": "date" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -353,13 +417,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -386,13 +452,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -419,13 +487,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -452,13 +522,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ] @@ -521,6 +593,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -547,6 +620,7 @@ "script_type": "sh", "script": "echo \"Hello\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -564,6 +638,7 @@ "script_type": "sh", "script": "echo \"Hello again\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -583,7 +658,8 @@ "is_hidden": false, "is_mandatory": false } - ] + ], + "output_variables": [] } }, { @@ -610,13 +686,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -643,13 +721,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -676,13 +756,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -709,13 +791,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -742,13 +826,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -775,13 +861,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ] @@ -819,13 +907,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -852,13 +942,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -885,13 +977,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -918,13 +1012,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -951,13 +1047,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -984,13 +1082,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -1058,13 +1158,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1091,13 +1193,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1124,13 +1228,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1157,13 +1263,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1190,13 +1298,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1223,13 +1333,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -1275,6 +1387,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1297,13 +1410,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -e\n\necho \"Installation script goes here ...\"\nsleep 3\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_MySQLDeploymentPackage_action_uninstall", @@ -1324,13 +1439,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -1366,6 +1483,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1388,13 +1506,15 @@ "script_type": "sh", "script": "echo @@{foo}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_PHPDeploymentPackage_action_uninstall", @@ -1415,13 +1535,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -1548,6 +1670,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1570,13 +1693,15 @@ "script_type": "static_py3", "script": "print ('Hello!')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -1870,6 +1995,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1896,6 +2022,7 @@ "script_type": "sh", "script": "echo \"Hello\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -1915,13 +2042,15 @@ "name": "PHPDeployment_test_action_runbook" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ] diff --git a/tests/simple_blueprint/test_simple_blueprint.py b/tests/simple_blueprint/test_simple_blueprint.py index 9a6fee46e..7496c6652 100644 --- a/tests/simple_blueprint/test_simple_blueprint.py +++ b/tests/simple_blueprint/test_simple_blueprint.py @@ -12,10 +12,11 @@ from calm.dsl.builtins import ref, basic_cred from calm.dsl.builtins import SimpleDeployment, SimpleBlueprint from calm.dsl.builtins import read_provider_spec, read_spec, read_local_file -from calm.dsl.builtins import CalmTask as Task +from calm.dsl.builtins import CalmTask as Task, CalmEndpoint as Endpoint from calm.dsl.builtins import CalmVariable as Var from calm.dsl.builtins import action, parallel + CRED_USERNAME = read_local_file(".tests/username") CRED_PASSWORD = read_local_file(".tests/password") DNS_SERVER = read_local_file(".tests/dns_server") @@ -65,6 +66,11 @@ def custom_action_1(): blah = Var("2") # noqa Task.Exec.ssh(name="Task4", script='echo "Hello"') Task.Exec.ssh(name="Task5", script='echo "Hello again"') + Task.HTTP.get( + name="Task11", + target_endpoint=ref(Endpoint.use_existing("DND-Http-Endpoint")), + status_mapping={200: True}, + ) @action def custom_action_2(): diff --git a/tests/simple_blueprint/test_simple_bp_create.py b/tests/simple_blueprint/test_simple_bp_create.py index 4bad1dec7..66591bc80 100644 --- a/tests/simple_blueprint/test_simple_bp_create.py +++ b/tests/simple_blueprint/test_simple_bp_create.py @@ -8,10 +8,13 @@ from calm.dsl.cli import main as cli from calm.dsl.log import get_logging_handle from calm.dsl.builtins import read_local_file +from calm.dsl.api import get_api_client # for tcs from calm.dsl.store import Version from distutils.version import LooseVersion as LV +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp # Setting the recursion limit to max for sys.setrecursionlimit(100000) @@ -31,16 +34,48 @@ NTNX_LOCAL_ACCOUNT = DSL_CONFIG["ACCOUNTS"]["NTNX_LOCAL_AZ"] SUBNET_UUID = NTNX_LOCAL_ACCOUNT["SUBNETS"][0]["UUID"] +# Endpoint file used to create endpoints for runbook used in scheduler test +DSL_EP_PATH = "tests/cli/endpoints/http_endpoint.py" +CALM_ENDPOINT_NAME = "DND-Http-Endpoint" + class TestSimpleBlueprint: def setup_method(self): """Method to instantiate to created_bp_list""" + # Method to create endpoint + client = get_api_client() + self.endpoint = CALM_ENDPOINT_NAME + + # Check if there is existing endpoint with this name + payload = {"filter": "name=={}".format(self.endpoint)} + res, _ = client.endpoint.list(payload) + res = res.json() + + if res["metadata"]["total_matches"] > 0: + return + + # If there is no endpoint, create one + LOG.info("Creating Endpoint {}".format(self.endpoint)) + runner = CliRunner() + result = runner.invoke( + cli, ["create", "endpoint", "-f", DSL_EP_PATH, "-n", self.endpoint] + ) + assert result.exit_code == 0 + LOG.info("Successult Created Endoint {}".format(self.endpoint)) + self.created_bp_list = [] def teardown_method(self): """Method to delete creates bps and apps during tests""" + # Method to delete endpoint + + LOG.info("Deleting Endpoint {}".format(self.endpoint)) + runner = CliRunner() + result = runner.invoke(cli, ["delete", "endpoint", self.endpoint]) + assert result.exit_code == 0 + for bp_name in self.created_bp_list: LOG.info("Deleting Blueprint {}".format(bp_name)) runner = CliRunner() @@ -148,5 +183,10 @@ def test_compile(self, bp_file_path, json_file_path): # Pop the project referecne from metadata generated_json["metadata"].pop("project_reference", None) + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json["spec"]["resources"]) + + remove_output_variables_from_bp(known_json["spec"]["resources"]) + remove_output_variables_from_bp(generated_json["spec"]["resources"]) assert sorted(known_json.items()) == sorted(generated_json.items()) diff --git a/tests/simple_blueprint/test_simple_bp_with_downloadable_image.json b/tests/simple_blueprint/test_simple_bp_with_downloadable_image.json index 7b0893147..75f9fb282 100644 --- a/tests/simple_blueprint/test_simple_bp_with_downloadable_image.json +++ b/tests/simple_blueprint/test_simple_bp_with_downloadable_image.json @@ -44,13 +44,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -77,13 +79,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -110,13 +114,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -143,13 +149,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -176,13 +184,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -209,13 +219,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ] @@ -246,13 +258,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_VmDeploymentPackage_action_uninstall", @@ -273,13 +287,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ diff --git a/tests/task_library/test_task_library.json b/tests/task_library/test_task_library.json index b910cfaf2..af74e696c 100644 --- a/tests/task_library/test_task_library.json +++ b/tests/task_library/test_task_library.json @@ -6,6 +6,7 @@ "script_type": "npsscript", "script": "Write-Output \"Installing IIS including Management tools\"\n\nInstall-WindowsFeature -name Web-Server -IncludeManagementTools\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", diff --git a/tests/task_library/test_task_library_item.py b/tests/task_library/test_task_library_item.py index 83814b93d..5637b8cb8 100644 --- a/tests/task_library/test_task_library_item.py +++ b/tests/task_library/test_task_library_item.py @@ -2,6 +2,9 @@ Create task library item """ +import json +from distutils.version import LooseVersion as LV +from calm.dsl.store.version import Version from calm.dsl.builtins import CalmTask Install_IIS = CalmTask.Exec.powershell( @@ -20,7 +23,16 @@ def test_json(): generated_json = Install_IIS.json_dumps(pprint=True) known_json = open(file_path).read() - assert generated_json == known_json + + known_json = json.loads(known_json) + generated_json = json.loads(generated_json) + + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) < LV("3.9.0"): + if "status_map_list" in known_json: + known_json.pop("status_map_list") + + assert sorted(known_json.items()) == sorted(generated_json.items()) def main(): diff --git a/tests/test_inheritance/test_inheritance.py b/tests/test_inheritance/test_inheritance.py index b5b3bdbb2..31e701125 100644 --- a/tests/test_inheritance/test_inheritance.py +++ b/tests/test_inheritance/test_inheritance.py @@ -1,6 +1,8 @@ import os import json +from distutils.version import LooseVersion as LV +from calm.dsl.store.version import Version from calm.dsl.builtins import Service, Package, Substrate from calm.dsl.builtins import Deployment, Profile, Blueprint from calm.dsl.builtins import CalmVariable as Variable @@ -10,6 +12,8 @@ from calm.dsl.builtins import vm_disk_package, AhvVmDisk, AhvVmNic from calm.dsl.builtins import AhvVmGC, AhvVmResources, AhvVm from calm.dsl.config import get_context +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp # SSH Credentials @@ -276,4 +280,11 @@ def test_json(): for _cred in generated_json["credential_definition_list"]: _cred.pop("cred_class", None) + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json) + + remove_output_variables_from_bp(known_json) + remove_output_variables_from_bp(generated_json) + assert sorted(known_json.items()) == sorted(generated_json.items()) diff --git a/tests/test_inheritance/test_inheritance_bp_output.json b/tests/test_inheritance/test_inheritance_bp_output.json index 0ee7f1e12..bcbb9b023 100644 --- a/tests/test_inheritance/test_inheritance_bp_output.json +++ b/tests/test_inheritance/test_inheritance_bp_output.json @@ -59,13 +59,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -92,13 +94,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -125,13 +129,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -158,13 +164,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -191,13 +199,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -224,13 +234,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -257,6 +269,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -279,13 +292,15 @@ "script_type": "sh", "script": "echo @@{sound}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -366,13 +381,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -399,13 +416,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -432,13 +451,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -465,13 +486,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -498,13 +521,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -531,13 +556,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -564,6 +591,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -586,13 +614,15 @@ "script_type": "sh", "script": "echo @@{sound}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -619,6 +649,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -641,13 +672,15 @@ "script_type": "sh", "script": "echo @@{dog_prop}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -716,13 +749,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -749,13 +784,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -782,13 +819,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -815,13 +854,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -848,13 +889,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -881,13 +924,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -914,6 +959,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -936,13 +982,15 @@ "script_type": "sh", "script": "echo @@{sound}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -1035,13 +1083,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1068,13 +1118,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1101,13 +1153,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1134,13 +1188,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1167,13 +1223,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1200,13 +1258,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1233,6 +1293,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1255,13 +1316,15 @@ "script_type": "sh", "script": "echo @@{sound}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1288,6 +1351,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1310,13 +1374,15 @@ "script_type": "sh", "script": "echo @@{dog_prop}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -1343,6 +1409,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1365,13 +1432,15 @@ "script_type": "sh", "script": "echo @@{husky_prop}@@" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -1426,6 +1495,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1448,13 +1518,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -ex\n\necho \"Package installation steps go here ...\"\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "AnimalPackage___uninstall___runbook", @@ -1475,6 +1547,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1497,13 +1570,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -ex\n\necho \"Package uninstallation steps go here ...\"\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -1539,6 +1614,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1561,13 +1637,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -ex\n\necho \"Package installation steps go here ...\"\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "DogPackage___uninstall___runbook", @@ -1588,6 +1666,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1610,13 +1689,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -ex\n\necho \"Package uninstallation steps go here ...\"\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -1652,6 +1733,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1674,13 +1756,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -ex\n\necho \"Package installation steps go here ...\"\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "CatPackage___uninstall___runbook", @@ -1701,6 +1785,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1723,13 +1808,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -ex\n\necho \"Package uninstallation steps go here ...\"\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -1765,6 +1852,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1787,13 +1875,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -ex\n\necho \"Package installation steps go here ...\"\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "HuskyPackage___uninstall___runbook", @@ -1814,6 +1904,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -1836,13 +1927,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -ex\n\necho \"Package uninstallation steps go here ...\"\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -2327,6 +2420,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -2363,6 +2457,7 @@ "name": "AnimalService_get_sound_runbook" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2382,6 +2477,7 @@ "name": "DogService_get_sound_runbook" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2401,6 +2497,7 @@ "name": "HuskyService_get_sound_runbook" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -2420,13 +2517,15 @@ "name": "CatService_get_sound_runbook" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ] diff --git a/tests/testprep.py b/tests/testprep.py index 86b210517..95234a3ab 100644 --- a/tests/testprep.py +++ b/tests/testprep.py @@ -16,6 +16,7 @@ LOG = get_logging_handle(__name__) VPC_LINUX_EP_PATH = "tests/tunnel_endpoints/linux_endpoint.py" VPC_WINDOWS_EP_PATH = "tests/tunnel_endpoints/windows_endpoint.py" +HTTP_EP_PATH = "tests/cli/endpoints/http_endpoint.py" LOCAL_LINUX_ENDPOINT_VPC = os.path.join( os.path.expanduser("~"), ".calm", ".local", ".tests", "endpoint_linux_vpc" @@ -23,6 +24,9 @@ LOCAL_WINDOWS_ENDPOINT_VPC = os.path.join( os.path.expanduser("~"), ".calm", ".local", ".tests", "endpoint_windows_vpc" ) +LOCAL_HTTP_ENDPOINT = os.path.join( + os.path.expanduser("~"), ".calm", ".local", ".tests", "endpoint_http" +) dsl_config_file_location = os.path.expanduser("~/.calm/.local/.tests/config.json") VPC_PROJECT_NAME = "test_vpc_project" @@ -118,12 +122,6 @@ def add_account_details(config): # If it is local nutanix account, assign it to local nutanix ACCOUNT if a_entity["status"]["resources"]["data"].get("host_pc", False): accounts["NTNX_LOCAL_AZ"] = account_data - elif account_type == "CUSTOM_PROVIDER": - provider_name = a_entity["status"]["resources"]["data"][ - "provider_reference" - ]["name"] - if provider_name == STRATOS.PROVIDER.NDB: - accounts[STRATOS.PROVIDER.NDB] = account_data accounts[account_type].append(account_data) @@ -407,6 +405,16 @@ def add_vpc_details(config): add_tunnel_details(config) +def add_approval_details(config): + + config["IS_POLICY_ENABLED"] = False + project_exists = check_project_exists("test_approval_policy") + if project_exists: + config["IS_POLICY_ENABLED"] = True + + add_project_details(config, "POLICY_PROJECTS", "test_approval_policy") + + def add_protection_policy_details(config, config_header, project_name): """Adds protection policy details of vmware/ahv snapshot projects for tests""" @@ -546,14 +554,38 @@ def add_vpc_endpoints(config): f.write(dsl_windows_endpoint) -def add_approval_details(config): +def add_http_endpoint(config): + dsl_http_endpoint = "Endpoint_HTTP_{}".format(str(uuid.uuid4())) - config["IS_POLICY_ENABLED"] = False - project_exists = check_project_exists("test_approval_policy") - if project_exists: - config["IS_POLICY_ENABLED"] = True + make_file_dir(LOCAL_HTTP_ENDPOINT) + with open(LOCAL_HTTP_ENDPOINT, "w+") as f: + f.write(dsl_http_endpoint) - add_project_details(config, "POLICY_PROJECTS", "test_approval_policy") + runner = CliRunner() + result = runner.invoke( + cli, + [ + "create", + "endpoint", + "--file={}".format(HTTP_EP_PATH), + "--name={}".format(dsl_http_endpoint), + "--description='Test DSL HTTP Endpoint'", + ], + ) + LOG.info(result.output) + if result.exit_code: + cli_res_dict = { + "Output": result.output, + "Exception": str(result.exception), + } + LOG.debug( + "Cli Response: {}".format( + json.dumps(cli_res_dict, indent=4, separators=(",", ": ")) + ) + ) + LOG.debug( + "Traceback: \n{}".format("".join(traceback.format_tb(result.exc_info[2]))) + ) def add_provider_constants(config): @@ -592,12 +624,13 @@ def add_provider_constants(config): add_directory_service_user_groups(config) add_project_details(config) add_vpc_details(config) +add_approval_details(config) add_ahv_snapshot_policy(config) add_vmw_snapshot_policy(config) add_rerun_report_portal(config) add_vpc_endpoints(config) -add_approval_details(config) add_provider_constants(config) +add_http_endpoint(config) f = open(dsl_config_file_location, "w") f.write(json.dumps(config, indent=4)) f.close() diff --git a/tests/two_vm_example/test_two_vm_bp.py b/tests/two_vm_example/test_two_vm_bp.py index be1167aaa..d437734cb 100644 --- a/tests/two_vm_example/test_two_vm_bp.py +++ b/tests/two_vm_example/test_two_vm_bp.py @@ -11,6 +11,8 @@ from calm.dsl.builtins import read_provider_spec, CalmVariable, read_local_file from calm.dsl.store import Version from distutils.version import LooseVersion as LV +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp CRED_USERNAME = read_local_file(".tests/username") CRED_PASSWORD = read_local_file(".tests/password") @@ -151,6 +153,12 @@ def test_json(): for cred in known_json["credential_definition_list"]: cred["cred_class"] = "static" + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json) + + remove_output_variables_from_bp(known_json) + remove_output_variables_from_bp(generated_json) + assert sorted(known_json.items()) == sorted(generated_json.items()) diff --git a/tests/two_vm_example/two_vm_bp_output.json b/tests/two_vm_example/two_vm_bp_output.json index ebcdf7fda..0632e0473 100644 --- a/tests/two_vm_example/two_vm_bp_output.json +++ b/tests/two_vm_example/two_vm_bp_output.json @@ -42,13 +42,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -75,13 +77,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -108,13 +112,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -141,13 +147,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -174,13 +182,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -207,13 +217,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -278,13 +290,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -311,13 +325,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -344,13 +360,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -377,13 +395,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -410,13 +430,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -443,13 +465,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -481,13 +505,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_MySQLPackage_action_uninstall", @@ -508,13 +534,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -563,13 +591,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_PHPPackage_action_uninstall", @@ -590,13 +620,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -624,7 +656,7 @@ "subnet_reference": { "kind": "subnet", "name": "vlan.0", - "uuid": "6a049405-a612-48b6-b098-c922fe024964" + "uuid": "da1a6db3-ca32-4afd-8b83-e587a89b5415" }, "network_function_nic_type": "INGRESS", "nic_type": "NORMAL_NIC" @@ -688,7 +720,7 @@ "subnet_reference": { "kind": "subnet", "name": "vlan.0", - "uuid": "6a049405-a612-48b6-b098-c922fe024964" + "uuid": "da1a6db3-ca32-4afd-8b83-e587a89b5415" }, "network_function_nic_type": "INGRESS", "nic_type": "NORMAL_NIC" diff --git a/tests/unit/jsons/dynamic_variable_with_endpoint.json b/tests/unit/jsons/dynamic_variable_with_endpoint.json new file mode 100644 index 000000000..4c96a0c3d --- /dev/null +++ b/tests/unit/jsons/dynamic_variable_with_endpoint.json @@ -0,0 +1,34 @@ +{ + "type": "EXEC_LOCAL", + "name": "powershell_var", + "uuid": "48a825ee-5158-4682-ae86-6c8fa3e5928c", + "description": "", + "regex": {}, + "options": { + "type": "EXEC", + "attrs": { + "type": "EXEC", + "script": "echo \"ps1\"", + "script_type": "npsscript", + "command_line_args": "", + "exit_status": [] + }, + "exec_target_reference": { + "name": "windows_endpoint", + "uuid": "a59aca97-b816-47a2-9f70-4ad22591098d", + "kind": "app_endpoint" + } + }, + "is_hidden": false, + "is_mandatory": false, + "data_type": "BASE", + "val_type": "STRING", + "label": "", + "attrs": { + "type": "" + }, + "editables": { + "value": true + }, + "value": "" +} \ No newline at end of file diff --git a/tests/unit/jsons/escript_all_tasks.json b/tests/unit/jsons/escript_all_tasks.json index e50ec5be4..b9d4ddf7c 100644 --- a/tests/unit/jsons/escript_all_tasks.json +++ b/tests/unit/jsons/escript_all_tasks.json @@ -38,6 +38,7 @@ } ] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -61,6 +62,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -83,6 +85,7 @@ "script_type": "static_py3", "script": "print(\"just printing...\")\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -99,6 +102,7 @@ "var1" ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -109,6 +113,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -127,6 +132,7 @@ "script_type": "static_py3", "script": "print (\"Decision else part\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -148,6 +154,7 @@ "name": "escript_decision_failure_meta_task" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -158,6 +165,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -176,6 +184,7 @@ "script_type": "static_py3", "script": "print (\"Decision if part\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -186,6 +195,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -208,6 +218,7 @@ "script_type": "static_py3", "script": "print(\"just printing...\")\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -224,6 +235,7 @@ "var1" ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -245,6 +257,7 @@ "name": "escript2_decision_failure_meta_task" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -255,6 +268,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -277,6 +291,7 @@ "script_type": "static_py3", "script": "print(\"just printing...\")\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -293,6 +308,7 @@ "var1" ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -303,6 +319,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -321,6 +338,7 @@ "script_type": "static_py3", "script": "print(\"Decision else part\")" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -342,12 +360,14 @@ "name": "escript3_decision_failure_meta_task" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variable_list": [] } } diff --git a/tests/unit/jsons/python_remote_all_tasks.json b/tests/unit/jsons/python_remote_all_tasks.json index 00dc95de4..75807d6a2 100644 --- a/tests/unit/jsons/python_remote_all_tasks.json +++ b/tests/unit/jsons/python_remote_all_tasks.json @@ -17,6 +17,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -32,6 +33,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -58,6 +60,7 @@ "script_type": "python_remote", "script": "print(\"just printing...\")\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -78,6 +81,7 @@ "var1" ] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -88,6 +92,7 @@ "description": "", "type": "META", "attrs": {}, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -110,6 +115,7 @@ "script_type": "python_remote", "script": "print \"Decision else part\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", @@ -135,12 +141,14 @@ "name": "python_decision_failure_meta_task" } }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variable_list": [] } } diff --git a/tests/unit/providers/__init__.py b/tests/unit/providers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/providers/dsl_files/__init__.py b/tests/unit/providers/dsl_files/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/providers/dsl_files/provider_with_custom_ep_schema.py b/tests/unit/providers/dsl_files/provider_with_custom_ep_schema.py new file mode 100644 index 000000000..15e4424dc --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_custom_ep_schema.py @@ -0,0 +1,29 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CalmVariable, CloudProvider, ProviderEndpointSchema + + +class DslProviderWithCustomEPSchema(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string(name="server_ip", value="1.1.1.1"), + CalmVariable.Simple.int(name="port_number", value="443"), + ], + ) + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] diff --git a/tests/unit/providers/dsl_files/provider_with_incorrect_action_name.py b/tests/unit/providers/dsl_files/provider_with_incorrect_action_name.py new file mode 100644 index 000000000..087b3388c --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_incorrect_action_name.py @@ -0,0 +1,42 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.runbooks import RunbookTask as Task +from calm.dsl.builtins import ( + CalmVariable, + CloudProvider, + action, + ProviderEndpointSchema, +) + + +class DslProviderWithIncorrectActionName(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string(name="server_ip", value="1.1.1.1"), + CalmVariable.Simple.int(name="port_number", value="443"), + ], + ) + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] + + @action + def MyAction(): + + """Verify action for Provider""" + + Task.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py2") diff --git a/tests/unit/providers/dsl_files/provider_with_incorrect_action_type.py b/tests/unit/providers/dsl_files/provider_with_incorrect_action_type.py new file mode 100644 index 000000000..b79de46a1 --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_incorrect_action_type.py @@ -0,0 +1,42 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.runbooks import RunbookTask as Task +from calm.dsl.builtins import ( + CalmVariable, + CloudProvider, + action, + ProviderEndpointSchema, +) + + +class DslProviderWithIncorrectActionType(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string(name="server_ip", value="1.1.1.1"), + CalmVariable.Simple.int(name="port_number", value="443"), + ], + ) + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] + + @action + def Verify(type="fsdfdssdf"): + + """Verify action for Provider""" + + Task.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py2") diff --git a/tests/unit/providers/dsl_files/provider_with_incorrect_ep_schema_type.py b/tests/unit/providers/dsl_files/provider_with_incorrect_ep_schema_type.py new file mode 100644 index 000000000..7151bc533 --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_incorrect_ep_schema_type.py @@ -0,0 +1,23 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CalmVariable, CloudProvider, ProviderEndpointSchema + + +class DslProviderWithIncorrectEPSchemaType(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema(type="fasdfasdads") + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] diff --git a/tests/unit/providers/dsl_files/provider_with_incorrect_infra_type.py b/tests/unit/providers/dsl_files/provider_with_incorrect_infra_type.py new file mode 100644 index 000000000..5111a2350 --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_incorrect_infra_type.py @@ -0,0 +1,14 @@ +from calm.dsl.builtins import CloudProvider, CalmVariable + + +class DslProviderWithIncorrectInfraType(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = "notexpected" + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] diff --git a/tests/unit/providers/dsl_files/provider_with_just_auth_schema.py b/tests/unit/providers/dsl_files/provider_with_just_auth_schema.py new file mode 100644 index 000000000..ea1d2e016 --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_just_auth_schema.py @@ -0,0 +1,15 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CalmVariable, CloudProvider + + +class DslProviderWithJustAuthSchema(CloudProvider): + + """Sample provider with just authentication schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] diff --git a/tests/unit/providers/dsl_files/provider_with_multiple_actions.py b/tests/unit/providers/dsl_files/provider_with_multiple_actions.py new file mode 100644 index 000000000..76c2d2a3b --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_multiple_actions.py @@ -0,0 +1,49 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.runbooks import RunbookTask as Task +from calm.dsl.builtins import ( + CalmVariable, + CloudProvider, + action, + ProviderEndpointSchema, +) + + +class DslProviderWithMultipleActions(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string(name="server_ip", value="1.1.1.1"), + CalmVariable.Simple.int(name="port_number", value="443"), + ], + ) + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] + + @action + def Verify(): # Name of the action must be 'Verify' + + """Verify action for Provider""" + + Task.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py2") + + @action + def Verify2(): # Name of the action must be 'Verify' + + """Verify action for Provider""" + + Task.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py2") diff --git a/tests/unit/providers/dsl_files/provider_with_none_ep_schema.py b/tests/unit/providers/dsl_files/provider_with_none_ep_schema.py new file mode 100644 index 000000000..5fb6e9d42 --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_none_ep_schema.py @@ -0,0 +1,24 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CalmVariable, CloudProvider +from calm.dsl.builtins import NoneEndpointSchema + + +class DslProviderWithNoneEPSchema(CloudProvider): + + """Sample provider with authentication schema, variables & 'none' endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # No Endpoint Schema + endpoint_schema = NoneEndpointSchema() + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] diff --git a/tests/unit/providers/dsl_files/provider_with_ntnx_ep_schema.py b/tests/unit/providers/dsl_files/provider_with_ntnx_ep_schema.py new file mode 100644 index 000000000..2a4d5de21 --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_ntnx_ep_schema.py @@ -0,0 +1,24 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CalmVariable, CloudProvider +from calm.dsl.builtins import NutanixEndpointSchema + + +class DslProviderWithNtnxEPSchema(CloudProvider): + + """Sample provider with authentication schema, variables & 'ntnx' endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Nutanix Endpoint Schema + endpoint_schema = NutanixEndpointSchema() + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] diff --git a/tests/unit/providers/dsl_files/provider_with_test_account.py b/tests/unit/providers/dsl_files/provider_with_test_account.py new file mode 100644 index 000000000..161392807 --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_test_account.py @@ -0,0 +1,60 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.runbooks import RunbookTask as Task +from calm.dsl.builtins import ( + CalmVariable, + CloudProvider, + action, + ProviderEndpointSchema, + ProviderTestAccount, +) + + +class HelloProvider(CloudProvider): + """Sample provider for Hello""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value="", is_mandatory=True), + CalmVariable.Simple.Secret.string(name="password", value="", is_mandatory=True), + ] + + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string( + name="server_ip", value="1.1.1.1", is_mandatory=True + ), + CalmVariable.Simple.int(name="port_number", value="443", is_mandatory=True), + ], + ) + + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] + + test_account = ProviderTestAccount( + name="TestHelloAccount", + description="Used for test executions", + variables=[ + CalmVariable.Simple.string( + name="server_ip", value="10.10.10.10", is_mandatory=True + ), + CalmVariable.Simple.int( + name="port_number", value="9440", is_mandatory=True + ), + CalmVariable.Simple.string( + name="username", value="root", is_mandatory=True + ), + CalmVariable.Simple.Secret.string( + name="password", value="iamasecret", is_mandatory=True + ), + ], + ) + + @action + def Verify(): + + """Verify action for Provider""" + + Task.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py2") diff --git a/tests/unit/providers/dsl_files/provider_with_unexpected_ep_variables.py b/tests/unit/providers/dsl_files/provider_with_unexpected_ep_variables.py new file mode 100644 index 000000000..e5112221f --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_unexpected_ep_variables.py @@ -0,0 +1,26 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CalmVariable, CloudProvider, ProviderEndpointSchema + + +class DslProviderWithVariablesInStandardEPSchema(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.GCP, + variables=[CalmVariable.Simple.string(name="server_ip", value="")], + ) + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] diff --git a/tests/unit/providers/dsl_files/provider_with_variables.py b/tests/unit/providers/dsl_files/provider_with_variables.py new file mode 100644 index 000000000..657a1f3fa --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_variables.py @@ -0,0 +1,20 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CalmVariable, CloudProvider + + +class DslProviderWithVariables(CloudProvider): + + """Sample provider with authentication schema & variables configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] diff --git a/tests/unit/providers/dsl_files/provider_with_verify_action.py b/tests/unit/providers/dsl_files/provider_with_verify_action.py new file mode 100644 index 000000000..7e4e7cd8f --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_with_verify_action.py @@ -0,0 +1,42 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.runbooks import RunbookTask as Task +from calm.dsl.builtins import ( + CalmVariable, + CloudProvider, + action, + ProviderEndpointSchema, +) + + +class DslProviderWithVerifyAction(CloudProvider): + + """Sample provider with authentication schema, variables, endpoint schema & verify action configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string(name="server_ip", value=""), + CalmVariable.Simple.int(name="port_number", value=""), + ], + ) + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value=""), + ] + + @action + def Verify(): # Name of the action must be 'Verify' + + """Verify action for Provider""" + + Task.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py2") diff --git a/tests/unit/providers/dsl_files/provider_without_auth_schema.py b/tests/unit/providers/dsl_files/provider_without_auth_schema.py new file mode 100644 index 000000000..5bfe1f3e8 --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_without_auth_schema.py @@ -0,0 +1,9 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CloudProvider + + +class DslProviderWithNoAuthSchema(CloudProvider): + + """Sample provider with NO authentication schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD diff --git a/tests/unit/providers/dsl_files/provider_without_variables_in_custom_ep_schema.py b/tests/unit/providers/dsl_files/provider_without_variables_in_custom_ep_schema.py new file mode 100644 index 000000000..71f6395ef --- /dev/null +++ b/tests/unit/providers/dsl_files/provider_without_variables_in_custom_ep_schema.py @@ -0,0 +1,23 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.builtins import CalmVariable, CloudProvider, ProviderEndpointSchema + + +class DslProviderWithoutVariablesInCustomEPSchema(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema(type=PROVIDER.ENDPOINT_KIND.CUSTOM) + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] diff --git a/tests/unit/providers/dsl_files/resource_type_with_incorrect_action_type.py b/tests/unit/providers/dsl_files/resource_type_with_incorrect_action_type.py new file mode 100644 index 000000000..dcb37eb6f --- /dev/null +++ b/tests/unit/providers/dsl_files/resource_type_with_incorrect_action_type.py @@ -0,0 +1,62 @@ +from calm.dsl.constants import CLOUD_PROVIDER as PROVIDER +from calm.dsl.runbooks import RunbookTask as Task +from calm.dsl.builtins import ( + CalmVariable, + CloudProvider, + action, + ProviderEndpointSchema, + ResourceType, + action, +) + + +class DSLResourceTypeWithIncorrectActionType(ResourceType): + """Sample ResourceType""" + + resource_kind = "CUSTOM" + + @action + def List(type="sseedeeded"): + """List Action for HelloResourceType""" + outputs = [CalmVariable.Simple.string(name="resource_ids", value="")] + Task.SetVariable.escript( + name="List Resources", + variables=["resource_ids"], + script="print (\"resource_ids = ['resource1', 'resource2']\")", + ) + + +class DslProvider(CloudProvider): + + """Sample provider with authentication schema, variables & custom endpoint schema configured""" + + infra_type = PROVIDER.INFRA_TYPE.CLOUD + + resource_types = [DSLResourceTypeWithIncorrectActionType] + + # Defining variables for Authentication Schema + auth_schema_variables = [ + CalmVariable.Simple.string(name="username", value=""), + CalmVariable.Simple.Secret.string(name="password", value=""), + ] + + # Defining custom Endpoint Schema + endpoint_schema = ProviderEndpointSchema( + type=PROVIDER.ENDPOINT_KIND.CUSTOM, + variables=[ + CalmVariable.Simple.string(name="server_ip", value="1.1.1.1"), + CalmVariable.Simple.int(name="port_number", value="443"), + ], + ) + + # Defining variables for Provider attributes + variables = [ + CalmVariable.Simple.string(name="provider_var", value="provider_val"), + ] + + @action + def Verify(): + + """Verify action for Provider""" + + Task.Exec.escript(name="VerifyCreds", filename="scripts/verify_script.py2") diff --git a/tests/unit/providers/dsl_files/scripts/verify_script.py2 b/tests/unit/providers/dsl_files/scripts/verify_script.py2 new file mode 100644 index 000000000..492db7f91 --- /dev/null +++ b/tests/unit/providers/dsl_files/scripts/verify_script.py2 @@ -0,0 +1,9 @@ +# Script to verify credentials of custom provider accounts + +print ("Account credentials being validated:") +print ("Username: @@{username}@@, Password: @@{password}@@") + +print ("Making a dummy request to authenticate to the below provider endpoint") +print ("Server IP: @@{server_ip}@@, Port: @@{port_number}@@") + +print ("Successfully Authenticated!!!") diff --git a/tests/unit/providers/expected_jsons/provider_with_custom_ep_schema.json b/tests/unit/providers/expected_jsons/provider_with_custom_ep_schema.json new file mode 100644 index 000000000..c4304a0f0 --- /dev/null +++ b/tests/unit/providers/expected_jsons/provider_with_custom_ep_schema.json @@ -0,0 +1,93 @@ +{ + "spec": { + "name": "DslProviderWithCustomEPSchema", + "resources": { + "type": "CUSTOM", + "infra_type": "cloud", + "auth_schema_list": [ + { + "name": "username", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "password", + "description": "", + "type": "SECRET", + "label": "", + "attrs": { + "is_secret_modified": true, + "type": "SECRET" + }, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "endpoint_schema": { + "type": "CUSTOM", + "variable_list": [ + { + "name": "server_ip", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "1.1.1.1", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "port_number", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "INT", + "value": "443", + "data_type": "BASE", + "regex": { + "value": "^[\\d]*$", + "should_validate": false + }, + "is_hidden": false, + "is_mandatory": false + } + ] + }, + "variable_list": [ + { + "name": "provider_var", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "provider_val", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "resource_type_list": [], + "action_list": [], + "credential_definition_list": [] + }, + "description": "Sample provider with authentication schema, variables & custom endpoint schema configured" + }, + "metadata": { + "kind": "provider", + "name": "DslProviderWithCustomEPSchema" + } +} \ No newline at end of file diff --git a/tests/unit/providers/expected_jsons/provider_with_just_auth_schema.json b/tests/unit/providers/expected_jsons/provider_with_just_auth_schema.json new file mode 100644 index 000000000..8d9eadc2a --- /dev/null +++ b/tests/unit/providers/expected_jsons/provider_with_just_auth_schema.json @@ -0,0 +1,47 @@ +{ + "spec": { + "name": "DslProviderWithJustAuthSchema", + "resources": { + "type": "CUSTOM", + "infra_type": "cloud", + "auth_schema_list": [ + { + "name": "username", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "password", + "description": "", + "type": "SECRET", + "label": "", + "attrs": { + "is_secret_modified": true, + "type": "SECRET" + }, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "variable_list": [], + "resource_type_list": [], + "action_list": [], + "credential_definition_list": [] + }, + "description": "Sample provider with just authentication schema configured" + }, + "metadata": { + "kind": "provider", + "name": "DslProviderWithJustAuthSchema" + } +} \ No newline at end of file diff --git a/tests/unit/providers/expected_jsons/provider_with_none_ep_schema.json b/tests/unit/providers/expected_jsons/provider_with_none_ep_schema.json new file mode 100644 index 000000000..c81e9ed0e --- /dev/null +++ b/tests/unit/providers/expected_jsons/provider_with_none_ep_schema.json @@ -0,0 +1,63 @@ +{ + "spec": { + "name": "DslProviderWithNoneEPSchema", + "resources": { + "type": "CUSTOM", + "infra_type": "cloud", + "auth_schema_list": [ + { + "name": "username", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "password", + "description": "", + "type": "SECRET", + "label": "", + "attrs": { + "is_secret_modified": true, + "type": "SECRET" + }, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "endpoint_schema": { + "type": "NONE" + }, + "variable_list": [ + { + "name": "provider_var", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "provider_val", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "resource_type_list": [], + "action_list": [], + "credential_definition_list": [] + }, + "description": "Sample provider with authentication schema, variables & 'none' endpoint schema configured" + }, + "metadata": { + "kind": "provider", + "name": "DslProviderWithNoneEPSchema" + } +} \ No newline at end of file diff --git a/tests/unit/providers/expected_jsons/provider_with_ntnx_ep_schema.json b/tests/unit/providers/expected_jsons/provider_with_ntnx_ep_schema.json new file mode 100644 index 000000000..b5b7bc56f --- /dev/null +++ b/tests/unit/providers/expected_jsons/provider_with_ntnx_ep_schema.json @@ -0,0 +1,63 @@ +{ + "spec": { + "name": "DslProviderWithNtnxEPSchema", + "resources": { + "type": "CUSTOM", + "infra_type": "cloud", + "auth_schema_list": [ + { + "name": "username", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "password", + "description": "", + "type": "SECRET", + "label": "", + "attrs": { + "is_secret_modified": true, + "type": "SECRET" + }, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "endpoint_schema": { + "type": "NUTANIX_PC" + }, + "variable_list": [ + { + "name": "provider_var", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "provider_val", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "resource_type_list": [], + "action_list": [], + "credential_definition_list": [] + }, + "description": "Sample provider with authentication schema, variables & 'ntnx' endpoint schema configured" + }, + "metadata": { + "kind": "provider", + "name": "DslProviderWithNtnxEPSchema" + } +} \ No newline at end of file diff --git a/tests/unit/providers/expected_jsons/provider_with_test_account.json b/tests/unit/providers/expected_jsons/provider_with_test_account.json new file mode 100644 index 000000000..e64e5ee5d --- /dev/null +++ b/tests/unit/providers/expected_jsons/provider_with_test_account.json @@ -0,0 +1,208 @@ +{ + "spec": { + "name": "HelloProvider", + "resources": { + "type": "CUSTOM", + "infra_type": "cloud", + "auth_schema_list": [ + { + "name": "username", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "password", + "description": "", + "type": "SECRET", + "label": "", + "attrs": { + "is_secret_modified": true, + "type": "SECRET" + }, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + } + ], + "endpoint_schema": { + "type": "CUSTOM", + "variable_list": [ + { + "name": "server_ip", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "1.1.1.1", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "port_number", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "INT", + "value": "443", + "data_type": "BASE", + "regex": { + "value": "^[\\d]*$", + "should_validate": false + }, + "is_hidden": false, + "is_mandatory": true + } + ] + }, + "variable_list": [ + { + "name": "provider_var", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "provider_val", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "resource_type_list": [], + "action_list": [ + { + "name": "Verify", + "description": "Verify action for Provider", + "type": "provider", + "critical": false, + "runbook": { + "name": "HelloProvider_Verify_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "HelloProvider_Verify_dag" + }, + "task_definition_list": [ + { + "name": "HelloProvider_Verify_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "VerifyCreds" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "VerifyCreds", + "description": "", + "type": "EXEC", + "attrs": { + "script_type": "static_py3", + "script": "# Script to verify credentials of custom provider accounts\n\nprint (\"Account credentials being validated:\")\nprint (\"Username: @@{username}@@, Password: @@{password}@@\")\n\nprint (\"Making a dummy request to authenticate to the below provider endpoint\")\nprint (\"Server IP: @@{server_ip}@@, Port: @@{port_number}@@\")\n\nprint (\"Successfully Authenticated!!!\")\n" + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variable_list": [] + } + } + ], + "credential_definition_list": [], + "test_account": { + "name": "TestHelloAccount", + "description": "Used for test executions", + "data": { + "variable_list": [ + { + "name": "server_ip", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "10.10.10.10", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "port_number", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "INT", + "value": "9440", + "data_type": "BASE", + "regex": { + "value": "^[\\d]*$", + "should_validate": false + }, + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "username", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "root", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + }, + { + "name": "password", + "description": "", + "type": "SECRET", + "label": "", + "attrs": { + "is_secret_modified": true, + "type": "SECRET" + }, + "val_type": "STRING", + "value": "iamasecret", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": true + } + ] + }, + "type": "HelloProvider" + } + }, + "description": "Sample provider for Hello" + }, + "metadata": { + "kind": "provider", + "name": "HelloProvider" + } +} diff --git a/tests/unit/providers/expected_jsons/provider_with_variables.json b/tests/unit/providers/expected_jsons/provider_with_variables.json new file mode 100644 index 000000000..10b6ca17e --- /dev/null +++ b/tests/unit/providers/expected_jsons/provider_with_variables.json @@ -0,0 +1,60 @@ +{ + "spec": { + "name": "DslProviderWithVariables", + "resources": { + "type": "CUSTOM", + "infra_type": "cloud", + "auth_schema_list": [ + { + "name": "username", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "password", + "description": "", + "type": "SECRET", + "label": "", + "attrs": { + "is_secret_modified": true, + "type": "SECRET" + }, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "variable_list": [ + { + "name": "provider_var", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "provider_val", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "resource_type_list": [], + "action_list": [], + "credential_definition_list": [] + }, + "description": "Sample provider with authentication schema & variables configured" + }, + "metadata": { + "kind": "provider", + "name": "DslProviderWithVariables" + } +} \ No newline at end of file diff --git a/tests/unit/providers/expected_jsons/provider_with_verify_action.json b/tests/unit/providers/expected_jsons/provider_with_verify_action.json new file mode 100644 index 000000000..3f2b6fabd --- /dev/null +++ b/tests/unit/providers/expected_jsons/provider_with_verify_action.json @@ -0,0 +1,144 @@ +{ + "spec": { + "name": "DslProviderWithVerifyAction", + "resources": { + "type": "CUSTOM", + "infra_type": "cloud", + "auth_schema_list": [ + { + "name": "username", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "password", + "description": "", + "type": "SECRET", + "label": "", + "attrs": { + "is_secret_modified": true, + "type": "SECRET" + }, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "endpoint_schema": { + "type": "CUSTOM", + "variable_list": [ + { + "name": "server_ip", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + }, + { + "name": "port_number", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "INT", + "value": "", + "data_type": "BASE", + "regex": { + "value": "^[\\d]*$", + "should_validate": false + }, + "is_hidden": false, + "is_mandatory": false + } + ] + }, + "variable_list": [ + { + "name": "provider_var", + "description": "", + "type": "LOCAL", + "label": "", + "attrs": {}, + "val_type": "STRING", + "value": "", + "data_type": "BASE", + "is_hidden": false, + "is_mandatory": false + } + ], + "resource_type_list": [], + "action_list": [ + { + "name": "Verify", + "description": "Verify action for Provider", + "type": "provider", + "critical": false, + "runbook": { + "name": "DslProviderWithVerifyAction_Verify_runbook", + "description": "", + "main_task_local_reference": { + "kind": "app_task", + "name": "DslProviderWithVerifyAction_Verify_dag" + }, + "task_definition_list": [ + { + "name": "DslProviderWithVerifyAction_Verify_dag", + "description": "", + "type": "DAG", + "attrs": { + "edges": [] + }, + "status_map_list": [], + "child_tasks_local_reference_list": [ + { + "kind": "app_task", + "name": "VerifyCreds" + } + ], + "variable_list": [], + "retries": "", + "timeout_secs": "" + }, + { + "name": "VerifyCreds", + "description": "", + "type": "EXEC", + "attrs": { + "script_type": "static_py3", + "script": "# Script to verify credentials of custom provider accounts\n\nprint (\"Account credentials being validated:\")\nprint (\"Username: @@{username}@@, Password: @@{password}@@\")\n\nprint (\"Making a dummy request to authenticate to the below provider endpoint\")\nprint (\"Server IP: @@{server_ip}@@, Port: @@{port_number}@@\")\n\nprint (\"Successfully Authenticated!!!\")\n" + }, + "status_map_list": [], + "child_tasks_local_reference_list": [], + "variable_list": [], + "retries": "", + "timeout_secs": "" + } + ], + "variable_list": [], + "output_variable_list": [] + } + } + ], + "credential_definition_list": [] + }, + "description": "Sample provider with authentication schema, variables, endpoint schema & verify action configured" + }, + "metadata": { + "kind": "provider", + "name": "DslProviderWithVerifyAction" + } +} diff --git a/tests/unit/providers/test_provider_negative_scenarios.py b/tests/unit/providers/test_provider_negative_scenarios.py new file mode 100644 index 000000000..e966cbf13 --- /dev/null +++ b/tests/unit/providers/test_provider_negative_scenarios.py @@ -0,0 +1,78 @@ +import os +import pytest + +from calm.dsl.cli.providers import compile_provider +from calm.dsl.log import get_logging_handle + +LOG = get_logging_handle(__name__) + + +def _test_compile_negative_scenarios(provider_file, error_substring): + """ + Verifies whether appropriate exceptions are raised in error scenarios or not + + Args: + provider_file (string): Path to the DSL file that needs to be compiled + error_substring (string): Substring expected in the exception + """ + dir_path = os.path.dirname(os.path.realpath(__file__)) + try: + compile_provider(os.path.join(dir_path, provider_file)) + error_msg = "Expected '{}' error. But got none".format(error_substring) + LOG.error(error_msg) + assert False, error_msg + except SystemExit as ex: + assert error_substring.lower() in str(ex).lower() + + +@pytest.mark.provider +@pytest.mark.parametrize( + "provider_file, error_substring", + [ + ("./dsl_files/provider_without_auth_schema.py", "Auth_schema is required"), + ( + "./dsl_files/provider_without_variables_in_custom_ep_schema.py", + "No variables specified", + ), + ( + "./dsl_files/provider_with_incorrect_infra_type.py", + "Infra type should be one of", + ), + ( + "./dsl_files/provider_with_multiple_actions.py", + "Atmost one verify action can be added for provider", + ), + ( + "./dsl_files/provider_with_incorrect_action_name.py", + "Action should be named", + ), + ( + "./dsl_files/provider_with_incorrect_action_type.py", + "action should be of type 'provider'", + ), + ( + "./dsl_files/provider_with_incorrect_ep_schema_type.py", + "Endpoint Schema type should be one of", + ), + ( + "./dsl_files/provider_with_unexpected_ep_variables.py", + "Cannot specify Variables", + ), + ], +) +def test_provider_compile_negative_scenarios(provider_file, error_substring): + _test_compile_negative_scenarios(provider_file, error_substring) + + +@pytest.mark.provider +@pytest.mark.parametrize( + "provider_file, error_substring", + [ + ( + "./dsl_files/resource_type_with_incorrect_action_type.py", + "ResourceType Action's type should be one of", + ), + ], +) +def test_resource_type_compile_negative_scenarios(provider_file, error_substring): + _test_compile_negative_scenarios(provider_file, error_substring) diff --git a/tests/unit/providers/test_provider_without_rts.py b/tests/unit/providers/test_provider_without_rts.py new file mode 100644 index 000000000..996939fd5 --- /dev/null +++ b/tests/unit/providers/test_provider_without_rts.py @@ -0,0 +1,114 @@ +import json +import os +import pytest +from distutils.version import LooseVersion as LV + +from calm.dsl.store.version import Version +from calm.dsl.cli.providers import compile_provider +from calm.dsl.log import get_logging_handle + +LOG = get_logging_handle(__name__) + + +def _prepare_json_for_comparsion(generated_json): + """ + This helper functions pops all the fields where UUIDs are dynamically + generated & inserted during 'compile' operation + + Args: + generated_json (dict): Dictionary to do the edits on + """ + resources = generated_json["spec"]["resources"] + auth_schema_list = resources["auth_schema_list"] + variable_list = resources["variable_list"] + endpoint_schema_list = resources.get("endpoint_schema", {}).get("variable_list", []) + acc_var_list = ( + resources.get("test_account", {}).get("data", {}).get("variable_list", []) + ) + for var in auth_schema_list + variable_list + endpoint_schema_list + acc_var_list: + var.pop("uuid", None) + + resources.get("test_account", {}).pop("uuid", None) + + action_list = resources.get("action_list") + if action_list: + action, runbook = action_list[0], action_list[0]["runbook"] + action.pop("uuid", None) + runbook.pop("uuid", None) + runbook["main_task_local_reference"].pop("uuid", None) + tasks = runbook["task_definition_list"] + for task in tasks: + task.pop("uuid", None) + if task["type"] == "DAG": + for t in task["child_tasks_local_reference_list"]: + t.pop("uuid", None) + + generated_json["metadata"].pop("uuid", None) + + +def _test_compare_compile_result(provider_file, expected_output_file): + """ + Compares the runbook compilation and known output + + Args: + provider_file (string): Path to the DSL file that needs to be compiled + expected_output_file (string): Path to the JSON expected out of compile operation + """ + + LOG.info("JSON compilation test for {}".format(provider_file)) + + dir_path = os.path.dirname(os.path.realpath(__file__)) + generated_json = compile_provider(os.path.join(dir_path, provider_file)) + _prepare_json_for_comparsion(generated_json) + + known_json = open(os.path.join(dir_path, expected_output_file)).read() + known_json = json.loads(known_json) + + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) < LV("3.9.0"): + for action in known_json["spec"]["resources"]["action_list"]: + for task in action["runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + + assert generated_json == known_json + LOG.info("JSON compilation successful for {}".format(provider_file)) + + +# TODO: After all the development is done, update the JSONs +@pytest.mark.provider +@pytest.mark.parametrize( + "provider_file, expected_json_file", + [ + ( + "./dsl_files/provider_with_just_auth_schema.py", + "./expected_jsons/provider_with_just_auth_schema.json", + ), + ( + "./dsl_files/provider_with_custom_ep_schema.py", + "./expected_jsons/provider_with_custom_ep_schema.json", + ), + ( + "./dsl_files/provider_with_none_ep_schema.py", + "./expected_jsons/provider_with_none_ep_schema.json", + ), + ( + "./dsl_files/provider_with_ntnx_ep_schema.py", + "./expected_jsons/provider_with_ntnx_ep_schema.json", + ), + ( + "./dsl_files/provider_with_verify_action.py", + "./expected_jsons/provider_with_verify_action.json", + ), + ( + "./dsl_files/provider_with_test_account.py", + "./expected_jsons/provider_with_test_account.json", + ), + ( + "./dsl_files/provider_with_variables.py", + "./expected_jsons/provider_with_variables.json", + ), + ], +) +def test_provider_compile_without_rts(provider_file, expected_json_file): + _test_compare_compile_result(provider_file, expected_json_file) diff --git a/tests/unit/test_clone_runbook.py b/tests/unit/test_clone_runbook.py new file mode 100644 index 000000000..74c7bf829 --- /dev/null +++ b/tests/unit/test_clone_runbook.py @@ -0,0 +1,147 @@ +import pytest +import os +import time +import json +import uuid +import click +import traceback +from click.testing import CliRunner + +from calm.dsl.cli import main as cli +from calm.dsl.builtins import read_local_file +from calm.dsl.config import get_context +from calm.dsl.log import get_logging_handle +from calm.dsl.store.version import Version +from distutils.version import LooseVersion as LV + +LOG = get_logging_handle(__name__) + +CALM_VERSION = Version.get_version("Calm") + +DSL_RB_FILEPATH = "tests/sample_runbooks/simple_runbook.py" + + +@pytest.mark.skipif( + LV(CALM_VERSION) < LV("4.0.0"), + reason="Tests for runbook clone changes are introduced in 4.0.0", +) +class TestRBCloneCommand: + def setup_method(self): + """Method to instantiate to created_rb_list""" + + self.created_rb_list = [] + + def teardown_method(self): + """Method to delete created runbooks during tests""" + + for rb_name in self.created_rb_list: + LOG.info("Deleting Runbook {}".format(rb_name)) + runner = CliRunner() + result = runner.invoke(cli, ["delete", "runbook", rb_name]) + assert result.exit_code == 0 + + self.created_rb_list = [] + + def _create_rb(self, file, name): + runner = CliRunner() + result = runner.invoke( + cli, + [ + "create", + "runbook", + "--file={}".format(file), + "--name={}".format(name), + "--description='Test DSL Runbook; to delete'", + "--force", + ], + ) + if result.exit_code: + cli_res_dict = {"Output": result.output, "Exception": str(result.exception)} + LOG.info( + "Cli Response: {}".format( + json.dumps(cli_res_dict, indent=4, separators=(",", ": ")) + ) + ) + LOG.info( + "Traceback: \n{}".format( + "".join(traceback.format_tb(result.exc_info[2])) + ) + ) + pytest.fail("RB creation failed") + + def _test_rb_clone(self, original_rb_name, cloned_rb_name): + runner = CliRunner() + result = runner.invoke( + cli, + [ + "clone", + "runbook", + original_rb_name, + cloned_rb_name, + ], + ) + return result + + def test_rb_clone_command(self): + """ + Test 'calm clone runbook' command + """ + LOG.info("Testing 'calm clone runbook' command") + self.original_rb_name = "original_runbook" + str(uuid.uuid4())[-10:] + self._create_rb(DSL_RB_FILEPATH, self.original_rb_name) + self.created_rb_list.append(self.original_rb_name) + self.cloned_rb_name = "cloned_runbook" + str(uuid.uuid4())[-10:] + self.created_rb_list.append(self.cloned_rb_name) + + result = self._test_rb_clone(self.original_rb_name, self.cloned_rb_name) + + if result.exit_code: + cli_res_dict = {"Output": result.output, "Exception": str(result.exception)} + LOG.info( + "Cli Response: {}".format( + json.dumps(cli_res_dict, indent=4, separators=(",", ": ")) + ) + ) + LOG.info( + "Traceback: \n{}".format( + "".join(traceback.format_tb(result.exc_info[2])) + ) + ) + pytest.fail("RB clone call failed") + LOG.info("Success") + + def test_rb_clone_command_with_duplicate_name(self): + """ + Test 'calm clone runbook' command with runbook with duplicate name + """ + LOG.info( + "Testing 'calm clone runbook' command with runbook with duplicate name" + ) + + self.original_rb_name_duplicate = "original_runbook" + str(uuid.uuid4())[-10:] + self._create_rb(DSL_RB_FILEPATH, self.original_rb_name_duplicate) + self.created_rb_list.append(self.original_rb_name_duplicate) + + result = self._test_rb_clone( + self.original_rb_name_duplicate, self.original_rb_name_duplicate + ) + + if not result.exit_code: + pytest.fail("RB clone call with runbook with duplicate name failed") + LOG.info("Success") + + def test_rb_clone_command_with_invalid_runbook(self): + """ + Test 'calm clone runbook' command with runbook that does not exist + """ + LOG.info( + "Testing 'calm clone runbook' command with runbook that does not exist" + ) + self.cloned_rb_name = "cloned_runbook" + str(uuid.uuid4())[-10:] + self.invalid_rb_name = "invalid_runbook" + str(uuid.uuid4())[-10:] + + result = self._test_rb_clone(self.invalid_rb_name, self.cloned_rb_name) + + if not result.exit_code: + pytest.fail("RB clone call with runbook with duplicate name failed") + LOG.info("Success") diff --git a/tests/unit/test_dynamic_variable_with_endpoint_decompile.py b/tests/unit/test_dynamic_variable_with_endpoint_decompile.py new file mode 100644 index 000000000..24e1e731b --- /dev/null +++ b/tests/unit/test_dynamic_variable_with_endpoint_decompile.py @@ -0,0 +1,24 @@ +import os +import json +import shutil +from calm.dsl.builtins import VariableType +from calm.dsl.decompile.file_handler import init_bp_dir +from calm.dsl.decompile.variable import render_variable_template + + +def test_dynamic_variable_with_endpoint_decompile(): + _, _, _, scripts_dir = init_bp_dir("./tests/test_dynamic_variable_with_endpoint") + dir_path = os.path.dirname(os.path.realpath(__file__)) + file_path = os.path.join(dir_path, "./jsons/dynamic_variable_with_endpoint.json") + var_dict = json.loads(open(file_path).read()) + endpoints = [] + ep_list = [] + cls = VariableType.decompile(var_dict) + assert "target_endpoint=ref(windows_endpoint)" in render_variable_template( + cls, "runbook", endpoints=endpoints, ep_list=ep_list + ), "expected endpoint windows_endpoint to be decompiled in task" + assert "windows_endpoint" in ep_list, "endpoint name should be added in ep_list" + assert ( + 'windows_endpoint=Endpoint.use_existing("windows_endpoint")' in endpoints + ), "rendered enpoint should be added in endpoints" + shutil.rmtree(scripts_dir) diff --git a/tests/unit/test_escript_all_tasks.py b/tests/unit/test_escript_all_tasks.py index c36c6f985..44b1bf41d 100644 --- a/tests/unit/test_escript_all_tasks.py +++ b/tests/unit/test_escript_all_tasks.py @@ -1,7 +1,10 @@ import uuid import os import pytest +import json +from distutils.version import LooseVersion as LV +from calm.dsl.store.version import Version from calm.dsl.runbooks import * from calm.dsl.runbooks import ( RunbookTask as CalmTask, @@ -77,9 +80,14 @@ def _test_compare_compile_result(Runbook, json_file): dir_path = os.path.dirname(os.path.realpath(__file__)) file_path = os.path.join(dir_path, json_file) - generated_json = runbook_json(Runbook) - known_json = open(file_path).read() - assert generated_json == known_json + generated_json = json.loads(runbook_json(Runbook)) + known_json = json.loads(open(file_path).read()) + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) < LV("3.9.0"): + for task in known_json["runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + assert sorted(known_json.items()) == sorted(generated_json.items()) print("JSON compilation successful for {}".format(Runbook.action_name)) diff --git a/tests/unit/test_python_remote_all_tasks.py b/tests/unit/test_python_remote_all_tasks.py index afa68f858..b26f0f031 100644 --- a/tests/unit/test_python_remote_all_tasks.py +++ b/tests/unit/test_python_remote_all_tasks.py @@ -1,6 +1,9 @@ import os import pytest +import json +from distutils.version import LooseVersion as LV +from calm.dsl.store.version import Version from calm.dsl.runbooks import * from calm.dsl.runbooks import RunbookTask as CalmTask, CalmEndpoint as Endpoint @@ -44,9 +47,14 @@ def _test_compare_compile_result(Runbook, json_file): dir_path = os.path.dirname(os.path.realpath(__file__)) file_path = os.path.join(dir_path, json_file) - generated_json = runbook_json(Runbook) - known_json = open(file_path).read() - assert generated_json == known_json + generated_json = json.loads(runbook_json(Runbook)) + known_json = json.loads(open(file_path).read()) + CALM_VERSION = Version.get_version("Calm") + if LV(CALM_VERSION) < LV("3.9.0"): + for task in known_json["runbook"]["task_definition_list"]: + if "status_map_list" in task: + task.pop("status_map_list") + assert sorted(known_json.items()) == sorted(generated_json.items()) print("JSON compilation successful for {}".format(Runbook.action_name)) diff --git a/tests/vm_blueprints/single_vm_blueprint/single_vm_bp_output.json b/tests/vm_blueprints/single_vm_blueprint/single_vm_bp_output.json index 2a0db1d61..fabb53dc7 100644 --- a/tests/vm_blueprints/single_vm_blueprint/single_vm_bp_output.json +++ b/tests/vm_blueprints/single_vm_blueprint/single_vm_bp_output.json @@ -34,13 +34,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -67,13 +69,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -100,13 +104,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -133,13 +139,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -166,13 +174,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -199,13 +209,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -237,6 +249,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -259,13 +272,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -e\n\necho \"Installation script goes here ...\"\nsleep 3\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_Profile1Package_action_uninstall", @@ -286,13 +301,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -396,6 +413,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -418,13 +436,15 @@ "script_type": "static_py3", "script": "print ('Hello!')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -528,6 +548,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -550,13 +571,15 @@ "script_type": "sh", "script": "echo \"Hello\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ] diff --git a/tests/vm_blueprints/single_vm_multi_profile_blueprint/single_vm_multi_profile_bp_output.json b/tests/vm_blueprints/single_vm_multi_profile_blueprint/single_vm_multi_profile_bp_output.json index db0f1419b..6d1262768 100644 --- a/tests/vm_blueprints/single_vm_multi_profile_blueprint/single_vm_multi_profile_bp_output.json +++ b/tests/vm_blueprints/single_vm_multi_profile_blueprint/single_vm_multi_profile_bp_output.json @@ -34,13 +34,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -67,13 +69,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -100,13 +104,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -133,13 +139,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -166,13 +174,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, { @@ -199,13 +209,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -237,6 +249,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -259,13 +272,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -e\n\necho \"Installation script goes here ...\"\nsleep 3\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_AhvVmSmallProfilePackage_action_uninstall", @@ -286,13 +301,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -328,6 +345,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -350,13 +368,15 @@ "script_type": "sh", "script": "#!/bin/bash\n\nset -e\n\necho \"Installation script goes here ...\"\nsleep 3\n" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] }, "uninstall_runbook": { "name": "Runbook_for_Package_AhvVmLargeProfilePackage_action_uninstall", @@ -377,13 +397,15 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } }, "service_local_reference_list": [ @@ -487,6 +509,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -509,13 +532,15 @@ "script_type": "static_py3", "script": "print ('Hello!')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -624,6 +649,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -646,13 +672,15 @@ "script_type": "static_py3", "script": "print ('Hello!')" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ], @@ -756,6 +784,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -778,13 +807,15 @@ "script_type": "sh", "script": "echo \"Hello\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ] @@ -856,6 +887,7 @@ "attrs": { "edges": [] }, + "status_map_list": [], "child_tasks_local_reference_list": [ { "kind": "app_task", @@ -878,13 +910,15 @@ "script_type": "sh", "script": "echo \"Hello\"" }, + "status_map_list": [], "child_tasks_local_reference_list": [], "variable_list": [], "retries": "", "timeout_secs": "" } ], - "variable_list": [] + "variable_list": [], + "output_variables": [] } } ] diff --git a/tests/vm_blueprints/test_vm_blueprints.py b/tests/vm_blueprints/test_vm_blueprints.py index 0e86d2ae5..d6a7ef72f 100644 --- a/tests/vm_blueprints/test_vm_blueprints.py +++ b/tests/vm_blueprints/test_vm_blueprints.py @@ -12,6 +12,8 @@ # for tcs from calm.dsl.store import Version from distutils.version import LooseVersion as LV +from tests.helper.status_map_helper import remove_status_map_from_bp +from tests.helper.output_variables_helper import remove_output_variables_from_bp # Setting the recursion limit to max for sys.setrecursionlimit(100000) @@ -148,4 +150,9 @@ def test_vm_bp_compile(self, bp_file_path, bp_output_file_path): for cred in known_json["credential_definition_list"]: cred["cred_class"] = "static" + if LV(CALM_VERSION) < LV("3.9.0"): + remove_status_map_from_bp(known_json) + + remove_output_variables_from_bp(known_json) + remove_output_variables_from_bp(generated_json) assert generated_json == known_json