Repository: ansible-collections/community.vmware

Issue:
community.vmware.vmware_guest_powerstate not finding VM by name
##### SUMMARY
When trying to control powerstate of a VM by name the module is unable to find the VM. This despite the fact that the exact same parameters will find the VM in other modules (such as vmware_guest_snapshot).

##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
vmware_guest_powerstate

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```ansible [core 2.12.2]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.8/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.8.12 (default, Sep 21 2021, 00:10:52) [GCC 8.5.0 20210514 (Red Hat 8.5.0-3)]
  jinja version = 2.10.3
```

##### COLLECTION VERSION
<!--- Paste verbatim output from "ansible-galaxy collection list <namespace>.<collection>"  between the quotes
for example: ansible-galaxy collection list community.general
-->
```# /root/.ansible/collections/ansible_collections
Collection       Version
---------------- -------
community.vmware 2.1.0
[root@jumpserver snaprevert_test]# ansible-galaxy collection list community.vmware
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```[root@jumpserver snaprevert_test]# ansible-config dump --only-changed
[root@jumpserver snaprevert_test]#

```

##### OS / ENVIRONMENT
```NAME="CentOS Stream"
VERSION="8"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="8"
PLATFORM_ID="platform:el8"
PRETTY_NAME="CentOS Stream 8"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:8"
HOME_URL="https://centos.org/"
BUG_REPORT_URL="https://bugzilla.redhat.com/"
REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux 8"
REDHAT_SUPPORT_PRODUCT_VERSION="CentOS Stream"```


##### STEPS TO REPRODUCE
Running the playbook below you'll find that the  vmware_guest_snapshot task will find the VM and perform action while the vmware_guest_powerstate will fail with "Unable to set power state for non-existing virtual machine" despite all parameters being identical.


```---
- name: Test of snapshot revert
  hosts: localhost
  gather_facts: no
  vars:
    vcenter_hostname: 1.2.3.4
    vcenter_username: administrator@vsphere.local
    vcenter_password: FOO
    datacenter_name: BAR
  tasks:
  - name: Revert to initial snapshot
    community.vmware.vmware_guest_snapshot:
      validate_certs: no
      hostname: "{{ vcenter_hostname }}"
      username: "{{ vcenter_username }}"
      password: "{{ vcenter_password }}"
      datacenter: "{{ datacenter_name }}"
      folder: "/{{ datacenter_name }}/vm/Jumpserver_VMs/"
      name: "jump_7216"
      state: revert
      snapshot_name: "Initial_Setup"
    delegate_to: localhost

  - name: Power on machine
    community.vmware.vmware_guest_powerstate:
      validate_certs: no
      hostname: "{{ vcenter_hostname }}"
      username: "{{ vcenter_username }}"
      password: "{{ vcenter_password }}"
      datacenter: "{{ datacenter_name }}"
      folder: "/{{ datacenter_name }}/vm/Jumpserver_VMs/"
      name: "jump_7216"
      state: powered-on
    delegate_to: localhost
```

##### EXPECTED RESULTS
I would expect vmware_guest_powerstate to find the VM just like vmware_guest_snapshot does. 


##### ACTUAL RESULTS
Task fails with "non-existing virtual machine" error despite VM existing.

<!--- Paste verbatim command output between quotes -->
```PLAY [Test of snapshot revert] ********************************************************************************************************************************************************************************************************************************************************************************************************************************************

TASK [Revert to a snapshot] ***********************************************************************************************************************************************************************************************************************************************************************************************************************************************
changed: [localhost]

TASK [Power on machine] ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Unable to set power state for non-existing virtual machine : 'jump_7216'"}

PLAY RECAP ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```



Discussion:
@danthem - I tried to power off and power on a VM using vmware_guest_powerstate and I did not notice any issue. 

Can you please add some pause/delay after the task "Revert to initial snapshot" ?
Hi @tejaswi-bachu , 
Thank you for looking in to this. The 'Revert to Initial snapshot' task is irrelevant, I face this issue regardless if that task is present or not. The problem I'm experiencing is that the '_name: "whatever"_' does not seem to be able to match a VM. The reason I included the 'Revert to Initial snapshot' task was to show that the exact same name pattern works in that task but not when using vmware_guest_powerstate.

Here is me running a playbook with only the 'Power on machine' task with -vvvv flags:
```[root@jumpserver-old snaprevert_test]# ansible-playbook -vvvv issuetest.yml --vault-password-file=~/.vault_secret
ansible-playbook [core 2.12.3]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.8/site-packages/ansible
  ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible-playbook
  python version = 3.8.12 (default, Sep 21 2021, 00:10:52) [GCC 8.5.0 20210514 (Red Hat 8.5.0-3)]
  jinja version = 2.10.3
  libyaml = True
