We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
community.vmware.vmware_cfg_backup: Failed to write backup file
<!--- Verify first that your issue is not already reported on GitHub -->
<!--- Also test if the latest release and devel branch are affected too -->
<!--- Complete *all* sections as described, this form is processed automatically -->

##### SUMMARY
When I run the ansible playbook it's return "Failed to write backup file" error.

```
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] *************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [ESXI backup test] ******************************************************************************************************************************************************************************************************************
fatal: [localhost -> localhost]: FAILED! => {"changed": false, "msg": "Failed to write backup file. Ensure that the dest path exists and is writable. Details : <urlopen error [Errno 111] Connection refused>"}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0
```


##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->
`community.vmware.vmware_cfg_backup`

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.9.6
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['/home/sergen/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.8.10 (default, Nov 26 2021, 20:14:08) [GCC 9.3.0]

```

##### COLLECTION VERSION
<!--- Paste verbatim output from "ansible-galaxy collection list <namespace>.<collection>"  between the quotes
for example: ansible-galaxy collection list community.general
-->
```paste below
ansible-galaxy collection list community.vmware
usage: ansible-galaxy collection [-h] COLLECTION_ACTION ...
ansible-galaxy collection: error: argument COLLECTION_ACTION: invalid choice: 'list' (choose from 'init', 'build', 'publish', 'install')

```
List command is not working but I install collection with this command

`ansible-galaxy collection install community.vmware`

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below

```
no output

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
Ubuntu 20.04
VMware ESXI 7.0.3

##YAML FILE

returns the same result in both files 

```
- hosts: localhost
  vars:
    esxi_hostname: "192.168.88.154"
    esxi_username: "root"
    esxi_password: "password"
  tasks:
  - name: ESXI backup test
    local_action:
      module: vmware_cfg_backup
      hostname: '{{esxi_hostname}}'
      username: '{{esxi_username}}'
      password: '{{esxi_password}}'
      state: saved
      dest: /tmp/
      validate_certs: no
```
```

cat vmware.yaml
- hosts: localhost
  vars:
    esxi_hostname: "192.168.88.154"
    esxi_username: "root"
    esxi_password: "password"
  tasks:
  - name: Save
    community.vmware.vmware_cfg_backup:
      hostname: '{{ esxi_hostname }}'
      username: '{{ esxi_username }}'
      password: '{{ esxi_password }}'
      state: saved
      dest: /tmp/
      validate_certs: no
    delegate_to: localhost

```


##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml

```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->


##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes -->
```
ansible-playbook vmware.yaml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [localhost] *************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************
ok: [localhost]

TASK [Save] ******************************************************************************************************************************************************************************************************************************
fatal: [localhost -> localhost]: FAILED! => {"changed": false, "msg": "Failed to write backup file. Ensure that the dest path exists and is writable. Details : <urlopen error [Errno 111] Connection refused>"}

PLAY RECAP *******************************************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0


```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `plugins/modules/vmware_cfg_backup.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 
4 # Copyright: (c) 2017, IBM Corp
5 # Author(s): Andreas Nafpliotis <nafpliot@de.ibm.com>
6 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
7 
8 from __future__ import absolute_import, division, print_function
9 __metaclass__ = type
10 
11 
12 DOCUMENTATION = r'''
13 ---
14 module: vmware_cfg_backup
15 short_description: Backup / Restore / Reset ESXi host configuration
16 description:
17     - This module can be used to perform various operations related to backup, restore and reset of ESXi host configuration.
18 author:
19     - Andreas Nafpliotis (@nafpliot-ibm)
20 notes:
21     - Tested on ESXi 6.0
22     - Works only for ESXi hosts
23     - For configuration load or reset, the host will be switched automatically to maintenance mode.
24 requirements:
25     - "python >= 2.6"
26     - PyVmomi installed
27 options:
28     esxi_hostname:
29         description:
30             - Name of ESXi server. This is required only if authentication against a vCenter is done.
31         required: False
32         type: str
33     dest:
34         description:
35             - The destination where the ESXi configuration bundle will be saved. The I(dest) can be a folder or a file.
36             - If I(dest) is a folder, the backup file will be saved in the folder with the default filename generated from the ESXi server.
37             - If I(dest) is a file, the backup file will be saved with that filename. The file extension will always be .tgz.
38         type: path
39     src:
40         description:
41             - The file containing the ESXi configuration that will be restored.
42         type: path
43     state:
44         description:
45             - If C(saved), the .tgz backup bundle will be saved in I(dest).
46             - If C(absent), the host configuration will be reset to default values.
47             - If C(loaded), the backup file in I(src) will be loaded to the ESXi host rewriting the hosts settings.
48         choices: [saved, absent, loaded]
49         type: str
50         required: True
51 extends_documentation_fragment:
52 - community.vmware.vmware.documentation
53 
54 '''
55 
56 EXAMPLES = r'''
57 - name: Save the ESXi configuration locally by authenticating directly against the ESXi host
58   community.vmware.vmware_cfg_backup:
59     hostname: '{{ esxi_hostname }}'
60     username: '{{ esxi_username }}'
61     password: '{{ esxi_password }}'
62     state: saved
63     dest: /tmp/
64   delegate_to: localhost
65 
66 - name: Save the ESXi configuration locally by authenticating against the vCenter and selecting the ESXi host
67   community.vmware.vmware_cfg_backup:
68     hostname: '{{ vcenter_hostname }}'
69     esxi_hostname: '{{ esxi_hostname }}'
70     username: '{{ esxi_username }}'
71     password: '{{ esxi_password }}'
72     state: saved
73     dest: /tmp/
74   delegate_to: localhost
75 '''
76 
77 RETURN = r'''
78 dest_file:
79     description: The full path of where the file holding the ESXi configurations was stored
80     returned: changed
81     type: str
82     sample: /tmp/configBundle-esxi.host.domain.tgz
83 '''
84 
85 import os
86 try:
87     from pyVmomi import vim
88 except ImportError:
89     pass
90 
91 from ansible_collections.community.vmware.plugins.module_utils.vmware import vmware_argument_spec, get_all_objs, wait_for_task, PyVmomi
92 from ansible.module_utils.basic import AnsibleModule
93 from ansible.module_utils.urls import open_url
94 from ansible.module_utils.six.moves.urllib.error import HTTPError
95 from ansible.module_utils._text import to_native
96 
97 
98 class VMwareConfigurationBackup(PyVmomi):
99     def __init__(self, module):
100         super(VMwareConfigurationBackup, self).__init__(module)
101         self.state = self.module.params['state']
102         self.dest = self.module.params['dest']
103         self.src = self.module.params['src']
104         self.hostname = self.module.params['hostname']
105         self.username = self.module.params['username']
106         self.password = self.module.params['password']
107         self.validate_certs = self.module.params['validate_certs']
108         self.esxi_hostname = self.module.params.get('esxi_hostname', None)
109         self.host = self.find_host_system()
110 
111     def find_host_system(self):
112         if self.esxi_hostname:
113             host_system_obj = self.find_hostsystem_by_name(host_name=self.esxi_hostname)
114             if host_system_obj:
115                 return host_system_obj
116             else:
117                 self.module.fail_json(msg="Failed to find ESXi %s" % self.esxi_hostname)
118 
119         host_system = get_all_objs(self.content, [vim.HostSystem])
120         return list(host_system)[0]
121 
122     def process_state(self):
123         if self.state == 'saved':
124             self.save_configuration()
125 
126         if self.state == 'absent':
127             self.reset_configuration()
128 
129         if self.state == 'loaded':
130             self.load_configuration()
131 
132     def load_configuration(self):
133         if not os.path.isfile(self.src):
134             self.module.fail_json(msg="Source file {0} does not exist".format(self.src))
135 
136         url = self.host.configManager.firmwareSystem.QueryFirmwareConfigUploadURL()
137         url = url.replace('*', self.host.name)
138         # find manually the url if there is a redirect because urllib2 -per RFC- doesn't do automatic redirects for PUT requests
139         try:
140             open_url(url=url, method='HEAD', validate_certs=self.validate_certs)
141         except HTTPError as e:
142             url = e.geturl()
143 
144         try:
145             with open(self.src, 'rb') as file:
146                 data = file.read()
147             open_url(
148                 url=url, data=data, method='PUT', validate_certs=self.validate_certs,
149                 url_username=self.username, url_password=self.password, force_basic_auth=True)
150         except Exception as e:
151             self.module.fail_json(msg=to_native(e))
152 
153         if not self.host.runtime.inMaintenanceMode:
154             self.enter_maintenance()
155         try:
156             self.host.configManager.firmwareSystem.RestoreFirmwareConfiguration(force=True)
157             self.module.exit_json(changed=True)
158         except Exception as e:
159             self.exit_maintenance()
160             self.module.fail_json(msg=to_native(e))
161 
162     def reset_configuration(self):
163         if not self.host.runtime.inMaintenanceMode:
164             self.enter_maintenance()
165         try:
166             self.host.configManager.firmwareSystem.ResetFirmwareToFactoryDefaults()
167             self.module.exit_json(changed=True)
168         except Exception as e:
169             self.exit_maintenance()
170             self.module.fail_json(msg=to_native(e))
171 
172     def save_configuration(self):
173         url = self.host.configManager.firmwareSystem.BackupFirmwareConfiguration()
174         url = url.replace('*', self.host.name)
175         if os.path.isdir(self.dest):
176             filename = url.rsplit('/', 1)[1]
177             self.dest = os.path.join(self.dest, filename)
178         else:
179             filename, file_extension = os.path.splitext(self.dest)
180             if file_extension != ".tgz":
181                 self.dest = filename + ".tgz"
182         try:
183             request = open_url(url=url, validate_certs=self.validate_certs)
184             with open(self.dest, "wb") as file:
185                 file.write(request.read())
186             self.module.exit_json(changed=True, dest_file=self.dest)
187         except IOError as e:
188             self.module.fail_json(msg="Failed to write backup file. Ensure that "
189                                       "the dest path exists and is writable. Details : %s" % to_native(e))
190         except Exception as e:
191             self.module.fail_json(msg=to_native(e))
192 
193     def enter_maintenance(self):
194         try:
195             task = self.host.EnterMaintenanceMode_Task(timeout=15)
196             success, result = wait_for_task(task)
197         except Exception as e:
198             self.module.fail_json(msg="Failed to enter maintenance mode."
199                                       " Ensure that there are no powered on machines on the host. %s" % to_native(e))
200 
201     def exit_maintenance(self):
202         try:
203             task = self.host.ExitMaintenanceMode_Task(timeout=15)
204             success, result = wait_for_task(task)
205         except Exception as generic_exc:
206             self.module.fail_json(msg="Failed to exit maintenance mode due to %s" % to_native(generic_exc))
207 
208 
209 def main():
210     argument_spec = vmware_argument_spec()
211     argument_spec.update(dict(dest=dict(required=False, type='path'),
212                               esxi_hostname=dict(required=False, type='str'),
213                               src=dict(required=False, type='path'),
214                               state=dict(required=True, choices=['saved', 'absent', 'loaded'], type='str')))
215     required_if = [('state', 'saved', ['dest']),
216                    ('state', 'loaded', ['src'])]
217 
218     module = AnsibleModule(argument_spec=argument_spec,
219                            required_if=required_if,
220                            supports_check_mode=False)
221 
222     vmware_cfg_backup = VMwareConfigurationBackup(module)
223     vmware_cfg_backup.process_state()
224 
225 
226 if __name__ == '__main__':
227     main()
228
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/plugins/modules/vmware_cfg_backup.py b/plugins/modules/vmware_cfg_backup.py
--- a/plugins/modules/vmware_cfg_backup.py
+++ b/plugins/modules/vmware_cfg_backup.py
@@ -172,6 +172,8 @@
     def save_configuration(self):
         url = self.host.configManager.firmwareSystem.BackupFirmwareConfiguration()
         url = url.replace('*', self.host.name)
+        if self.module.params["port"] == 443:
+            url = url.replace("http:", "https:")
         if os.path.isdir(self.dest):
             filename = url.rsplit('/', 1)[1]
             self.dest = os.path.join(self.dest, filename)

{"golden_diff": "diff --git a/plugins/modules/vmware_cfg_backup.py b/plugins/modules/vmware_cfg_backup.py\n--- a/plugins/modules/vmware_cfg_backup.py\n+++ b/plugins/modules/vmware_cfg_backup.py\n@@ -172,6 +172,8 @@\n     def save_configuration(self):\n         url = self.host.configManager.firmwareSystem.BackupFirmwareConfiguration()\n         url = url.replace('*', self.host.name)\n+        if self.module.params[\"port\"] == 443:\n+            url = url.replace(\"http:\", \"https:\")\n         if os.path.isdir(self.dest):\n             filename = url.rsplit('/', 1)[1]\n             self.dest = os.path.join(self.dest, filename)\n", "issue": "community.vmware.vmware_cfg_backup: Failed to write backup file\n<!--- Verify first that your issue is not already reported on GitHub -->\r\n<!--- Also test if the latest release and devel branch are affected too -->\r\n<!--- Complete *all* sections as described, this form is processed automatically -->\r\n\r\n##### SUMMARY\r\nWhen I run the ansible playbook it's return \"Failed to write backup file\" error.\r\n\r\n```\r\n[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'\r\n\r\nPLAY [localhost] *************************************************************************************************************************************************************************************************************************\r\n\r\nTASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************\r\nok: [localhost]\r\n\r\nTASK [ESXI backup test] ******************************************************************************************************************************************************************************************************************\r\nfatal: [localhost -> localhost]: FAILED! => {\"changed\": false, \"msg\": \"Failed to write backup file. Ensure that the dest path exists and is writable. Details : <urlopen error [Errno 111] Connection refused>\"}\r\n\r\nPLAY RECAP *******************************************************************************************************************************************************************************************************************************\r\nlocalhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0\r\n```\r\n\r\n\r\n##### ISSUE TYPE\r\n- Bug Report\r\n\r\n##### COMPONENT NAME\r\n<!--- Write the short name of the module, plugin, task or feature below, use your best guess if unsure -->\r\n`community.vmware.vmware_cfg_backup`\r\n\r\n##### ANSIBLE VERSION\r\n<!--- Paste verbatim output from \"ansible --version\" between quotes -->\r\n```paste below\r\nansible 2.9.6\r\n  config file = /etc/ansible/ansible.cfg\r\n  configured module search path = ['/home/sergen/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']\r\n  ansible python module location = /usr/lib/python3/dist-packages/ansible\r\n  executable location = /usr/bin/ansible\r\n  python version = 3.8.10 (default, Nov 26 2021, 20:14:08) [GCC 9.3.0]\r\n\r\n```\r\n\r\n##### COLLECTION VERSION\r\n<!--- Paste verbatim output from \"ansible-galaxy collection list <namespace>.<collection>\"  between the quotes\r\nfor example: ansible-galaxy collection list community.general\r\n-->\r\n```paste below\r\nansible-galaxy collection list community.vmware\r\nusage: ansible-galaxy collection [-h] COLLECTION_ACTION ...\r\nansible-galaxy collection: error: argument COLLECTION_ACTION: invalid choice: 'list' (choose from 'init', 'build', 'publish', 'install')\r\n\r\n```\r\nList command is not working but I install collection with this command\r\n\r\n`ansible-galaxy collection install community.vmware`\r\n\r\n##### CONFIGURATION\r\n<!--- Paste verbatim output from \"ansible-config dump --only-changed\" between quotes -->\r\n```paste below\r\n\r\n```\r\nno output\r\n\r\n##### OS / ENVIRONMENT\r\n<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->\r\nUbuntu 20.04\r\nVMware ESXI 7.0.3\r\n\r\n##YAML FILE\r\n\r\nreturns the same result in both files \r\n\r\n```\r\n- hosts: localhost\r\n  vars:\r\n    esxi_hostname: \"192.168.88.154\"\r\n    esxi_username: \"root\"\r\n    esxi_password: \"password\"\r\n  tasks:\r\n  - name: ESXI backup test\r\n    local_action:\r\n      module: vmware_cfg_backup\r\n      hostname: '{{esxi_hostname}}'\r\n      username: '{{esxi_username}}'\r\n      password: '{{esxi_password}}'\r\n      state: saved\r\n      dest: /tmp/\r\n      validate_certs: no\r\n```\r\n```\r\n\r\ncat vmware.yaml\r\n- hosts: localhost\r\n  vars:\r\n    esxi_hostname: \"192.168.88.154\"\r\n    esxi_username: \"root\"\r\n    esxi_password: \"password\"\r\n  tasks:\r\n  - name: Save\r\n    community.vmware.vmware_cfg_backup:\r\n      hostname: '{{ esxi_hostname }}'\r\n      username: '{{ esxi_username }}'\r\n      password: '{{ esxi_password }}'\r\n      state: saved\r\n      dest: /tmp/\r\n      validate_certs: no\r\n    delegate_to: localhost\r\n\r\n```\r\n\r\n\r\n##### STEPS TO REPRODUCE\r\n<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->\r\n\r\n<!--- Paste example playbooks or commands between quotes below -->\r\n```yaml\r\n\r\n```\r\n\r\n<!--- HINT: You can paste gist.github.com links for larger files -->\r\n\r\n##### EXPECTED RESULTS\r\n<!--- Describe what you expected to happen when running the steps above -->\r\n\r\n\r\n##### ACTUAL RESULTS\r\n<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->\r\n\r\n<!--- Paste verbatim command output between quotes -->\r\n```\r\nansible-playbook vmware.yaml\r\n[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'\r\n\r\nPLAY [localhost] *************************************************************************************************************************************************************************************************************************\r\n\r\nTASK [Gathering Facts] *******************************************************************************************************************************************************************************************************************\r\nok: [localhost]\r\n\r\nTASK [Save] ******************************************************************************************************************************************************************************************************************************\r\nfatal: [localhost -> localhost]: FAILED! => {\"changed\": false, \"msg\": \"Failed to write backup file. Ensure that the dest path exists and is writable. Details : <urlopen error [Errno 111] Connection refused>\"}\r\n\r\nPLAY RECAP *******************************************************************************************************************************************************************************************************************************\r\nlocalhost                  : ok=1    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0\r\n\r\n\r\n```\r\n\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# Copyright: (c) 2017, IBM Corp\n# Author(s): Andreas Nafpliotis <nafpliot@de.ibm.com>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\n\nDOCUMENTATION = r'''\n---\nmodule: vmware_cfg_backup\nshort_description: Backup / Restore / Reset ESXi host configuration\ndescription:\n    - This module can be used to perform various operations related to backup, restore and reset of ESXi host configuration.\nauthor:\n    - Andreas Nafpliotis (@nafpliot-ibm)\nnotes:\n    - Tested on ESXi 6.0\n    - Works only for ESXi hosts\n    - For configuration load or reset, the host will be switched automatically to maintenance mode.\nrequirements:\n    - \"python >= 2.6\"\n    - PyVmomi installed\noptions:\n    esxi_hostname:\n        description:\n            - Name of ESXi server. This is required only if authentication against a vCenter is done.\n        required: False\n        type: str\n    dest:\n        description:\n            - The destination where the ESXi configuration bundle will be saved. The I(dest) can be a folder or a file.\n            - If I(dest) is a folder, the backup file will be saved in the folder with the default filename generated from the ESXi server.\n            - If I(dest) is a file, the backup file will be saved with that filename. The file extension will always be .tgz.\n        type: path\n    src:\n        description:\n            - The file containing the ESXi configuration that will be restored.\n        type: path\n    state:\n        description:\n            - If C(saved), the .tgz backup bundle will be saved in I(dest).\n            - If C(absent), the host configuration will be reset to default values.\n            - If C(loaded), the backup file in I(src) will be loaded to the ESXi host rewriting the hosts settings.\n        choices: [saved, absent, loaded]\n        type: str\n        required: True\nextends_documentation_fragment:\n- community.vmware.vmware.documentation\n\n'''\n\nEXAMPLES = r'''\n- name: Save the ESXi configuration locally by authenticating directly against the ESXi host\n  community.vmware.vmware_cfg_backup:\n    hostname: '{{ esxi_hostname }}'\n    username: '{{ esxi_username }}'\n    password: '{{ esxi_password }}'\n    state: saved\n    dest: /tmp/\n  delegate_to: localhost\n\n- name: Save the ESXi configuration locally by authenticating against the vCenter and selecting the ESXi host\n  community.vmware.vmware_cfg_backup:\n    hostname: '{{ vcenter_hostname }}'\n    esxi_hostname: '{{ esxi_hostname }}'\n    username: '{{ esxi_username }}'\n    password: '{{ esxi_password }}'\n    state: saved\n    dest: /tmp/\n  delegate_to: localhost\n'''\n\nRETURN = r'''\ndest_file:\n    description: The full path of where the file holding the ESXi configurations was stored\n    returned: changed\n    type: str\n    sample: /tmp/configBundle-esxi.host.domain.tgz\n'''\n\nimport os\ntry:\n    from pyVmomi import vim\nexcept ImportError:\n    pass\n\nfrom ansible_collections.community.vmware.plugins.module_utils.vmware import vmware_argument_spec, get_all_objs, wait_for_task, PyVmomi\nfrom ansible.module_utils.basic import AnsibleModule\nfrom ansible.module_utils.urls import open_url\nfrom ansible.module_utils.six.moves.urllib.error import HTTPError\nfrom ansible.module_utils._text import to_native\n\n\nclass VMwareConfigurationBackup(PyVmomi):\n    def __init__(self, module):\n        super(VMwareConfigurationBackup, self).__init__(module)\n        self.state = self.module.params['state']\n        self.dest = self.module.params['dest']\n        self.src = self.module.params['src']\n        self.hostname = self.module.params['hostname']\n        self.username = self.module.params['username']\n        self.password = self.module.params['password']\n        self.validate_certs = self.module.params['validate_certs']\n        self.esxi_hostname = self.module.params.get('esxi_hostname', None)\n        self.host = self.find_host_system()\n\n    def find_host_system(self):\n        if self.esxi_hostname:\n            host_system_obj = self.find_hostsystem_by_name(host_name=self.esxi_hostname)\n            if host_system_obj:\n                return host_system_obj\n            else:\n                self.module.fail_json(msg=\"Failed to find ESXi %s\" % self.esxi_hostname)\n\n        host_system = get_all_objs(self.content, [vim.HostSystem])\n        return list(host_system)[0]\n\n    def process_state(self):\n        if self.state == 'saved':\n            self.save_configuration()\n\n        if self.state == 'absent':\n            self.reset_configuration()\n\n        if self.state == 'loaded':\n            self.load_configuration()\n\n    def load_configuration(self):\n        if not os.path.isfile(self.src):\n            self.module.fail_json(msg=\"Source file {0} does not exist\".format(self.src))\n\n        url = self.host.configManager.firmwareSystem.QueryFirmwareConfigUploadURL()\n        url = url.replace('*', self.host.name)\n        # find manually the url if there is a redirect because urllib2 -per RFC- doesn't do automatic redirects for PUT requests\n        try:\n            open_url(url=url, method='HEAD', validate_certs=self.validate_certs)\n        except HTTPError as e:\n            url = e.geturl()\n\n        try:\n            with open(self.src, 'rb') as file:\n                data = file.read()\n            open_url(\n                url=url, data=data, method='PUT', validate_certs=self.validate_certs,\n                url_username=self.username, url_password=self.password, force_basic_auth=True)\n        except Exception as e:\n            self.module.fail_json(msg=to_native(e))\n\n        if not self.host.runtime.inMaintenanceMode:\n            self.enter_maintenance()\n        try:\n            self.host.configManager.firmwareSystem.RestoreFirmwareConfiguration(force=True)\n            self.module.exit_json(changed=True)\n        except Exception as e:\n            self.exit_maintenance()\n            self.module.fail_json(msg=to_native(e))\n\n    def reset_configuration(self):\n        if not self.host.runtime.inMaintenanceMode:\n            self.enter_maintenance()\n        try:\n            self.host.configManager.firmwareSystem.ResetFirmwareToFactoryDefaults()\n            self.module.exit_json(changed=True)\n        except Exception as e:\n            self.exit_maintenance()\n            self.module.fail_json(msg=to_native(e))\n\n    def save_configuration(self):\n        url = self.host.configManager.firmwareSystem.BackupFirmwareConfiguration()\n        url = url.replace('*', self.host.name)\n        if os.path.isdir(self.dest):\n            filename = url.rsplit('/', 1)[1]\n            self.dest = os.path.join(self.dest, filename)\n        else:\n            filename, file_extension = os.path.splitext(self.dest)\n            if file_extension != \".tgz\":\n                self.dest = filename + \".tgz\"\n        try:\n            request = open_url(url=url, validate_certs=self.validate_certs)\n            with open(self.dest, \"wb\") as file:\n                file.write(request.read())\n            self.module.exit_json(changed=True, dest_file=self.dest)\n        except IOError as e:\n            self.module.fail_json(msg=\"Failed to write backup file. Ensure that \"\n                                      \"the dest path exists and is writable. Details : %s\" % to_native(e))\n        except Exception as e:\n            self.module.fail_json(msg=to_native(e))\n\n    def enter_maintenance(self):\n        try:\n            task = self.host.EnterMaintenanceMode_Task(timeout=15)\n            success, result = wait_for_task(task)\n        except Exception as e:\n            self.module.fail_json(msg=\"Failed to enter maintenance mode.\"\n                                      \" Ensure that there are no powered on machines on the host. %s\" % to_native(e))\n\n    def exit_maintenance(self):\n        try:\n            task = self.host.ExitMaintenanceMode_Task(timeout=15)\n            success, result = wait_for_task(task)\n        except Exception as generic_exc:\n            self.module.fail_json(msg=\"Failed to exit maintenance mode due to %s\" % to_native(generic_exc))\n\n\ndef main():\n    argument_spec = vmware_argument_spec()\n    argument_spec.update(dict(dest=dict(required=False, type='path'),\n                              esxi_hostname=dict(required=False, type='str'),\n                              src=dict(required=False, type='path'),\n                              state=dict(required=True, choices=['saved', 'absent', 'loaded'], type='str')))\n    required_if = [('state', 'saved', ['dest']),\n                   ('state', 'loaded', ['src'])]\n\n    module = AnsibleModule(argument_spec=argument_spec,\n                           required_if=required_if,\n                           supports_check_mode=False)\n\n    vmware_cfg_backup = VMwareConfigurationBackup(module)\n    vmware_cfg_backup.process_state()\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/vmware_cfg_backup.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# Copyright: (c) 2017, IBM Corp\n# Author(s): Andreas Nafpliotis <nafpliot@de.ibm.com>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\n\nDOCUMENTATION = r'''\n---\nmodule: vmware_cfg_backup\nshort_description: Backup / Restore / Reset ESXi host configuration\ndescription:\n    - This module can be used to perform various operations related to backup, restore and reset of ESXi host configuration.\nauthor:\n    - Andreas Nafpliotis (@nafpliot-ibm)\nnotes:\n    - Tested on ESXi 6.0\n    - Works only for ESXi hosts\n    - For configuration load or reset, the host will be switched automatically to maintenance mode.\nrequirements:\n    - \"python >= 2.6\"\n    - PyVmomi installed\noptions:\n    esxi_hostname:\n        description:\n            - Name of ESXi server. This is required only if authentication against a vCenter is done.\n        required: False\n        type: str\n    dest:\n        description:\n            - The destination where the ESXi configuration bundle will be saved. The I(dest) can be a folder or a file.\n            - If I(dest) is a folder, the backup file will be saved in the folder with the default filename generated from the ESXi server.\n            - If I(dest) is a file, the backup file will be saved with that filename. The file extension will always be .tgz.\n        type: path\n    src:\n        description:\n            - The file containing the ESXi configuration that will be restored.\n        type: path\n    state:\n        description:\n            - If C(saved), the .tgz backup bundle will be saved in I(dest).\n            - If C(absent), the host configuration will be reset to default values.\n            - If C(loaded), the backup file in I(src) will be loaded to the ESXi host rewriting the hosts settings.\n        choices: [saved, absent, loaded]\n        type: str\n        required: True\nextends_documentation_fragment:\n- community.vmware.vmware.documentation\n\n'''\n\nEXAMPLES = r'''\n- name: Save the ESXi configuration locally by authenticating directly against the ESXi host\n  community.vmware.vmware_cfg_backup:\n    hostname: '{{ esxi_hostname }}'\n    username: '{{ esxi_username }}'\n    password: '{{ esxi_password }}'\n    state: saved\n    dest: /tmp/\n  delegate_to: localhost\n\n- name: Save the ESXi configuration locally by authenticating against the vCenter and selecting the ESXi host\n  community.vmware.vmware_cfg_backup:\n    hostname: '{{ vcenter_hostname }}'\n    esxi_hostname: '{{ esxi_hostname }}'\n    username: '{{ esxi_username }}'\n    password: '{{ esxi_password }}'\n    state: saved\n    dest: /tmp/\n  delegate_to: localhost\n'''\n\nRETURN = r'''\ndest_file:\n    description: The full path of where the file holding the ESXi configurations was stored\n    returned: changed\n    type: str\n    sample: /tmp/configBundle-esxi.host.domain.tgz\n'''\n\nimport os\ntry:\n    from pyVmomi import vim\nexcept ImportError:\n    pass\n\nfrom ansible_collections.community.vmware.plugins.module_utils.vmware import vmware_argument_spec, get_all_objs, wait_for_task, PyVmomi\nfrom ansible.module_utils.basic import AnsibleModule\nfrom ansible.module_utils.urls import open_url\nfrom ansible.module_utils.six.moves.urllib.error import HTTPError\nfrom ansible.module_utils._text import to_native\n\n\nclass VMwareConfigurationBackup(PyVmomi):\n    def __init__(self, module):\n        super(VMwareConfigurationBackup, self).__init__(module)\n        self.state = self.module.params['state']\n        self.dest = self.module.params['dest']\n        self.src = self.module.params['src']\n        self.hostname = self.module.params['hostname']\n        self.username = self.module.params['username']\n        self.password = self.module.params['password']\n        self.validate_certs = self.module.params['validate_certs']\n        self.esxi_hostname = self.module.params.get('esxi_hostname', None)\n        self.host = self.find_host_system()\n\n    def find_host_system(self):\n        if self.esxi_hostname:\n            host_system_obj = self.find_hostsystem_by_name(host_name=self.esxi_hostname)\n            if host_system_obj:\n                return host_system_obj\n            else:\n                self.module.fail_json(msg=\"Failed to find ESXi %s\" % self.esxi_hostname)\n\n        host_system = get_all_objs(self.content, [vim.HostSystem])\n        return list(host_system)[0]\n\n    def process_state(self):\n        if self.state == 'saved':\n            self.save_configuration()\n\n        if self.state == 'absent':\n            self.reset_configuration()\n\n        if self.state == 'loaded':\n            self.load_configuration()\n\n    def load_configuration(self):\n        if not os.path.isfile(self.src):\n            self.module.fail_json(msg=\"Source file {0} does not exist\".format(self.src))\n\n        url = self.host.configManager.firmwareSystem.QueryFirmwareConfigUploadURL()\n        url = url.replace('*', self.host.name)\n        # find manually the url if there is a redirect because urllib2 -per RFC- doesn't do automatic redirects for PUT requests\n        try:\n            open_url(url=url, method='HEAD', validate_certs=self.validate_certs)\n        except HTTPError as e:\n            url = e.geturl()\n\n        try:\n            with open(self.src, 'rb') as file:\n                data = file.read()\n            open_url(\n                url=url, data=data, method='PUT', validate_certs=self.validate_certs,\n                url_username=self.username, url_password=self.password, force_basic_auth=True)\n        except Exception as e:\n            self.module.fail_json(msg=to_native(e))\n\n        if not self.host.runtime.inMaintenanceMode:\n            self.enter_maintenance()\n        try:\n            self.host.configManager.firmwareSystem.RestoreFirmwareConfiguration(force=True)\n            self.module.exit_json(changed=True)\n        except Exception as e:\n            self.exit_maintenance()\n            self.module.fail_json(msg=to_native(e))\n\n    def reset_configuration(self):\n        if not self.host.runtime.inMaintenanceMode:\n            self.enter_maintenance()\n        try:\n            self.host.configManager.firmwareSystem.ResetFirmwareToFactoryDefaults()\n            self.module.exit_json(changed=True)\n        except Exception as e:\n            self.exit_maintenance()\n            self.module.fail_json(msg=to_native(e))\n\n    def save_configuration(self):\n        url = self.host.configManager.firmwareSystem.BackupFirmwareConfiguration()\n        url = url.replace('*', self.host.name)\n        if self.module.params[\"port\"] == 443:\n            url = url.replace(\"http:\", \"https:\")\n        if os.path.isdir(self.dest):\n            filename = url.rsplit('/', 1)[1]\n            self.dest = os.path.join(self.dest, filename)\n        else:\n            filename, file_extension = os.path.splitext(self.dest)\n            if file_extension != \".tgz\":\n                self.dest = filename + \".tgz\"\n        try:\n            request = open_url(url=url, validate_certs=self.validate_certs)\n            with open(self.dest, \"wb\") as file:\n                file.write(request.read())\n            self.module.exit_json(changed=True, dest_file=self.dest)\n        except IOError as e:\n            self.module.fail_json(msg=\"Failed to write backup file. Ensure that \"\n                                      \"the dest path exists and is writable. Details : %s\" % to_native(e))\n        except Exception as e:\n            self.module.fail_json(msg=to_native(e))\n\n    def enter_maintenance(self):\n        try:\n            task = self.host.EnterMaintenanceMode_Task(timeout=15)\n            success, result = wait_for_task(task)\n        except Exception as e:\n            self.module.fail_json(msg=\"Failed to enter maintenance mode.\"\n                                      \" Ensure that there are no powered on machines on the host. %s\" % to_native(e))\n\n    def exit_maintenance(self):\n        try:\n            task = self.host.ExitMaintenanceMode_Task(timeout=15)\n            success, result = wait_for_task(task)\n        except Exception as generic_exc:\n            self.module.fail_json(msg=\"Failed to exit maintenance mode due to %s\" % to_native(generic_exc))\n\n\ndef main():\n    argument_spec = vmware_argument_spec()\n    argument_spec.update(dict(dest=dict(required=False, type='path'),\n                              esxi_hostname=dict(required=False, type='str'),\n                              src=dict(required=False, type='path'),\n                              state=dict(required=True, choices=['saved', 'absent', 'loaded'], type='str')))\n    required_if = [('state', 'saved', ['dest']),\n                   ('state', 'loaded', ['src'])]\n\n    module = AnsibleModule(argument_spec=argument_spec,\n                           required_if=required_if,\n                           supports_check_mode=False)\n\n    vmware_cfg_backup = VMwareConfigurationBackup(module)\n    vmware_cfg_backup.process_state()\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/vmware_cfg_backup.py"}]}