From 57db17f6367b30d6575a0c1ef48c93c041a80213 Mon Sep 17 00:00:00 2001 From: "ankur.ankur" Date: Wed, 4 Jun 2025 12:05:33 +0530 Subject: [PATCH] Virtana Pack Added --- virtana_pack/actions/echo_alert.py | 6 + virtana_pack/actions/echo_alert.yaml | 12 ++ virtana_pack/actions/restart_vm.py | 55 +++++ virtana_pack/actions/restart_vm.yaml | 18 ++ virtana_pack/ansible/restart_vm.yml | 97 +++++++++ virtana_pack/icon.png | Bin 0 -> 2992 bytes virtana_pack/pack.yaml | 13 ++ virtana_pack/requirements.txt | 4 + .../sensors/action_execution_polling.py | 46 +++++ .../sensors/action_execution_polling.yaml | 5 + virtana_pack/sensors/action_status_polling.py | 46 +++++ .../sensors/action_status_polling.yaml | 5 + virtana_pack/sensors/lib/__init__.py | 0 virtana_pack/sensors/lib/action_executor.py | 193 ++++++++++++++++++ .../sensors/lib/action_status_notifier.py | 125 ++++++++++++ virtana_pack/sensors/lib/auth_helper.py | 34 +++ .../sensors/lib/create_action_definitions.py | 91 +++++++++ virtana_pack/sensors/lib/disable_sensor.py | 39 ++++ virtana_pack/sensors/lib/list_actions.py | 133 ++++++++++++ virtana_pack/sensors/lib/mongo_helper.py | 111 ++++++++++ .../lib/update_and_cleanup_executions.py | 46 +++++ virtana_pack/sensors/setup_sensor.py | 56 +++++ virtana_pack/sensors/setup_sensor.yaml | 5 + 23 files changed, 1140 insertions(+) create mode 100644 virtana_pack/actions/echo_alert.py create mode 100644 virtana_pack/actions/echo_alert.yaml create mode 100644 virtana_pack/actions/restart_vm.py create mode 100644 virtana_pack/actions/restart_vm.yaml create mode 100644 virtana_pack/ansible/restart_vm.yml create mode 100644 virtana_pack/icon.png create mode 100755 virtana_pack/pack.yaml create mode 100755 virtana_pack/requirements.txt create mode 100644 virtana_pack/sensors/action_execution_polling.py create mode 100644 virtana_pack/sensors/action_execution_polling.yaml create mode 100644 virtana_pack/sensors/action_status_polling.py create mode 100644 virtana_pack/sensors/action_status_polling.yaml create mode 100644 virtana_pack/sensors/lib/__init__.py create mode 100644 virtana_pack/sensors/lib/action_executor.py create mode 100644 virtana_pack/sensors/lib/action_status_notifier.py create mode 100644 virtana_pack/sensors/lib/auth_helper.py create mode 100644 virtana_pack/sensors/lib/create_action_definitions.py create mode 100644 virtana_pack/sensors/lib/disable_sensor.py create mode 100644 virtana_pack/sensors/lib/list_actions.py create mode 100644 virtana_pack/sensors/lib/mongo_helper.py create mode 100644 virtana_pack/sensors/lib/update_and_cleanup_executions.py create mode 100644 virtana_pack/sensors/setup_sensor.py create mode 100644 virtana_pack/sensors/setup_sensor.yaml diff --git a/virtana_pack/actions/echo_alert.py b/virtana_pack/actions/echo_alert.py new file mode 100644 index 00000000..6abee6c1 --- /dev/null +++ b/virtana_pack/actions/echo_alert.py @@ -0,0 +1,6 @@ +from st2common.runners.base_action import Action + +class EchoAlertAction(Action): + def run(self, text): + self.logger.info(f"Echo alert received: {text}") + return text diff --git a/virtana_pack/actions/echo_alert.yaml b/virtana_pack/actions/echo_alert.yaml new file mode 100644 index 00000000..a98b11db --- /dev/null +++ b/virtana_pack/actions/echo_alert.yaml @@ -0,0 +1,12 @@ +name: "echo_alert" +description: "Echoes the input text" +runner_type: "python-script" +entry_point: "echo_alert.py" +enabled: true +parameters: + text: + type: "string" + required: true + description: "Text message to echo" +tags: + - name: "dev" \ No newline at end of file diff --git a/virtana_pack/actions/restart_vm.py b/virtana_pack/actions/restart_vm.py new file mode 100644 index 00000000..54701863 --- /dev/null +++ b/virtana_pack/actions/restart_vm.py @@ -0,0 +1,55 @@ +import ansible_runner +from st2common.runners.base_action import Action + + +class RestartVMAction(Action): + def run(self, vcenter_hostname, vm_name): + try: + # Validate both parameters are provided as strings + if not vcenter_hostname or not isinstance(vcenter_hostname, str): + raise ValueError("vcenter_hostname must be a non-empty string.") + + if not vm_name or not isinstance(vm_name, str): + raise ValueError("vm_name must be a non-empty string.") + + # Retrieve credentials from stackstorm datastore + vcenter_username = self.action_service.get_value('vcenter.username', decrypt=True, local=False) + vcenter_password = self.action_service.get_value('vcenter.password', decrypt=True, local=False) + + if not vcenter_username or not vcenter_password: + raise Exception("vCenter credentials are missing in the datastore.") + + # Prepare the extra variables for the Ansible playbook + extra_vars = { + "vcenter_hostname": vcenter_hostname, + "vcenter_username": vcenter_username, + "vcenter_password": vcenter_password, + "vm_name": vm_name, + "validate_certs": False + } + + # Path to the Ansible playbook + playbook_path = '/opt/stackstorm/packs/virtana_pack/ansible/restart_vm.yml' + + # Run the Ansible playbook using the provided credentials and parameters + runner = ansible_runner.run( + private_data_dir='.', + playbook=playbook_path, + extravars=extra_vars + ) + + # Check the result of the playbook execution + if runner.status == "failed": + raise Exception(f"Ansible playbook failed with status: {runner.status}") + + # Return the result of the operation + return { + "status": "VM restarted successfully", + "vm_name": vm_name, + "vcenter": vcenter_hostname, + "ansible_result": runner.stats + } + + except Exception as e: + # Raise exception + raise diff --git a/virtana_pack/actions/restart_vm.yaml b/virtana_pack/actions/restart_vm.yaml new file mode 100644 index 00000000..1e94cb55 --- /dev/null +++ b/virtana_pack/actions/restart_vm.yaml @@ -0,0 +1,18 @@ +name: "restart_vm" +runner_type: "python-script" +description: "Restart a VM in vSphere based on vcenter_hostname and vm_name inputs" +enabled: true +entry_point: "restart_vm.py" + +parameters: + vcenter_hostname: + type: "string" + required: true + description: "vCenter hostname (e.g., vcenter.example.com)" + + vm_name: + type: "string" + required: true + description: "Name of the VM to restart" +tags: + - name: "dev" \ No newline at end of file diff --git a/virtana_pack/ansible/restart_vm.yml b/virtana_pack/ansible/restart_vm.yml new file mode 100644 index 00000000..71364835 --- /dev/null +++ b/virtana_pack/ansible/restart_vm.yml @@ -0,0 +1,97 @@ +- name: Restart VM + hosts: localhost + gather_facts: no + collections: + - community.vmware + + tasks: + + # Authenticate with vCenter using REST API + - name: Authenticate with vCenter + uri: + url: "https://{{ vcenter_hostname }}/rest/com/vmware/cis/session" + method: POST + user: "{{ vcenter_username }}" + password: "{{ vcenter_password }}" + force_basic_auth: yes + validate_certs: "{{ validate_certs }}" + status_code: 200 + return_content: yes + register: login_response + no_log: true + + # Save the session token returned from authentication + - name: Save session token + set_fact: + session_token: "{{ login_response.json.value }}" + + # Fetch list of all VMs from vCenter + - name: Get list of VMs + uri: + url: "https://{{ vcenter_hostname }}/rest/vcenter/vm" + method: GET + headers: + vmware-api-session-id: "{{ session_token }}" + validate_certs: "{{ validate_certs }}" + status_code: 200 + register: vm_list + no_log: true + + # Identify VM ID by matching VM name + - name: Find VM ID by name + set_fact: + vm_id: "{{ item.vm }}" + loop: "{{ vm_list.json.value }}" + when: item.name == vm_name + + # Fail the play if VM not found + - name: Fail if VM not found + fail: + msg: "VM not found." + when: vm_id is not defined + + # Power off the VM + - name: Stop VM + uri: + url: "https://{{ vcenter_hostname }}/rest/vcenter/vm/{{ vm_id }}/power/stop" + method: POST + headers: + vmware-api-session-id: "{{ session_token }}" + validate_certs: "{{ validate_certs }}" + status_code: 200 + timeout: 60 + register: stop_result + + # Wait until the VM is fully powered off + - name: Wait for power off + uri: + url: "https://{{ vcenter_hostname }}/rest/vcenter/vm/{{ vm_id }}/power" + method: GET + headers: + vmware-api-session-id: "{{ session_token }}" + validate_certs: "{{ validate_certs }}" + register: power_status + until: power_status.json.value.state == "POWERED_OFF" + retries: 10 + delay: 10 + no_log: true + + # Power on the VM again + - name: Start VM + uri: + url: "https://{{ vcenter_hostname }}/rest/vcenter/vm/{{ vm_id }}/power/start" + method: POST + headers: + vmware-api-session-id: "{{ session_token }}" + validate_certs: "{{ validate_certs }}" + status_code: 200 + timeout: 60 + register: start_result + retries: 5 + delay: 10 + until: start_result.status == 200 + + # Output final message on success + - name: Print result + debug: + msg: "VM restarted successfully." diff --git a/virtana_pack/icon.png b/virtana_pack/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d473f655d017efb1ef4423cc30127296b0c602a2 GIT binary patch literal 2992 zcmV;h3s3ZkP);3uuc41eJ0t$ix7OH}R@qj2T(=>^bR8%k?)0y1$w$tfFFM88Ip*QLD zs?+Ic1Vun2aS~&uRs|6S1cWGZK6v0HAh7%UeZQYq`7N%B?5?|6?tYJl;<{g&78bO) zpv46(E@*Klj)x@qK7gAGhr?WjOkCo0E7?%GLcOdm?qI=2{4c$f+pL5F#$HcWO0!I z@omCb8IzGk|4d-a1r9*;M;OR}8A!qRxTfl0f;*{XGUhxb`_mWna7HB3sPVLsXv#8W z*Ey)4BXd#5$Qo0pjK!F~gcHY(pW*naa}1=dCZ zc65>mzRQNESH8hN{PTa23HlQu{`kVbFk$={M1c5;xrE`f6Q`JU&p%P`1|#|t3_w); zzP5+sZD(mKgUE@=U03l#&jJJvtP1vQ!-%YctQwv+6VZ*C95{581BX86;Nj2s@@yBACX5C|Q`nTwy!6TzO#cc7 zV4$AhnU3xJ{kb~;fniae$&_j|`_f5_psb^VJJHNLu}UgI?S`As-p$Ac3SNEV1CAa$ z&BO^~xLiV1JoV5*o_^sSph7ueWiDklZ9l?~pZIq!G9bWAo!ri!-ue|O(Zj&UyN_TC zWcm{fKtRf3eX*C1+rOa6f{sSPlIj*9(JS_CK^PfCP)(W+o#TN_R!LVKo?p8WA^Kt^ z{%YY|uAkfq!3E}W0t?810Rcv2rcSzsB)(Q=SiX8amKjJe03$M)^HH&?eLGc{ERmd9 zTA$B|vLcxag2u7WNnajE0TrSV1;w=4ID#-2m=&wu#27GOdKr+R)|OcIzyjoqCHi8( z2m{6#sFZko*_|NKYu&~j>^*P_6AUEy9s&$J-?oKD1W*Bq1zqErT}{OVK}NYwKC~Tj zU@pqj=Tq2Z2_fFwx{n>Zjvz99GfymCh#ZK_;D`~0S^B_z7}I26#hSM<38`Wr!60N{ zM>@*;Z3huSO1U>B(>LBg1-V+gPB_sQ0Z%--!GL^#uk4s^rK$Q3=DSiX8488CebYBl)zlDoK+ z2#d#--HpgJSru<@{E$5dP6HVbgB1*!kb_mVEp*pPdd+K0W==JcDnuA^HXPWBF^*MA@L9cx3 zGvfFmvII8W8c9r>2R2z?#0xKNpd`~D+x%kb0z^g#%RIGoAz+$h@$&y}=HwS$n4t@X z$UxXMBC)1z2j@~Z7=)Qxj^e(?41%BlM8^Af63z$%8DBper2-LHC3_E?Wc|k7;FA9Q ze965ODS*uQaqax{hx3pVV47B}d>aU?lA#Lz43z+94yQA`-o6_v0j8}gc(gu;WMhOJ z2KOUy;p$kC)pGYBd`py1c5UPqYzOu2R(i|?HcN<8$FJLu@Bks~9-=f_X;+8ZAL z896aj!7yatxsh8z#u8cPXZ4#H*J#5IvMStlhT4%me8YIge2f5r#p>U_%ei`q=vyG3 zeB>T*$uE{JB)f(TxWNm*SQD|zWN@r=}RV9djH+r`j=O;@b1}!jb$>46)&yFPE^Xs zVyJ>)u!B>@dDgb?q*pRLTECsbU@kD^L;F$vBq%{tG-{(s*UjV`;@Q<3$jbC*7GxhPFPfk*t-K%2N?pYF%wDSCSr-E z?BMw|8_Cf?h#(VWf=rN+Ojw-j?qc=YcOlmpu7WG1JHda9T+dORMK%U%g^1in6(vG+ zpZ}EhkG7CIN09|7Cp=~pIw366v|`Oh?z&?(vH@`^gW$cbdpUl(0s=EUitB!9u8;s^ zz@mpt7MO350MWyI#enIdOp^>k^d}>(gn}!m5=cfiO_so1LN<_TGQGst95fkWE+s(b zBAF|p;0g+Z5#}Np%%97E`8IQb_=>m^3a%h?DRVi~BnC%(m$|Zn78kU*pv46(E@*K< miwjy@(Bgs?7qqyb#r;1lPh%!IGRfQk0000