Using /etc/ansible/ansible.cfg as config file
setting up inventory plugins
host_list declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
script declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
auto declined parsing /etc/ansible/hosts as it did not pass its verify_file() method
Parsed /etc/ansible/hosts inventory source with ini plugin
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
Loading collection community.vmware from /root/.ansible/collections/ansible_collections/community/vmware
Loading callback plugin default of type stdout, v2.0 from /usr/lib/python3.8/site-packages/ansible/plugins/callback/default.py
Skipping callback 'default', as we already have a stdout callback.
Skipping callback 'minimal', as we already have a stdout callback.
Skipping callback 'oneline', as we already have a stdout callback.

PLAYBOOK: issuetest.yml ***************************************************************************************************************************************************************************************************************************************************************************************************************************************************
Positional arguments: issuetest.yml
verbosity: 4
connection: smart
timeout: 10
become_method: sudo
tags: ('all',)
inventory: ('/etc/ansible/hosts',)
vault_password_files: ('/root/.vault_secret',)
forks: 5
1 plays in issuetest.yml
Trying secret FileVaultSecret(filename='/root/.vault_secret') for vault_id=default
Read vars_file 'vars/secrets.enc'
Trying secret FileVaultSecret(filename='/root/.vault_secret') for vault_id=default
Read vars_file 'vars/secrets.enc'
Trying secret FileVaultSecret(filename='/root/.vault_secret') for vault_id=default
Read vars_file 'vars/secrets.enc'

PLAY [get folder] *********************************************************************************************************************************************************************************************************************************************************************************************************************************************************
Trying secret FileVaultSecret(filename='/root/.vault_secret') for vault_id=default
Read vars_file 'vars/secrets.enc'
META: ran handlers
Trying secret FileVaultSecret(filename='/root/.vault_secret') for vault_id=default
Read vars_file 'vars/secrets.enc'
Trying secret FileVaultSecret(filename='/root/.vault_secret') for vault_id=default
Read vars_file 'vars/secrets.enc'

TASK [Power on machine] ***************************************************************************************************************************************************************************************************************************************************************************************************************************************************
task path: /root/ansible/snaprevert_test/issuetest.yml:10
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: root
<localhost> EXEC /bin/sh -c 'echo ~root && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1649178101.6537755-351047-37329199885717 `" && echo ansible-tmp-1649178101.6537755-351047-37329199885717="` echo /root/.ansible/tmp/ansible-tmp-1649178101.6537755-351047-37329199885717 `" ) && sleep 0'
Using module file /root/.ansible/collections/ansible_collections/community/vmware/plugins/modules/vmware_guest_powerstate.py
<localhost> PUT /root/.ansible/tmp/ansible-local-351042m2rcs4p4/tmpeeldimim TO /root/.ansible/tmp/ansible-tmp-1649178101.6537755-351047-37329199885717/AnsiballZ_vmware_guest_powerstate.py
<localhost> EXEC /bin/sh -c 'chmod u+x /root/.ansible/tmp/ansible-tmp-1649178101.6537755-351047-37329199885717/ /root/.ansible/tmp/ansible-tmp-1649178101.6537755-351047-37329199885717/AnsiballZ_vmware_guest_powerstate.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python3.8 /root/.ansible/tmp/ansible-tmp-1649178101.6537755-351047-37329199885717/AnsiballZ_vmware_guest_powerstate.py && sleep 0'
<localhost> EXEC /bin/sh -c 'rm -f -r /root/.ansible/tmp/ansible-tmp-1649178101.6537755-351047-37329199885717/ > /dev/null 2>&1 && sleep 0'
fatal: [localhost]: FAILED! => {
    "changed": false,
    "invocation": {
        "module_args": {
            "answer": null,
            "datacenter": "FOO",
            "folder": "/BAR/vm/Jumpserver_VMs/",
            "force": false,
            "hostname": "10.60.34.206",
            "moid": null,
            "name": "jump_7216",
            "name_match": "first",
            "password": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "port": 443,
            "proxy_host": null,
            "proxy_port": null,
            "schedule_task_description": null,
            "schedule_task_enabled": true,
            "schedule_task_name": null,
            "scheduled_at": null,
            "state": "powered-on",
            "state_change_timeout": 0,
            "use_instance_uuid": false,
            "username": "administrator@vsphere.local",
            "uuid": null,
            "validate_certs": false
        }
    },
    "msg": "Unable to set power state for non-existing virtual machine : 'jump_7216'"
}

