diff --git a/host-affinity/Host_Affinity_Update.json b/host-affinity/Host_Affinity_Update.json new file mode 100644 index 0000000..ef201c7 --- /dev/null +++ b/host-affinity/Host_Affinity_Update.json @@ -0,0 +1 @@ +{"status":{},"contains_secrets":false,"product_version":"3.5.0","spec":{"description":"This blueprint will create a VM and associated it with the \"AppType\" category. Subsequently, it will associate the selected hosts into host category and create a VM affinity policy.","resources":{"client_attrs":{"b1a5673a_deployment":{"y":-1498.315939144,"x":502.1135729765},"None":{"y":-1255.4802627563,"x":317.5502319336}},"service_definition_list":[{"singleton":false,"action_list":[{"description":"System action for creating an application","type":"system","critical":false,"runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"fe039418_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]}],"description":"","name":"36384ebb_runbook","main_task_local_reference":{"kind":"app_task","name":"fe039418_dag"},"variable_list":[]},"name":"action_create"},{"description":"System action for deleting an application. Deletes created VMs as well","type":"system","critical":false,"runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Delete Affinity Policy"}],"name":"08b9fefb_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Delete Affinity Policy","attrs":{"type":"CALL_RUNBOOK","inarg_list":[],"runbook_reference":{"kind":"app_runbook","name":"ae1f02ed_runbook"}},"timeout_secs":"0","type":"CALL_RUNBOOK","variable_list":[]}],"description":"","name":"602e942e_runbook","main_task_local_reference":{"kind":"app_task","name":"08b9fefb_dag"},"variable_list":[]},"name":"action_delete"},{"description":"System action for starting an application","type":"system","critical":false,"runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"5769341c_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]}],"description":"","name":"e2b19c60_runbook","main_task_local_reference":{"kind":"app_task","name":"5769341c_dag"},"variable_list":[]},"name":"action_start"},{"description":"System action for stopping an application","type":"system","critical":false,"runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"798c425f_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]}],"description":"","name":"426ac198_runbook","main_task_local_reference":{"kind":"app_task","name":"798c425f_dag"},"variable_list":[]},"name":"action_stop"},{"description":"System action for restarting an application","type":"system","critical":false,"runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"4c1b19be_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]}],"description":"","name":"8f1638ba_runbook","main_task_local_reference":{"kind":"app_task","name":"4c1b19be_dag"},"variable_list":[]},"name":"action_restart"},{"description":"","type":"user","critical":false,"runbook":{"task_definition_list":[{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Create Categories"},{"kind":"app_task","name":"Associate Hosts with Category"},{"kind":"app_task","name":"Associate VM with Category"},{"kind":"app_task","name":"Create Affinity Policy"},{"kind":"app_task","name":"Check for VM Affinity Policy"}],"name":"78e89ed3_dag","attrs":{"edges":[{"from_task_reference":{"kind":"app_task","name":"Create Categories"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Associate Hosts with Category"}},{"from_task_reference":{"kind":"app_task","name":"Create Affinity Policy"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Check for VM Affinity Policy"}},{"from_task_reference":{"kind":"app_task","name":"Associate Hosts with Category"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Associate VM with Category"}},{"from_task_reference":{"kind":"app_task","name":"Associate VM with Category"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Create Affinity Policy"}}],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Create Categories","attrs":{"script":"\ndef setup_category(category_name, category_value): \n\n pc_ip = '@@{PC_IP}@@'\n user = \"@@{PC_Creds.username}@@\"\n password = \"@@{PC_Creds.secret}@@\"\n #jwt = '@@{calm_jwt}@@'\n base_url = \"https:\/\/{}:9440\/api\/nutanix\/v3\".format(pc_ip)\n\n #headers = {'Content-Type': 'application\/json', 'Accept':'application\/json', 'Authorization': 'Bearer {}'.format(jwt)}\n headers = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\n\n project_name = \"@@{project_name}@@\"\n \n\n ### --------------------------------------------------------------------------------- ###\n def create_category_name(base_url, headers, user, password, category_name):\n method = 'PUT'\n url = base_url + \"\/categories\/{}\".format(category_name)\n payload = { \"api_version\": \"3.1.0\", \"name\": category_name }\n print(\"Making a {} API call to {}\".format(method, url))\n resp = urlreq(\n url,\n verb=method,\n params=json.dumps(payload),\n headers=headers,\n verify=False,\n auth=\"BASIC\", \n user=user, \n passwd=password\n )\n\n if resp.ok:\n json_resp = json.loads(resp.content)\n return json_resp[\"name\"]\n else:\n print(\"Request failed\")\n print(\"Headers: {}\".format(headers))\n print('Status code: {}'.format(resp.status_code))\n print('Response: {}'.format(json.dumps(json.loads(resp.content), indent=4)))\n exit(1)\n ### --------------------------------------------------------------------------------- ### \n\n ### --------------------------------------------------------------------------------- ###\n def create_category_value(base_url, headers, user, password, category_name, category_value):\n method = 'PUT'\n url = base_url + \"\/categories\/{0}\/{1}\".format(category_name,category_value)\n payload = { \"api_version\": \"3.1.0\", \"value\": category_value }\n print(\"Making a {} API call to {}\".format(method, url))\n resp = urlreq(\n url,\n verb=method,\n params=json.dumps(payload),\n headers=headers,\n verify=False,\n auth=\"BASIC\", \n user=user, \n passwd=password\n )\n\n if resp.ok:\n json_resp = json.loads(resp.content)\n return json_resp[\"value\"]\n else:\n print(\"Request failed\")\n print(\"Headers: {}\".format(headers))\n print('Status code: {}'.format(resp.status_code))\n print('Response: {}'.format(json.dumps(json.loads(resp.content), indent=4)))\n exit(1)\n ### --------------------------------------------------------------------------------- ### \n\n ### --------------------------------------------------------------------------------- ### \n create_category_name(base_url, headers, user, password, category_name)\n create_category_value(base_url, headers, user, password, category_name, category_value)\n ### --------------------------------------------------------------------------------- ### \n\nsetup_category(\"@@{host_category_name}@@\",\"@@{host_category_value}@@\")\nsetup_category(\"@@{vm_category_name}@@\",\"@@{vm_category_value}@@\")\n\n\n","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Associate Hosts with Category","attrs":{"exit_status":[],"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\nhosts = \"@@{host_name_list}@@\".replace(\" \", \"\") \nif len(hosts): \n print \"selected hosts: {0}\".format(hosts)\nelse: \n print(\"hosts is empty\")\n exit(1)\n\nselected_hosts_name = hosts.split(\",\")\nprint selected_hosts_name\n\npayload = {\n \n}\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\n#print \"Retrieve Host List Status code: {}\".format(r.status_code)\n#print \"Output: {}\".format(r.text)\nhost_uuid_list = []\nif r.ok:\n host_json = r.json()\n for host in host_json['entities']:\n current_host_spec = host['spec']\n if 'name' in current_host_spec:\n current_host_name = host['spec']['name']\n # print \"Loop the Host Name: {}\".format(current_host_name)\n for selected_host in selected_hosts_name:\n # print \"for loop for selected_host: {}\".format(selected_host)\n if (current_host_name == selected_host):\n # print \"Selected Host Name: {}\".format(current_host_name)\n # print \"Host_uuid={}\".format(host['metadata']['uuid'])\n \t host_uuid_list.append(host['metadata']['uuid'])\n break\n else:\n continue\n else:\n # print \"host name is null\"\n continue\n \n \n print \"Selected_Hosts_UUID_List={}\".format(host_uuid_list)\nelse:\n exit(1)\n \npayload2 = {\n}\n\ncategories_mapping = {\n \"@@{host_category_name}@@\": [\n \"@@{host_category_value}@@\"\n ]\n}\ncategories = {\n \"@@{host_category_name}@@\": \"@@{host_category_value}@@\"\n}\nurl_method2 = \"GET\"\n\nfor selected_host in host_uuid_list:\n\n url2 = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\/\" + selected_host\n print \"Associate host with category url: {}\".format(url2)\n r2 = process_request(url2, url_method2, user, password, headers, json.dumps(payload2))\n #print \"Retrieve Host Detail Status Code: {0}\".format(r2.status_code)\n #print \"Output: {0}\".format(r2.text)\n if r2.ok:\n selected_host_json = r2.json()\n del selected_host_json['status']\n del selected_host_json['metadata']['categories']\n del selected_host_json['metadata']['categories_mapping']\n selected_host_json['metadata']['categories_mapping'] = categories_mapping\n selected_host_json['metadata']['categories'] = categories\n print \"Put JSON: {0}\".format(selected_host_json)\n r3 = process_request(url2, \"PUT\", user, password, headers, json.dumps(selected_host_json))\n print \"Update Host with category status code: {0}\".format(r3.status_code)\n print \"Output: {0}\".format(r3.text)\n if r3.ok:\n print \"Host name: {0} updated with categories\".format(selected_host_json['spec']['name'])\n else:\n exit(1)\n else:\n exit(1)\nprint \"All selected hosts associated with host category and value successfully\"\n \n","eval_variables":["Selected_Hosts_UUID_List"],"eval_scope":"local","type":"","script_type":"static"},"timeout_secs":"0","type":"SET_VARIABLE","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Associate VM with Category","attrs":{"exit_status":[],"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\npayload = {\n \"filter\": \"vm_name==@@{name}@@\"\n \n}\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vms\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Retrieve VM List Status code: {}\".format(r.status_code)\nprint \"Output: {}\".format(r.text)\nvm_uuid = \"empty\"\nif r.ok:\n vm_json = r.json()\n for vm in vm_json['entities']:\n current_vm_spec = vm['spec']\n if 'name' in current_vm_spec:\n if (vm['spec']['name'] == \"@@{name}@@\"):\n vm_uuid = vm['metadata']['uuid']\n break\n else:\n continue\nelse:\n exit(1)\n \nprint \"vm_uuid={}\".format(vm_uuid)\nif vm_uuid==\"empty\":\n exit(1)\n \npayload2 = {\n}\n\nurl_method2 = \"GET\"\nurl2 = base_url + \"\/{}\".format(vm_uuid)\nprint \"url2: {}\".format(url2)\nr2 = process_request(url2, url_method2, user, password, headers, json.dumps(payload2))\nprint \"R2 Status Code: {}\".format(r2.status_code)\nprint \"R2 Output: {}\".format(r2.text)\nif r2.ok:\n vm_json = r2.json()\nelse:\n print \"Unable to retrieve VM: {}\".format(\"@@{name}@@\")\n exit(1)\n\ncategories_mapping = {\n \"@@{vm_category_name}@@\": [\n \"@@{vm_category_value}@@\"\n ]\n}\ncategories = {\n \"@@{vm_category_name}@@\": \"@@{vm_category_value}@@\"\n}\n\ndel vm_json['status']\ndel vm_json['metadata']['categories']\ndel vm_json['metadata']['categories_mapping']\nvm_json['metadata']['categories_mapping'] = categories_mapping\nvm_json['metadata']['categories'] = categories\nprint \"Put JSON: {0}\".format(vm_json)\nurl3 = base_url + \"\/{}\".format(vm_uuid)\nr3 = process_request(url2, \"PUT\", user, password, headers, json.dumps(vm_json))\nprint \"Update VM with category status code: {0}\".format(r3.status_code)\nprint \"Output: {0}\".format(r3.text)\nif r3.ok:\n print \"VM name: {0} updated with categories\".format(\"@@{name}@@\")\nelse:\n exit(1)\n","eval_variables":["vm_uuid"],"eval_scope":"local","type":"","script_type":"static"},"timeout_secs":"0","type":"SET_VARIABLE","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Create Affinity Policy","attrs":{"exit_status":[],"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\n\npayload = {\n \"config\": {\n \"vm_categories\": {\n \"@@{vm_category_name}@@\": [\n \"@@{vm_category_value}@@\"\n ]\n },\n \"description\": \"VM affinity policy created using Nutanix Calm\",\n \"host_categories\": {\n \"@@{host_category_name}@@\": [\n \"@@{host_category_value}@@\"\n ]\n },\n \"name\": \"@@{vm_affinity_policy}@@\"\n }\n}\nprint \"Payload: {}\".format(json.dumps(payload))\n\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vm_host_affinity_policies\"\nurl = base_url\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Status code: {}\".format(r.status_code)\nprint \"Output: {}\".format(r.text)\nif r.ok:\n print \"VM Affinity Policy during creation\"\nelse:\n print \"Error creating VM Affinity Policy\"\n exit(1)","eval_variables":["vm_affinity_policy_uuid"],"eval_scope":"local","type":"","script_type":"static"},"timeout_secs":"0","type":"SET_VARIABLE","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Check for VM Affinity Policy","attrs":{"exit_status":[],"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\n\npayload = {\n}\n\nurl = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vm_host_affinity_policies\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\ncount = 0\nwhile count < 6:\n r = urlreq(url, url_method, auth=\"BASIC\", user=user, passwd=password, params=json.dumps(payload), verify=False, headers=headers)\n print \"Status code: {}\".format(r.status_code)\n print \"Output: {}\".format(r.text)\n count = count + 1\n if r.ok:\n vm_host_affinity_json = r.json()\n for vm_host_affinity in vm_host_affinity_json['entities']:\n if vm_host_affinity['info']['config']['name'] == '@@{vm_affinity_policy}@@':\n print \"vm_affinity_policy_uuid={0}\".format(vm_host_affinity['metadata']['uuid'])\n exit(0)\n elif r.status_code == 404:\n print \"vm host affinity policy {0} is in the midst of creation. Sleeping for 10 seconds\".format(\"@@{vm_affinity_policy}@@\")\n sleep(10)\n\nprint \"No VM Affinity Policy {0} found after 60 seconds\".format(\"@@{vm_affinity_policy}@@\")\nexit(1)\n","eval_variables":["vm_affinity_policy_uuid"],"eval_scope":"local","type":"","script_type":"static"},"timeout_secs":"0","type":"SET_VARIABLE","variable_list":[]}],"description":"","name":"5f6ff0a8_runbook","main_task_local_reference":{"kind":"app_task","name":"78e89ed3_dag"},"variable_list":[]},"name":"Create New Affinity Policy"},{"description":"","type":"user","critical":false,"runbook":{"task_definition_list":[{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"GetSelectedHostNameList"},{"kind":"app_task","name":"Delete VM Affinity Policy"},{"kind":"app_task","name":"Wait"},{"kind":"app_task","name":"Delete Host to Category Association"}],"name":"78913458_dag","attrs":{"edges":[{"from_task_reference":{"kind":"app_task","name":"GetSelectedHostNameList"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Delete VM Affinity Policy"}},{"from_task_reference":{"kind":"app_task","name":"Delete VM Affinity Policy"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Wait"}},{"from_task_reference":{"kind":"app_task","name":"Wait"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Delete Host to Category Association"}}],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"GetSelectedHostNameList","attrs":{"exit_status":[],"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\npayload = {\n \n}\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/apps\"\nurl = base_url + \"\/{}\".format(\"@@{calm_application_uuid}@@\")\n\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"GET\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Retrieve Calm Application Status code: {}\".format(r.status_code)\n#print \"Output: {}\".format(r.text)\nhost_name_list = []\nif r.ok:\n host_json = r.json()\n for variable in host_json['spec']['resources']['variable_list']:\n if 'name' in variable:\n if variable['name'] == \"host_name_list\":\n print \"selectedhostnamelist={}\".format(variable['value'])\n exit(0)\nelse:\n exit(1)\n ","eval_variables":["selectedhostnamelist"],"eval_scope":"local","type":"","script_type":"static"},"timeout_secs":"0","type":"SET_VARIABLE","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Delete VM Affinity Policy","attrs":{"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\n\npayload = {\n\n}\n\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vm_host_affinity_policies\/@@{vm_affinity_policy_uuid}@@\"\nurl = base_url\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"DELETE\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Status code: {}\".format(r.status_code)\nprint \"Output: {}\".format(r.text)\nif r.ok:\n print \"VM Affinity Policy Name: {0} UUID: {1} deleted successfully\".format(\"@@{vm_affinity_policy}@@\", \"@@{vm_affinity_policy_uuid}@@\")\nelse:\n print \"Error deleting VM Affinity Policy\"\n exit(1)","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Wait","attrs":{"type":"","interval_secs":10},"timeout_secs":"0","type":"DELAY","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Delete Host to Category Association","attrs":{"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\n#hosts = \"@@{host_name_list}@@\".replace(\" \", \"\") \nhosts = \"@@{selectedhostnamelist}@@\".replace(\" \",\"\")\nif len(hosts): \n print \"selected hosts: {0}\".format(hosts)\nelse: \n print(\"hosts is empty\")\n exit(1)\n\nselected_hosts_name = hosts.split(\",\")\nprint selected_hosts_name\n\npayload = {\n \n}\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Retrieve Host List Status code: {}\".format(r.status_code)\nprint \"Output: {}\".format(r.text)\nhost_uuid_list = []\nif r.ok:\n host_json = r.json()\n for host in host_json['entities']:\n current_host_spec = host['spec']\n if 'name' in current_host_spec:\n current_host_name = host['spec']['name']\n # print \"Loop the Host Name: {}\".format(current_host_name)\n for selected_host in selected_hosts_name:\n # print \"for loop for selected_host: {}\".format(selected_host)\n if (current_host_name == selected_host):\n # print \"Selected Host Name: {}\".format(current_host_name)\n # print \"Host_uuid={}\".format(host['metadata']['uuid'])\n host_uuid_list.append(host['metadata']['uuid'])\n break\n else:\n continue\n else:\n # print \"host name is null\"\n continue\n \n \n print \"Selected_Hosts_UUID_List={}\".format(host_uuid_list)\nelse:\n exit(1)\n \npayload2 = {\n}\n\ncategories_mapping = {\n \n}\ncategories = {\n \n}\nurl_method2 = \"GET\"\n\nfor selected_host in host_uuid_list:\n\n url2 = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\/\" + selected_host\n r2 = process_request(url2, url_method2, user, password, headers, json.dumps(payload2))\n print \"Retrieve Host Detail Status Code: {0}\".format(r2.status_code)\n print \"Output: {0}\".format(r2.text)\n if r2.ok:\n selected_host_json = r2.json()\n del selected_host_json['status']\n del selected_host_json['metadata']['categories']\n del selected_host_json['metadata']['categories_mapping']\n selected_host_json['metadata']['categories_mapping'] = categories_mapping\n selected_host_json['metadata']['categories'] = categories\n print \"Put JSON: {0}\".format(selected_host_json)\n r3 = process_request(url2, \"PUT\", user, password, headers, json.dumps(selected_host_json))\n print \"Delete category from host status code: {0}\".format(r3.status_code)\n print \"Output: {0}\".format(r3.text)\n if r3.ok:\n print \"Categories deleting from Host name: {0}\".format(selected_host_json['spec']['name'])\n else:\n exit(1)\n else:\n exit(1)\nprint \"All categories deleted from selected hosts successfully\"\n \n","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]}],"description":"","name":"ae1f02ed_runbook","main_task_local_reference":{"kind":"app_task","name":"78913458_dag"},"variable_list":[]},"name":"Delete Affinity Policy"},{"description":"","type":"user","critical":false,"runbook":{"task_definition_list":[{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Associate Hosts with Category"},{"kind":"app_task","name":"Create Affinity Policy"},{"kind":"app_task","name":"Check for VM Affinity Policy"},{"kind":"app_task","name":"Set new hosts"}],"name":"946244f5_dag","attrs":{"edges":[{"from_task_reference":{"kind":"app_task","name":"Create Affinity Policy"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Check for VM Affinity Policy"}},{"from_task_reference":{"kind":"app_task","name":"Check for VM Affinity Policy"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Set new hosts"}},{"from_task_reference":{"kind":"app_task","name":"Associate Hosts with Category"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Create Affinity Policy"}}],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Associate Hosts with Category","attrs":{"exit_status":[],"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\nhosts = \"@@{new_host_name_list}@@\".replace(\" \", \"\") \n\nif len(hosts): \n print \"selected hosts: {0}\".format(hosts)\nelse: \n print(\"hosts is empty\")\n exit(1)\n\nselected_hosts_name = hosts.split(\",\")\nprint selected_hosts_name\n\npayload = {\n \n}\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Retrieve Host List Status code: {}\".format(r.status_code)\nprint \"Output: {}\".format(r.text)\nhost_uuid_list = []\nif r.ok:\n host_json = r.json()\n for host in host_json['entities']:\n current_host_spec = host['spec']\n if 'name' in current_host_spec:\n current_host_name = host['spec']['name']\n # print \"Loop the Host Name: {}\".format(current_host_name)\n for selected_host in selected_hosts_name:\n # print \"for loop for selected_host: {}\".format(selected_host)\n if (current_host_name == selected_host):\n # print \"Selected Host Name: {}\".format(current_host_name)\n # print \"Host_uuid={}\".format(host['metadata']['uuid'])\n \t host_uuid_list.append(host['metadata']['uuid'])\n break\n else:\n continue\n else:\n # print \"host name is null\"\n continue\n \n \n print \"Selected_Hosts_UUID_List={}\".format(host_uuid_list)\nelse:\n exit(1)\n \npayload2 = {\n}\n\ncategories_mapping = {\n \"@@{host_category_name}@@\": [\n \"@@{host_category_value}@@\"\n ]\n}\ncategories = {\n \"@@{host_category_name}@@\": \"@@{host_category_value}@@\"\n}\nurl_method2 = \"GET\"\n\nfor selected_host in host_uuid_list:\n\n url2 = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\/\" + selected_host\n r2 = process_request(url2, url_method2, user, password, headers, json.dumps(payload2))\n print \"Retrieve Host Detail Status Code: {0}\".format(r2.status_code)\n print \"Output: {0}\".format(r2.text)\n if r2.ok:\n selected_host_json = r2.json()\n del selected_host_json['status']\n del selected_host_json['metadata']['categories']\n del selected_host_json['metadata']['categories_mapping']\n selected_host_json['metadata']['categories_mapping'] = categories_mapping\n selected_host_json['metadata']['categories'] = categories\n print \"Put JSON: {0}\".format(selected_host_json)\n r3 = process_request(url2, \"PUT\", user, password, headers, json.dumps(selected_host_json))\n print \"Update Host with category status code: {0}\".format(r3.status_code)\n print \"Output: {0}\".format(r3.text)\n if r3.ok:\n print \"Host name: {0} updated with categories\".format(selected_host_json['spec']['name'])\n else:\n exit(1)\n else:\n exit(1)\nsleep(5)\nprint \"All selected hosts associated with host category and value successfully\"\nprint \"host_name_list=@@{new_host_name_list}@@\"\n \n","eval_variables":["Selected_Hosts_UUID_List","host_name_list"],"eval_scope":"local","type":"","script_type":"static"},"timeout_secs":"0","type":"SET_VARIABLE","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Create Affinity Policy","attrs":{"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\n\npayload = {\n \"config\": {\n \"vm_categories\": {\n \"@@{vm_category_name}@@\": [\n \"@@{vm_category_value}@@\"\n ]\n },\n \"description\": \"VM affinity policy created using Nutanix Calm\",\n \"host_categories\": {\n \"@@{host_category_name}@@\": [\n \"@@{host_category_value}@@\"\n ]\n },\n \"name\": \"@@{vm_affinity_policy}@@\"\n }\n}\nprint \"Payload: {}\".format(json.dumps(payload))\n\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vm_host_affinity_policies\"\nurl = base_url\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Status code: {}\".format(r.status_code)\nprint \"Output: {}\".format(r.text)\nif r.ok:\n sleep(5)\n print \"VM Affinity Policy during creation\"\nelse:\n print \"Error creating VM Affinity Policy\"\n exit(1)","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Check for VM Affinity Policy","attrs":{"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\n\nsleep(10)\npayload = {\n}\n\nurl = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vm_host_affinity_policies\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\ncount = 0\nwhile count < 6:\n r = urlreq(url, url_method, auth=\"BASIC\", user=user, passwd=password, params=json.dumps(payload), verify=False, headers=headers)\n print \"Status code: {}\".format(r.status_code)\n print \"Output: {}\".format(r.text)\n count = count + 1\n if r.ok:\n vm_host_affinity_json = r.json()\n for vm_host_affinity in vm_host_affinity_json['entities']:\n if vm_host_affinity['info']['config']['name'] == '@@{vm_affinity_policy}@@':\n print \"vm_affinity_policy_uuid={0}\".format(vm_host_affinity['metadata']['uuid'])\n exit(0)\n elif r.status_code == 404:\n print \"vm host affinity policy {0} is in the midst of creation. Sleeping for 10 seconds\".format(\"@@{vm_affinity_policy}@@\")\n sleep(5)\n\nprint \"No VM Affinity Policy {0} found after 60 seconds\".format(\"@@{vm_affinity_policy}@@\")\nexit(1)\n","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Set new hosts","attrs":{"exit_status":[],"script":"hosts = \"@@{new_host_name_list}@@\"\nhost_name_list = hosts.split(\",\")\nprint \"host_name_list={0}\".format(host_name_list)","eval_variables":["host_name_list"],"eval_scope":"local","type":"","script_type":"static"},"timeout_secs":"0","type":"SET_VARIABLE","variable_list":[]}],"description":"","name":"89d045aa_runbook","main_task_local_reference":{"kind":"app_task","name":"946244f5_dag"},"variable_list":[]},"name":"Replace New Affinity Policy"}],"depends_on_list":[],"name":"Centos","port_list":[],"tier":"","variable_list":[{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"vm_affinity_policy_uuid","value":"","label":"","attrs":{"type":""},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"LIST","type":"LOCAL","name":"selected_hosts_name","value":"","label":"","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[""]}}],"description":""}],"substrate_definition_list":[{"description":"","action_list":[{"description":"","type":"fragment","critical":false,"runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_substrate","name":"Centos_VM"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"89ec28e4_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]}],"description":"","name":"98a1f2a9_runbook","main_task_local_reference":{"kind":"app_task","name":"89ec28e4_dag"},"variable_list":[]},"name":"pre_action_create"},{"description":"","type":"fragment","critical":false,"runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_substrate","name":"Centos_VM"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"97f3b2cc_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]}],"description":"","name":"cb84f4d9_runbook","main_task_local_reference":{"kind":"app_task","name":"97f3b2cc_dag"},"variable_list":[]},"name":"post_action_delete"}],"type":"AHV_VM","name":"Centos_VM","readiness_probe":{"connection_type":"SSH","retries":"5","connection_protocol":"","connection_port":22,"address":"@@{platform.status.resources.nic_list[0].ip_endpoint_list[0].ip}@@","delay_secs":"60","disable_readiness_probe":false,"login_credential_local_reference":{"kind":"app_credential","name":"Centos 2 Credential"}},"editables":{"create_spec":{"categories":true}},"os_type":"Linux","create_spec":{"name":"vm-@@{calm_array_index}@@-@@{calm_time}@@","resources":{"nic_list":[{"nic_type":"NORMAL_NIC","vpc_reference":null,"ip_endpoint_list":[],"network_function_chain_reference":null,"network_function_nic_type":"INGRESS","mac_address":"","subnet_reference":{"kind":"subnet","type":"","name":"","uuid":"7c19b244-bcc7-4186-9f09-84db45dec57f"},"type":""}],"serial_port_list":[],"guest_tools":null,"num_vcpus_per_socket":1,"num_sockets":2,"gpu_list":[],"memory_size_mib":2048,"parent_reference":null,"hardware_clock_timezone":"","guest_customization":{"cloud_init":{"meta_data":"","type":"","user_data":"#cloud-config\nfqdn: @@{name}@@\nmanage_etc_hosts: true\nhostname: @@{name}@@\ndisable_root: False\nssh_enabled: True\nssh_pwauth: True\nusers:\n - name: @@{Nutanix.username}@@\n passwd: @@{Centos 2 Credential.secret}@@\n chpasswd: { expire: False }\n ssh-authorized-keys:\n - @@{Nutanix.public_key}@@\n ssh_pwauth: True\n sudo: ['ALL=(ALL) NOPASSWD:ALL']\n# Change root default password in image\nchpasswd:\n list: |\n @@{Centos 2 Credential.username}@@:@@{Centos 2 Credential.secret}@@\n expire: False"},"type":"","sysprep":null},"power_state":"ON","type":"","account_uuid":"2c06ed7e-f7f1-4e42-87d8-ab93a05d520e","boot_config":{"boot_device":{"type":"","disk_address":{"type":"","device_index":0,"adapter_type":"SCSI"}},"type":"","boot_type":"LEGACY","mac_address":""},"disk_list":[{"data_source_reference":{"kind":"image","type":"","name":"Centos7_Calm","uuid":"aee65068-d469-4a25-bc74-8882b16b6b7d"},"type":"","disk_size_mib":0,"volume_group_reference":null,"device_properties":{"type":"","disk_address":{"type":"","device_index":0,"adapter_type":"SCSI"},"device_type":"DISK"}}]},"availability_zone_reference":null,"backup_policy":null,"type":"","cluster_reference":{"kind":"cluster","type":"","name":"GROOT","uuid":"00058163-33b2-4359-0000-0000000167e4"},"categories":""},"variable_list":[]}],"credential_definition_list":[{"username":"nutanix","description":"","type":"KEY","secret":{"attrs":{"is_secret_modified":false,"secret_reference":{}}},"name":"Nutanix","cred_class":"static","passphrase":{"attrs":{"is_secret_modified":false,"secret_reference":{}}}},{"username":"kazi.ahmed@gso.lab","description":"","type":"PASSWORD","secret":{"attrs":{"is_secret_modified":false,"secret_reference":{}}},"name":"PC_Creds","cred_class":"static"},{"username":"nutanix","description":"","type":"PASSWORD","secret":{"attrs":{"is_secret_modified":false,"secret_reference":{}}},"name":"Centos 2 Credential","cred_class":"static"}],"package_definition_list":[{"description":"","action_list":[],"type":"DEB","service_local_reference_list":[{"kind":"app_service","name":"Centos"}],"name":"Package1","version":"","options":{"install_runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_package","name":"Package1"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Create New Affinity Policy"}],"name":"1b84d273_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Create New Affinity Policy","attrs":{"type":"CALL_RUNBOOK","inarg_list":[],"runbook_reference":{"kind":"app_runbook","name":"5f6ff0a8_runbook"}},"timeout_secs":"0","type":"CALL_RUNBOOK","variable_list":[]}],"description":"","name":"d58508cd_runbook","main_task_local_reference":{"kind":"app_task","name":"1b84d273_dag"},"variable_list":[]},"type":"","uninstall_runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_package","name":"Package1"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"c79f67cf_dag","attrs":{"edges":[],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]}],"description":"","name":"eefd2fa9_runbook","main_task_local_reference":{"kind":"app_task","name":"c79f67cf_dag"},"variable_list":[]}},"variable_list":[]}],"app_profile_list":[{"deployment_create_list":[{"type":"GREENFIELD","action_list":[],"name":"b1a5673a_deployment","min_replicas":"1","default_replicas":"1","depends_on_list":[],"published_service_local_reference_list":[],"max_replicas":"1","package_local_reference_list":[{"kind":"app_package","name":"Package1"}],"substrate_local_reference":{"kind":"app_substrate","name":"Centos_VM"},"variable_list":[],"description":""}],"environment_reference_list":[],"patch_list":[{"name":"Config1","type":"PATCH","is_system_generated":false,"variable_list":[],"attrs_list":[{"target_any_local_reference":{"kind":"app_blueprint_deployment","name":"b1a5673a_deployment"},"data":{"num_vcpus_per_socket_ruleset":{"type":""},"num_sockets_ruleset":{"type":""},"disk_delete_allowed":false,"pre_defined_nic_list":[{"nic_type":"NORMAL_NIC","vpc_reference":null,"ip_endpoint_list":[],"network_function_chain_reference":null,"editable":false,"network_function_nic_type":"INGRESS","mac_address":"","subnet_reference":{"kind":"subnet","type":"","name":"","uuid":"0c7a9cde-4b8b-422e-b457-382d8d8ded49"},"operation":"modify","identifier":"0","type":""}],"categories_delete_allowed":false,"pre_defined_categories":[],"pre_defined_disk_list":[{"device_properties":{"type":"","disk_address":{"type":"","device_index":0,"adapter_type":"SCSI"},"device_type":"DISK"},"operation":"modify","data_source_reference":{"kind":"image","type":"","name":"CentOS7.qcow2","uuid":"6e24104f-5b66-4953-b33f-f4e7a4e282b7"},"type":"","disk_size_mib":null,"volume_group_reference":null}],"nic_delete_allowed":false,"type":"nutanix","memory_size_mib_ruleset":{"type":""},"categories_add_allowed":false},"deployment_strategy":{"type":""},"uuid":"e7121f68-d2bf-ec12-c0ee-b824ce8fc4b7"}],"runbook":{"task_definition_list":[{"target_any_local_reference":{"kind":"app_profile","name":"Default"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Config1_meta_task"}],"name":"SYS_GEN__DAG_Patch_Config1_31435b00_99ba_e684_caeb_f3874f3ca128","attrs":{"edges":[],"type":"DAG"},"timeout_secs":"0","type":"DAG","variable_list":[]},{"target_any_local_reference":{"kind":"app_profile","name":"Default"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Config1_update_spec_task"},{"kind":"app_task","name":"Config1_Centos"}],"name":"Config1_meta_task","attrs":{"type":"","patch_attrs_reference":["e7121f68-d2bf-ec12-c0ee-b824ce8fc4b7"]},"timeout_secs":"0","type":"PATCH_META","variable_list":[]},{"target_any_local_reference":{"kind":"app_profile","name":"Default"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Config1_update_spec_task","attrs":{"type":""},"timeout_secs":"0","type":"UPDATE_SPEC","variable_list":[]},{"target_any_local_reference":{"kind":"app_blueprint_deployment","name":"b1a5673a_deployment"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"SYS_GEN__stop"},{"kind":"app_task","name":"SYS_GEN__Nutanix_Update_09959508_6766_4f9b_a239_ed18f2c36cc9"},{"kind":"app_task","name":"SYS_GEN__start"}],"name":"Config1_Centos","attrs":{"loop_variable":"","type":"","loop_counter":"","exit_condition_type":"","patch_attr_reference":"e7121f68-d2bf-ec12-c0ee-b824ce8fc4b7","loop_over_list":""},"timeout_secs":"0","type":"FOR_LOOP","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"SYS_GEN__stop","attrs":{"type":"CALL_RUNBOOK","inarg_list":[],"runbook_reference":{"kind":"app_runbook","name":"426ac198_runbook"}},"timeout_secs":"0","type":"CALL_RUNBOOK","variable_list":[]},{"target_any_local_reference":{"kind":"app_substrate","name":"Centos_VM"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"SYS_GEN__Nutanix_Update_09959508_6766_4f9b_a239_ed18f2c36cc9","attrs":{"type":""},"timeout_secs":"0","type":"UPDATE_NUTANIX","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"SYS_GEN__start","attrs":{"type":"CALL_RUNBOOK","inarg_list":[],"runbook_reference":{"kind":"app_runbook","name":"e2b19c60_runbook"}},"timeout_secs":"0","type":"CALL_RUNBOOK","variable_list":[]}],"description":"","name":"SYS_GEN__Runbook_Patch_Config1_31435b00_99ba_e684_caeb_f3874f3ca128","main_task_local_reference":{"kind":"app_task","name":"SYS_GEN__DAG_Patch_Config1_31435b00_99ba_e684_caeb_f3874f3ca128"},"variable_list":[]},"config_reference_list":[],"description":""}],"description":"","action_list":[{"description":"","type":"user","critical":false,"runbook":{"task_definition_list":[{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Check New Host List"},{"kind":"app_task","name":"Delete Affinity Policy"},{"kind":"app_task","name":"Replace New Affinity Policy"}],"name":"ee1671e0_dag","attrs":{"edges":[{"from_task_reference":{"kind":"app_task","name":"Delete Affinity Policy"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Replace New Affinity Policy"}},{"from_task_reference":{"kind":"app_task","name":"Check New Host List"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Delete Affinity Policy"}}],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Check New Host List","attrs":{"script":"print \"newhostnamelist={}\".format(\"@@{new_host_name_list}@@\")","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Delete Affinity Policy","attrs":{"type":"CALL_RUNBOOK","inarg_list":[],"runbook_reference":{"kind":"app_runbook","name":"ae1f02ed_runbook"}},"timeout_secs":"0","type":"CALL_RUNBOOK","variable_list":[]},{"target_any_local_reference":{"kind":"app_service","name":"Centos"},"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Replace New Affinity Policy","attrs":{"type":"CALL_RUNBOOK","inarg_list":[],"runbook_reference":{"kind":"app_runbook","name":"89d045aa_runbook"}},"timeout_secs":"0","type":"CALL_RUNBOOK","variable_list":[]}],"description":"","name":"4451529a_runbook","main_task_local_reference":{"kind":"app_task","name":"ee1671e0_dag"},"variable_list":[{"val_type":"STRING","is_mandatory":true,"description":"","data_type":"LIST","type":"EXEC_LOCAL","name":"new_host_name_list","value":"","label":"Select the new hosts for vm affinity","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"EXEC","attrs":{"script":"user = \"kazi.ahmed@gso.lab\"\npassword = \"nutanix\/4u\"\nip = \"@@{PC_IP}@@\"\n\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\npayload = {\n \"kind\": \"host\",\n \"sort_order\": \"ASCENDING\",\n \"offset\": 0,\n \"length\": 256,\n \"sort_attribute\": \"name\",\n \n \n}\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\n#print \"Status Code: {}\".format(r.status_code)\n#print \"{}\".format(r.text)\n\nhost_list = []\nhost_list_json = r.json()\nfor host in host_list_json['entities']:\n if host['status']['cluster_reference']['uuid'] == \"@@{cluster_uuid}@@\":\n # print host\n if \"name\" in host['spec']: # json may not return entities.spec.name for controller vm\n # print \"name exist\"\n host_list.append(\"{}\".format(host['spec']['name'].encode(\"utf-8\")))\n \n\nprint ','.join(host_list) \n","type":"EXEC","command_line_args":"","exit_status":[],"script_type":"static"}}}]},"name":"Change VM Host Affinity"}],"name":"Default","restore_config_list":[],"snapshot_config_list":[],"variable_list":[{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"PC_IP","value":"10.48.108.12","label":"","attrs":{"type":""},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":true,"description":"The new provisioned VM will be associated with this newly created category value","data_type":"BASE","type":"LOCAL","name":"vm_category_value","value":"Web","label":"Please key in the VM Category Value","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":true,"description":"The new provisioned VM will be associated with this newly created category","data_type":"BASE","type":"LOCAL","name":"vm_category_name","value":"AppTypeVM","label":"Please key in the VM Category","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":true,"description":"","data_type":"LIST","type":"EXEC_LOCAL","name":"host_name_list","value":"","label":"Select the host for VM affinity","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"EXEC","attrs":{"script":"user = \"kazi.ahmed@gso.lab\"\npassword = \"nutanix\/4u\"\nip = \"@@{PC_IP}@@\"\n\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\npayload = {\n \"kind\": \"host\",\n \"sort_order\": \"ASCENDING\",\n \"offset\": 0,\n \"length\": 256,\n \"sort_attribute\": \"name\",\n \n \n}\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\n#print \"Status Code: {}\".format(r.status_code)\n#print \"{}\".format(r.text)\n\nhost_list = []\nhost_list_json = r.json()\nfor host in host_list_json['entities']:\n if host['status']['cluster_reference']['uuid'] == \"@@{cluster_uuid}@@\":\n # print host\n if \"name\" in host['spec']: # json may not return entities.spec.name for controller vm\n # print \"name exist\"\n host_list.append(\"{}\".format(host['spec']['name']))\n else:\n continue\n\nprint ','.join(host_list) \n","type":"EXEC","command_line_args":"","exit_status":[],"script_type":"static"}}},{"val_type":"STRING","is_mandatory":true,"description":"","data_type":"BASE","type":"LOCAL","name":"host_category_value","value":"HostCategoryValue","label":"Please key in the host category value","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":true,"description":"A new category will be created and associated with the selected hosts. Please ensure the category is unique","data_type":"BASE","type":"LOCAL","name":"host_category_name","value":"HostsCategory","label":"Please key in the category name for the host","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"EXEC_LOCAL","name":"cluster_uuid","value":"","label":"","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"EXEC","attrs":{"script":"user = \"kazi.ahmed@gso.lab\"\npassword = \"nutanix\/4u\"\nip = \"@@{PC_IP}@@\"\n\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\npayload = {\n \"kind\": \"cluster\",\n \"filter\": \"name==@@{cluster_name}@@\"\n}\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/clusters\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\n#print r.status_code\n#print r.text\n\ncluster_found_uuid=''\ncluster_list_json = r.json()\nfor cluster in cluster_list_json['entities']:\n if cluster['spec']['name'].lower() == \"@@{cluster_name}@@\".lower():\n cluster_found_uuid=cluster['metadata']['uuid']\n break\n else:\n continue\n#print ','.join(cluster_list)\nprint cluster_found_uuid\n","type":"EXEC","command_line_args":"","exit_status":[],"script_type":"static"}}},{"val_type":"STRING","is_mandatory":true,"description":"","data_type":"BASE","type":"EXEC_LOCAL","name":"cluster_name","value":"","label":"Please select the cluster","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"EXEC","attrs":{"script":"user = \"kazi.ahmed@gso.lab\"\npassword = \"nutanix\/4u\"\nip = \"@@{PC_IP}@@\"\n\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\npayload = {\n \"kind\": \"cluster\",\n \"sort_order\": \"ASCENDING\",\n \"offset\": 0,\n \"length\": 256,\n \"sort_attribute\": \"name\"\n}\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/clusters\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\n\ncluster_list = []\ncluster_list_json = r.json()\nfor cluster in cluster_list_json['entities']:\n if cluster['spec'] and cluster['spec']['name'] != 'Unnamed': #sometimes this value will be '{}'\n cluster_list.append(\"{}\".format(cluster['spec']['name']))\n\nprint ','.join(cluster_list) \n","type":"EXEC","command_line_args":"","exit_status":[],"script_type":"static"}}},{"val_type":"STRING","is_mandatory":true,"description":"","data_type":"BASE","type":"LOCAL","name":"vm_affinity_policy","value":"","label":"Please key in the VM affinity policy","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}}]}],"published_service_definition_list":[],"default_credential_local_reference":{"kind":"app_credential","name":"Nutanix"},"type":"USER"},"name":"Host_Affinity_Update"},"api_version":"3.0","metadata":{"last_update_time":"1655138145534315","kind":"blueprint","spec_version":9,"creation_time":"1655076871812632","name":"Host_Affinity_Update"}} \ No newline at end of file diff --git a/host-affinity/auto-reassign-host/Auto Update VM Hosts.json b/host-affinity/auto-reassign-host/Auto Update VM Hosts.json new file mode 100644 index 0000000..db0c0a3 --- /dev/null +++ b/host-affinity/auto-reassign-host/Auto Update VM Hosts.json @@ -0,0 +1 @@ +{"status":{},"contains_secrets":false,"product_version":"3.5.0","spec":{"description":"","resources":{"endpoints_information":[],"endpoint_definition_list":[],"client_attrs":{},"credential_definition_list":[{"username":"kazi.ahmed@gso.lab","description":"","type":"PASSWORD","secret":{"attrs":{"is_secret_modified":false,"secret_reference":{}}},"name":"PC_Creds","cred_class":"static"}],"runbook":{"task_definition_list":[{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Got VM Info"},{"kind":"app_task","name":"Move Host Or Not"},{"kind":"app_task","name":"Proceed or Not"}],"name":"1f9712b8_dag","attrs":{"edges":[{"from_task_reference":{"kind":"app_task","name":"Got VM Info"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Move Host Or Not"}},{"from_task_reference":{"kind":"app_task","name":"Move Host Or Not"},"edge_type":"user_defined","type":"","to_task_reference":{"kind":"app_task","name":"Proceed or Not"}}],"type":""},"timeout_secs":"0","type":"DAG","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Got VM Info","attrs":{"script":"print \"VM name {}, UUID {} and OS Type {}\".format(\"@@{vm_name}@@\",\"@@{vm_uuid}@@\",\"@@{host_category_value}@@\") ","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Move Host Or Not","attrs":{"exit_status":[],"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\nvm_uuid=\"@@{vm_uuid}@@\"\n#build host type arrays\nlinuxHostsList = \"@@{targetLinuxHostValues}@@\".replace(\" \", \"\") \nlinuxHosts = linuxHostsList.split(\",\")\nmovingForward = False\nwinHostsList = \"@@{targetWindowsHostValues}@@\".replace(\" \", \"\") \nwinHosts = winHostsList.split(\",\")\n\npayload = {}\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vms\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"GET\"\nurl = base_url + \"\/{}\".format(vm_uuid)\n#print \"R2 URL: {}\".format(url)\nr2 = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"R2 Status Code: {}\".format(r2.status_code)\n#print \"R2 Output: {}\".format(r2.text)\nif r2.ok:\n vm_json = r2.json()\n currentHost = vm_json[\"status\"][\"resources\"][\"host_reference\"][\"name\"]\n if \"@@{host_category_value}@@\" == \"Linux\":\n if currentHost in linuxHosts:\n print \"VM is already in the right linux host\"\n else:\n movingForward = True\n print \"targetHostToMoveTo = {}\".format(linuxHosts[0])\n else:\n if currentHost in winHosts:\n print \"VM is already in the right windows host\"\n else:\n movingForward = True\n print \"targetHostToMoveTo = {}\".format(winHosts[0])\n print \"movingForward = {}\".format(movingForward) \n\nelse:\n print \"Unable to retrieve VM: {}\".format(\"@@{vm_name}@@\")\n print \"movingForward = {}\".format(movingForward)\n exit(1)","eval_variables":["targetHostToMoveTo","movingForward"],"eval_scope":"local","type":"","script_type":"static"},"timeout_secs":"0","type":"SET_VARIABLE","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Proceed or Not","attrs":{"failure_child_reference":{"kind":"app_task","name":"8c60025c_FAILURE_META"},"exit_status":[],"script":"movingForward = @@{movingForward}@@\n\nif movingForward == True:\n exit(0)\nelse:\n exit(1)","success_child_reference":{"kind":"app_task","name":"3f2e618e_SUCCESS_META"},"type":"","command_line_args":"","script_type":"static"},"timeout_secs":"0","type":"DECISION","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Create Categories"},{"kind":"app_task","name":"Apply Categories to Host"},{"kind":"app_task","name":"Apply Categories to VMs"},{"kind":"app_task","name":"Create Affinity Rules"}],"name":"3f2e618e_SUCCESS_META","attrs":{"type":""},"timeout_secs":"0","type":"META","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Create Categories","attrs":{"script":"vm_category_value=\"@@{targetHostToMoveTo}@@\"\n\ndef setup_category(category_name, category_value): \n\n pc_ip = '@@{PC_IP}@@'\n user = \"@@{PC_Creds.username}@@\"\n password = \"@@{PC_Creds.secret}@@\"\n #jwt = '@@{calm_jwt}@@'\n base_url = \"https:\/\/{}:9440\/api\/nutanix\/v3\".format(pc_ip)\n\n #headers = {'Content-Type': 'application\/json', 'Accept':'application\/json', 'Authorization': 'Bearer {}'.format(jwt)}\n headers = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\n\n #project_name = \"@@{project_name}@@\"\n \n\n ### --------------------------------------------------------------------------------- ###\n def create_category_name(base_url, headers, user, password, category_name):\n method = 'PUT'\n url = base_url + \"\/categories\/{}\".format(category_name)\n payload = { \"api_version\": \"3.1.0\", \"name\": category_name }\n print(\"Making a {} API call to {}\".format(method, url))\n resp = urlreq(\n url,\n verb=method,\n params=json.dumps(payload),\n headers=headers,\n verify=False,\n auth=\"BASIC\", \n user=user, \n passwd=password\n )\n\n if resp.ok:\n json_resp = json.loads(resp.content)\n return json_resp[\"name\"]\n else:\n print(\"Request failed\")\n print(\"Headers: {}\".format(headers))\n print('Status code: {}'.format(resp.status_code))\n print('Response: {}'.format(json.dumps(json.loads(resp.content), indent=4)))\n exit(1)\n ### --------------------------------------------------------------------------------- ### \n\n ### --------------------------------------------------------------------------------- ###\n def create_category_value(base_url, headers, user, password, category_name, category_value):\n method = 'PUT'\n url = base_url + \"\/categories\/{0}\/{1}\".format(category_name,category_value)\n payload = { \"api_version\": \"3.1.0\", \"value\": category_value }\n print(\"Making a {} API call to {}\".format(method, url))\n resp = urlreq(\n url,\n verb=method,\n params=json.dumps(payload),\n headers=headers,\n verify=False,\n auth=\"BASIC\", \n user=user, \n passwd=password\n )\n\n if resp.ok:\n json_resp = json.loads(resp.content)\n return json_resp[\"value\"]\n else:\n print(\"Request failed\")\n print(\"Headers: {}\".format(headers))\n print('Status code: {}'.format(resp.status_code))\n print('Response: {}'.format(json.dumps(json.loads(resp.content), indent=4)))\n exit(1)\n ### --------------------------------------------------------------------------------- ### \n\n ### --------------------------------------------------------------------------------- ### \n create_category_name(base_url, headers, user, password, category_name)\n create_category_value(base_url, headers, user, password, category_name, category_value)\n ### --------------------------------------------------------------------------------- ### \n\nsetup_category(\"@@{host_category_name}@@\",\"@@{host_category_value}@@\")\nsetup_category(\"@@{vm_category_name}@@\",vm_category_value)","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Apply Categories to Host","attrs":{"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\nhosts = \"@@{targetHostToMoveTo}@@\"\n\nif len(hosts): \n print \"selected hosts: {0}\".format(hosts)\nelse: \n print(\"hosts is empty\")\n exit(1)\n\nselected_hosts_name = hosts.split(\",\")\n#print selected_hosts_name\npayload = {\n \n}\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\n#print \"Retrieve Host List Status code: {}\".format(r.status_code)\n#print \"Output: {}\".format(r.text)\nhost_uuid_list = []\nif r.ok:\n host_json = r.json()\n for host in host_json['entities']:\n current_host_spec = host['spec']\n if 'name' in current_host_spec:\n current_host_name = host['spec']['name']\n # print \"Loop the Host Name: {}\".format(current_host_name)\n for selected_host in selected_hosts_name:\n # print \"for loop for selected_host: {}\".format(selected_host)\n if (current_host_name == selected_host):\n #print \"came here Selected Host Name: {}\".format(current_host_name)\n # print \"Host_uuid={}\".format(host['metadata']['uuid'])\n host_uuid_list.append(host['metadata']['uuid'])\n break\n else:\n continue\n else:\n # print \"host name is null\"\n continue\n \n \n print \"Selected_Hosts_UUID_List={}\".format(host_uuid_list)\nelse:\n exit(1)\n \npayload2 = {\n}\n\ncategories_mapping = {\n \"@@{host_category_name}@@\": [\n \"@@{host_category_value}@@\"\n ]\n}\ncategories = {\n \"@@{host_category_name}@@\": \"@@{host_category_value}@@\"\n}\nurl_method2 = \"GET\"\n\nfor selected_host in host_uuid_list:\n\n url2 = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/hosts\/\" + selected_host\n #print \"Associate host with category url: {}\".format(url2)\n r2 = process_request(url2, url_method2, user, password, headers, json.dumps(payload2))\n #print \"Retrieve Host Detail Status Code: {0}\".format(r2.status_code)\n #print \"Output: {0}\".format(r2.text)\n if r2.ok:\n selected_host_json = r2.json()\n del selected_host_json['status']\n del selected_host_json['metadata']['categories']\n del selected_host_json['metadata']['categories_mapping']\n selected_host_json['metadata']['categories_mapping'] = categories_mapping\n selected_host_json['metadata']['categories'] = categories\n print \"Put JSON: {0}\".format(selected_host_json)\n r3 = process_request(url2, \"PUT\", user, password, headers, json.dumps(selected_host_json))\n print \"Update Host with category status code: {0}\".format(r3.status_code)\n print \"Output: {0}\".format(r3.text)\n if r3.ok:\n print \"Host name: {0} updated with categories\".format(selected_host_json['spec']['name'])\n else:\n exit(1)\n else:\n exit(1)\n\n \n","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Apply Categories to VMs","attrs":{"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nvm_category_value= \"@@{targetHostToMoveTo}@@\"\nip = \"@@{PC_IP}@@\"\n\npayload = {\n \"filter\": \"vm_name==@@{vm_name}@@\"\n \n}\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vms\"\nurl = base_url + \"\/list\"\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Retrieve VM List Status code: {}\".format(r.status_code)\nprint \"Output: {}\".format(r.text)\nvm_uuid = \"empty\"\nif r.ok:\n vm_json = r.json()\n for vm in vm_json['entities']:\n current_vm_spec = vm['spec']\n if 'name' in current_vm_spec:\n if (vm['spec']['name'] == \"@@{vm_name}@@\"):\n vm_uuid = vm['metadata']['uuid']\n break\n else:\n continue\nelse:\n exit(1)\n \nprint \"vm_uuid={}\".format(vm_uuid)\nif vm_uuid==\"empty\":\n exit(1)\n \npayload2 = {\n}\n\nurl_method2 = \"GET\"\nurl2 = base_url + \"\/{}\".format(vm_uuid)\nprint \"url2: {}\".format(url2)\nr2 = process_request(url2, url_method2, user, password, headers, json.dumps(payload2))\nprint \"R2 Status Code: {}\".format(r2.status_code)\nprint \"R2 Output: {}\".format(r2.text)\nif r2.ok:\n vm_json = r2.json()\nelse:\n print \"Unable to retrieve VM: {}\".format(\"@@{vm_name}@@\")\n exit(1)\n\ncategories_mapping = {\n \"@@{vm_category_name}@@\": [\n vm_category_value\n ]\n}\ncategories = {\n \"@@{vm_category_name}@@\": vm_category_value\n}\n\ndel vm_json['status']\ndel vm_json['metadata']['categories']\ndel vm_json['metadata']['categories_mapping']\nvm_json['metadata']['categories_mapping'] = categories_mapping\nvm_json['metadata']['categories'] = categories\nprint \"Put JSON: {0}\".format(vm_json)\nurl3 = base_url + \"\/{}\".format(vm_uuid)\nr3 = process_request(url2, \"PUT\", user, password, headers, json.dumps(vm_json))\nprint \"Update VM with category status code: {0}\".format(r3.status_code)\nprint \"Output: {0}\".format(r3.text)\nif r3.ok:\n print \"VM name: {0} updated with categories\".format(\"@@{vm_name}@@\")\nelse:\n exit(1)\n","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Create Affinity Rules","attrs":{"script":"user = \"@@{PC_Creds.username}@@\"\npassword = \"@@{PC_Creds.secret}@@\"\nip = \"@@{PC_IP}@@\"\nvm_category_value = \"@@{targetHostToMoveTo}@@\"\n\npayload = {\n \"config\": {\n \"vm_categories\": {\n \"@@{vm_category_name}@@\": [\n vm_category_value\n ]\n },\n \"description\": \"VM affinity policy created using Nutanix Calm\",\n \"host_categories\": {\n \"@@{host_category_name}@@\": [\n \"@@{host_category_value}@@\"\n ]\n },\n \"name\": \"@@{vm_affinity_policy}@@\"\n }\n}\nprint \"Payload: {}\".format(json.dumps(payload))\n\ndef process_request(url, method, user, password, headers, payload=None):\n r = urlreq(url, verb=method, auth=\"BASIC\", user=user, passwd=password, params=payload, verify=False, headers=headers)\n return r\n\nbase_url = \"https:\/\/\" + ip + \":9440\/api\/nutanix\/v3\/vm_host_affinity_policies\"\nurl = base_url\nheaders = {'Accept': 'application\/json', 'Content-Type': 'application\/json'}\nurl_method = \"POST\"\n\nr = process_request(url, url_method, user, password, headers, json.dumps(payload))\nprint \"Status code: {}\".format(r.status_code)\nprint \"Output: {}\".format(r.text)\nif r.ok:\n print \"VM Affinity Policy created\"\nelse:\n print \"Error creating VM Affinity Policy\"\n exit(1)","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[{"kind":"app_task","name":"Do Nothing"}],"name":"8c60025c_FAILURE_META","attrs":{"type":""},"timeout_secs":"0","type":"META","variable_list":[]},{"retries":"0","description":"","inherit_target":false,"child_tasks_local_reference_list":[],"name":"Do Nothing","attrs":{"script":"print \"No further action would be taken\"","type":"","command_line_args":"","exit_status":[],"script_type":"static"},"timeout_secs":"0","type":"EXEC","variable_list":[]}],"description":"","name":"ff1d8469_runbook","main_task_local_reference":{"kind":"app_task","name":"1f9712b8_dag"},"variable_list":[{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"vm_category_value","value":"","label":"","attrs":{"type":""},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"vm_category_name","value":"HostedNode","label":"","attrs":{"type":""},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"host_category_name","value":"HostType","label":"","attrs":{"type":""},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"targetWindowsHostValues","value":"GROOT-C","label":"","attrs":{"type":"LOCAL"},"editables":{"value":false},"is_hidden":false,"options":{"type":"PREDEFINED","choices":["GROOT-C","GROOT-D"]}},{"regex":{"should_validate":false,"value":"^.*$"},"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"targetLinuxHostValues","value":"GROOT-A","label":"","attrs":{"type":"LOCAL"},"editables":{"value":false},"is_hidden":false,"options":{"type":"PREDEFINED","choices":["GROOT-A","GROOT-B"]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"PC_IP","value":"10.48.108.12","label":"","attrs":{"type":""},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"vm_name","value":"","label":"","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"vm_uuid","value":"","label":"","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}},{"val_type":"STRING","is_mandatory":false,"description":"","data_type":"BASE","type":"LOCAL","name":"host_category_value","value":"","label":"","attrs":{"type":""},"editables":{"value":true},"is_hidden":false,"options":{"type":"PREDEFINED","choices":[]}}]}},"name":"Auto Update VM Hosts"},"api_version":"3.0","metadata":{"last_update_time":"1655576809708934","kind":"runbook","spec_version":22,"creation_time":"1655255340594570","name":"Auto Update VM Hosts"}} \ No newline at end of file diff --git a/host-affinity/auto-reassign-host/images/playbook-structure.png b/host-affinity/auto-reassign-host/images/playbook-structure.png new file mode 100644 index 0000000..46f0272 Binary files /dev/null and b/host-affinity/auto-reassign-host/images/playbook-structure.png differ diff --git a/host-affinity/auto-reassign-host/images/runbook.png b/host-affinity/auto-reassign-host/images/runbook.png new file mode 100644 index 0000000..05966d5 Binary files /dev/null and b/host-affinity/auto-reassign-host/images/runbook.png differ diff --git a/host-affinity/auto-reassign-host/playbooks-Place VMs to right hosts.pbk b/host-affinity/auto-reassign-host/playbooks-Place VMs to right hosts.pbk new file mode 100644 index 0000000..cdd86e4 --- /dev/null +++ b/host-affinity/auto-reassign-host/playbooks-Place VMs to right hosts.pbk @@ -0,0 +1 @@ +{"pcVersion":"6.1","pcUuid":"edf58913-382f-4449-bcab-a6884fa4345a","hashValue":"QHZve0rdRdEp1NR6Pb2bg6voVuphfd6zHb1YLdpZzs8=","actionRuleList":[{"uuid":"75bc0cbc-de07-4a9d-a519-d6fd170916e0","name":"Place VMs to right hosts","isEnabled":true,"validated":true,"executionUserName":"kazi.ahmed@gso.lab","executionUserUuid":"b745c124-88a0-5ae4-9b65-8814659ab6e4","triggerList":[{"uuid":"7d12d74a-9e74-4565-a915-e9bf96f6ed28","triggerType":{"type":"trigger_type","uuid":"","name":"event_trigger"},"displayName":"Event","inputParameterList":[{"name":"operation_type","value":"2"},{"name":"category_filter_list","value":"[]"},{"name":"type","value":"VmUpdateAudit"},{"name":"source_entity_info_list","value":"[]"}]}],"actionList":[{"uuid":"e1ac6c02-42af-483c-b2e4-342a4efa9f5c","actionType":{"type":"action_type","uuid":"","name":"branch_action"},"displayName":"Branch","inputParameterList":[{"name":"values","value":"[\"{{trigger[0].source_entity_info.name}}\",\"^L-[a-zA-Z0-9]*$\",\"{{trigger[0].source_entity_info.name}}\",\"^W-[a-zA-Z0-9]*$\"]"},{"name":"condition","value":"[\"if\",\"if\"]"},{"name":"branch","value":"[\"1717b1ce-97d6-4b35-9c9a-13a829aad257\",\"15db89a9-8c46-4b4e-ac8a-d1fed37d0e9e\"]"},{"name":"conditional_expression","value":"[\"{0}=regexp={1}\",\"{2}=regexp={3}\"]"}],"maxRetries":2,"description":"@|","childActionUuids":["1717b1ce-97d6-4b35-9c9a-13a829aad257","15db89a9-8c46-4b4e-ac8a-d1fed37d0e9e"]},{"uuid":"1717b1ce-97d6-4b35-9c9a-13a829aad257","actionType":{"type":"action_type","uuid":"","name":"rest_api_action"},"displayName":"REST API","inputParameterList":[{"name":"username","value":"kazi.ahmed@gso.lab"},{"name":"request_body","value":"{\n\t\"spec\": {\n\t\t\"args\": [{\n\t\t\t\t\"val_type\": \"STRING\",\n\t\t\t\t\"is_mandatory\": true,\n\t\t\t\t\"description\": \"Enter the name of the approval item\",\n\t\t\t\t\"data_type\": \"BASE\",\n\t\t\t\t\"name\": \"host_category_value\",\n\t\t\t\t\"value\": \"Linux\"\n\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"val_type\": \"STRING\",\n\t\t\t\t\"is_mandatory\": true,\n\t\t\t\t\"description\": \"VM name to upgrade\",\n\t\t\t\t\"data_type\": \"BASE\",\n\t\t\t\t\"name\": \"vm_name\",\n\t\t\t\t\"value\": \"{{trigger[0].source_entity_info.name}}\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"val_type\": \"STRING\",\n\t\t\t\t\"is_mandatory\": true,\n\t\t\t\t\"description\": \"VM uuid to upgrade\",\n\t\t\t\t\"data_type\": \"BASE\",\n\t\t\t\t\"name\": \"vm_uuid\",\n\t\t\t\t\"value\": \"{{trigger[0].source_entity_info.uuid}}\"\n\t\t\t}\n\t\t]\n\t}\n}"},{"name":"url","value":"https://10.48.108.12:9440/api/nutanix/v3/runbooks/eb39f2a5-ab9b-7c94-56d6-ad94cd988908/run"},{"name":"headers","value":"Content-Type: application/json"},{"name":"password","value":""},{"name":"method","value":"POST"}],"maxRetries":2},{"uuid":"15db89a9-8c46-4b4e-ac8a-d1fed37d0e9e","actionType":{"type":"action_type","uuid":"","name":"rest_api_action"},"displayName":"REST API","inputParameterList":[{"name":"username","value":"kazi.ahmed@gso.lab"},{"name":"request_body","value":"{\n\t\"spec\": {\n\t\t\"args\": [{\n\t\t\t\t\"val_type\": \"STRING\",\n\t\t\t\t\"is_mandatory\": true,\n\t\t\t\t\"description\": \"Enter the name of the approval item\",\n\t\t\t\t\"data_type\": \"BASE\",\n\t\t\t\t\"name\": \"host_category_value\",\n\t\t\t\t\"value\": \"Windows\"\n\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"val_type\": \"STRING\",\n\t\t\t\t\"is_mandatory\": true,\n\t\t\t\t\"description\": \"VM name to upgrade\",\n\t\t\t\t\"data_type\": \"BASE\",\n\t\t\t\t\"name\": \"vm_name\",\n\t\t\t\t\"value\": \"{{trigger[0].source_entity_info.name}}\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"val_type\": \"STRING\",\n\t\t\t\t\"is_mandatory\": true,\n\t\t\t\t\"description\": \"VM uuid to upgrade\",\n\t\t\t\t\"data_type\": \"BASE\",\n\t\t\t\t\"name\": \"vm_uuid\",\n\t\t\t\t\"value\": \"{{trigger[0].source_entity_info.uuid}}\"\n\t\t\t}\n\t\t]\n\t}\n}"},{"name":"url","value":"https://10.48.108.12:9440/api/nutanix/v3/runbooks/eb39f2a5-ab9b-7c94-56d6-ad94cd988908/run"},{"name":"headers","value":"Content-Type: application/json"},{"name":"password","value":""},{"name":"method","value":"POST"}],"maxRetries":2,"description":"Copy 1"}],"lastUpdateUser":"kazi.ahmed@gso.lab","isPrepackaged":false,"checkTriggerValidity":true,"triggerFilterableInputParamName":"type","triggerFilterableInputParamValue":"VmUpdateAudit","ruleType":"kXPlay"}]} \ No newline at end of file diff --git a/host-affinity/auto-reassign-host/readme.md b/host-affinity/auto-reassign-host/readme.md new file mode 100644 index 0000000..a0cc98f --- /dev/null +++ b/host-affinity/auto-reassign-host/readme.md @@ -0,0 +1,13 @@ +# Automatically reassign a VM to a host on update + +This section talk about how VM could be automatically glued to certain host groups based on its naming convention. This example here has two main components to it one the Prism Pro playbook and another is a runbook. + +## Description of the components: + +![playbook](/host-affinity/auto-reassign-host/images/playbook-structure.png?raw=true) + + - Playbook: The above playbook (Auto Update VM Hosts.json) is hooked with a VM update event. As soon as a VM update happens, the workflow executes and it finds out if the VM is Linux or Windows type and passes that to the runbook in the following. + + - Runbook: The runbook first figures out whether the VM is already on the right host or not, if it is not it picks the first host from the configured host list for that type of OS. Rest of the tasks in the runbook creates both host and vm categories and finally creates an affinity rule bonding these two entities which will put the VM to the appropriate host. + +![runbook](/host-affinity/auto-reassign-host/images/runbook.png?raw=true) \ No newline at end of file diff --git a/host-affinity/blob/change-action-prompt.png b/host-affinity/blob/change-action-prompt.png new file mode 100644 index 0000000..db042d4 Binary files /dev/null and b/host-affinity/blob/change-action-prompt.png differ diff --git a/host-affinity/blob/change-vm-host-affinity-action.png b/host-affinity/blob/change-vm-host-affinity-action.png new file mode 100644 index 0000000..491f437 Binary files /dev/null and b/host-affinity/blob/change-vm-host-affinity-action.png differ diff --git a/host-affinity/blob/delete-existing-affinity.png b/host-affinity/blob/delete-existing-affinity.png new file mode 100644 index 0000000..663ed22 Binary files /dev/null and b/host-affinity/blob/delete-existing-affinity.png differ diff --git a/host-affinity/blob/set-new-affinity.png b/host-affinity/blob/set-new-affinity.png new file mode 100644 index 0000000..93c1fa5 Binary files /dev/null and b/host-affinity/blob/set-new-affinity.png differ diff --git a/host-affinity/readme.md b/host-affinity/readme.md new file mode 100644 index 0000000..3ed0373 --- /dev/null +++ b/host-affinity/readme.md @@ -0,0 +1,22 @@ +# Apply vm-host affinity policy during a VM deployment or update post deployment from CALM app. + +This section of the blueprints repo demonstrates how to better leverage host affinities for VM deployment and management from Calm. Initially we providing a sample blueprint (Host_Affinity_Update.json) that would deploy a new Centos +VM, also has a custom action built in order to change the VM host affinity to other hosts as day 2 operation. + +Here is the overview from the profile action: +![conceptual overview](/host-affinity/blob/change-vm-host-affinity-action.png?raw=true) + +## Breakdown of the node blueprint actions: + + Once the action is run, it will prompt following and ask for new desired hosts list: + ![conceptual overview](/host-affinity/blob/change-action-prompt.png?raw=true) + + The action has the following 3 main sections. First task collects the hostnames and publishes it for the successive service actions: + + - Delete Affinity Policy: This is a service action that comprised of few tasks that auto collects exiting host names, deletes the associated affinity, waits a little to sync and then deletes the host and category association that it originally created. +![conceptual overview](/host-affinity/blob/delete-existing-affinity.png?raw=true) + + - Replace New Affinity Policy: This service action does pretty much everything in reverse what the previous action did. It first reestablishes the category association with the new hosts. Then creates the same affinity with the VM category and new hosts category, waits a little to sync up, check for availability of newly created affinity policy, then finally republishes the host names for the sanity of the workflow and successive use. that comprised of few tasks that auto collects exiting host names, deletes the associated affinity, waits a little to sync and then deletes the host and category association that it originally created. +![conceptual overview](/host-affinity/blob/set-new-affinity.png?raw=true) + + This talks leverages AOS 6.1 api for [host affinities](https://portal.nutanix.com/page/documents/details?targetId=AHV-Admin-Guide-v6_1:ahv-affinity-policies.html) that would also require Calm 3.5+ and Prism Central PC2022.1.02+