PLAY RECAP ****************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
localhost                  : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```


Note how it can't find my "jump_7216" machine, claiming it does not exist. But I am sure this machine exists in the path specified, also other modules such as "community.vmware.vmware_guest_snapshot" finds the machine with the exact same parameters. 

As a workaround I've reverted to using UUIDs with "vmware_guest_powerstate", this works fine but it would be better for me to use 'name' for matching possibly hundreds of VMs easily. I would expect that if 'name' works for other modules it should also work for vmware_guest_powerstate.

  
I'm not 100% sure what's happening here, but I might have an idea. Could you please try `folder: "/{{ datacenter_name }}/vm/Jumpserver_VMs"`, that is without a trailing slash? This would help me a lot.

BTW: If this works, just let us know but **don't close this issue**. I would consider this a work-around, but still a bug. Or at least a feature request that `vmware_guest_powerstate` and `vmware_guest_snapshot` should interpret `folder` the same way.

@tejaswi-bachu FYI: I think the problem is that `vmware_guest_snapshot` removes the trailing slash:

https://github.com/ansible-collections/community.vmware/blob/fd46931c7f38effa7d0a28844a1e94ea395374ff/plugins/modules/vmware_guest_snapshot.py#L437-L440

But `vmware_guest_powerstate`  doesn't, as far as I can see. Well, let's wait what @danthem says. If I'm right and my suggestion helps, I know what to do.

> As a workaround I've reverted to using UUIDs with "vmware_guest_powerstate", this works fine but it would be better for me to use 'name' for matching possibly hundreds of VMs easily.

Personally, I _hate_ using UUIDs. It's not infrastructure as code at all. The idea of infrastructure as code is that you can crash everything, and deploy it again. But the VMs would have a new UUID, so you'll run into problems. Same thing with MOIDs. I think we shouldn't use them, _anywhere_. They are internal IDs and shouldn't be used when doing IAC imho.
Thank you @mariolenz , when removing the trailing slash the machine is found correctly:
```[root@jumpserver-old snaprevert_test]# ansible-playbook issuetest.yml --vault-password-file=~/.vault_secret
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [get folder] ****************************************************************************************************************

TASK [Power on machine] **********************************************************************************************************
ok: [localhost]

PLAY RECAP ***********************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

[root@jumpserver-old snaprevert_test]#
```


So I agree, it does look like it's working in vmware_guest_snapshot due to it removing the trailing slash.. Interesting and an easy fix :-) 


Thanks for testing @danthem, this helps a lot!

Resolving patch:
diff --git a/plugins/modules/vmware_guest_powerstate.py b/plugins/modules/vmware_guest_powerstate.py
--- a/plugins/modules/vmware_guest_powerstate.py
+++ b/plugins/modules/vmware_guest_powerstate.py
@@ -261,6 +261,9 @@ def main():
 
     result = dict(changed=False,)
 
+    if module.params['folder']:
+        module.params['folder'] = module.params['folder'].rstrip('/')
+
     pyv = PyVmomi(module)
 
     # Check if the VM exists before continuing


Test patch:
diff --git a/tests/integration/targets/vmware_guest_powerstate/tasks/main.yml b/tests/integration/targets/vmware_guest_powerstate/tasks/main.yml
--- a/tests/integration/targets/vmware_guest_powerstate/tasks/main.yml
+++ b/tests/integration/targets/vmware_guest_powerstate/tasks/main.yml
@@ -40,7 +40,7 @@
     username: "{{ vcenter_username }}"
     password: "{{ vcenter_password }}"
     name: test_vm1
-    folder: '{{ f0 }}'
+    folder: '{{ f0 }}/' # Test with a trailing / because of issue 1238
     state: powered-off
   register: poweroff_d1_c1_f0