We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
vmware_tag_manager: allow tags to datastore clusters
##### SUMMARY
vmware_tag_manager: allow tags to be applied to datastore clusters much like they can to switches, vms, etc.

##### ISSUE TYPE
- Feature Idea

##### COMPONENT NAME
vmware_tag_manager

##### ADDITIONAL INFORMATION
As a VI Admin, it is common to need to apply tags to all items in VMware in order to track information about those items.  Additionally, in the instance of datastore clusters, these are often used to create storage policies within the environment.

```yaml
- name: "apply tags to datastore cluster"
  vmware_tag_manager:
    hostname:        "{{ vcenter_server }}"
    username:        "{{ vcenter_username }}"
    password:        "{{ vcenter_password }}"
    validate_certs:  false
    tag_names:       ["Storage:gold"]
    object_name:     "High-Performance-Datastore-Cluster"
    object_type:     "DatastoreCluster"
    state:           "present"
```



--- 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_tag_manager.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 # Copyright: (c) 2018, Ansible Project
4 # Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
5 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 
7 from __future__ import absolute_import, division, print_function
8 __metaclass__ = type
9 
10 
11 DOCUMENTATION = r'''
12 ---
13 module: vmware_tag_manager
14 short_description: Manage association of VMware tags with VMware objects
15 description:
16 - This module can be used to assign / remove VMware tags from the given VMware objects.
17 - Tag feature is introduced in vSphere 6 version, so this module is not supported in the earlier versions of vSphere.
18 - All variables and VMware object names are case sensitive.
19 author:
20 - Abhijeet Kasurde (@Akasurde)
21 - Frederic Van Reet (@GBrawl)
22 notes:
23 - Tested on vSphere 6.5
24 requirements:
25 - python >= 2.6
26 - PyVmomi
27 - vSphere Automation SDK
28 options:
29     tag_names:
30       description:
31       - List of tag(s) to be managed.
32       - User can also specify category name by specifying colon separated value. For example, "category_name:tag_name".
33       - User can also specify tag and category as dict, when tag or category contains colon.
34         See example for more information. Added in version 2.10.
35       - User can skip category name if you have unique tag names.
36       required: True
37       type: list
38     state:
39       description:
40       - If C(state) is set to C(add) or C(present) will add the tags to the existing tag list of the given object.
41       - If C(state) is set to C(remove) or C(absent) will remove the tags from the existing tag list of the given object.
42       - If C(state) is set to C(set) will replace the tags of the given objects with the user defined list of tags.
43       default: add
44       choices: [ present, absent, add, remove, set ]
45       type: str
46     object_type:
47       description:
48       - Type of object to work with.
49       required: True
50       choices: [ VirtualMachine, Datacenter, ClusterComputeResource, HostSystem, DistributedVirtualSwitch, DistributedVirtualPortgroup ]
51       type: str
52     object_name:
53       description:
54       - Name of the object to work with.
55       - For DistributedVirtualPortgroups the format should be "switch_name:portgroup_name"
56       required: True
57       type: str
58 extends_documentation_fragment:
59 - community.vmware.vmware_rest_client.documentation
60 
61 '''
62 
63 EXAMPLES = r'''
64 - name: Add tags to a virtual machine
65   community.vmware.vmware_tag_manager:
66     hostname: '{{ vcenter_hostname }}'
67     username: '{{ vcenter_username }}'
68     password: '{{ vcenter_password }}'
69     validate_certs: no
70     tag_names:
71       - Sample_Tag_0002
72       - Category_0001:Sample_Tag_0003
73     object_name: Fedora_VM
74     object_type: VirtualMachine
75     state: add
76   delegate_to: localhost
77 
78 - name: Specify tag and category as dict
79   community.vmware.vmware_tag_manager:
80     hostname: '{{ vcenter_hostname }}'
81     username: '{{ vcenter_username }}'
82     password: '{{ vcenter_password }}'
83     validate_certs: no
84     tag_names:
85       - tag: tag_0001
86         category: cat_0001
87       - tag: tag_0002
88         category: cat_0002
89     object_name: Fedora_VM
90     object_type: VirtualMachine
91     state: add
92   delegate_to: localhost
93 
94 - name: Remove a tag from a virtual machine
95   community.vmware.vmware_tag_manager:
96     hostname: '{{ vcenter_hostname }}'
97     username: '{{ vcenter_username }}'
98     password: '{{ vcenter_password }}'
99     validate_certs: no
100     tag_names:
101       - Sample_Tag_0002
102     object_name: Fedora_VM
103     object_type: VirtualMachine
104     state: remove
105   delegate_to: localhost
106 
107 - name: Add tags to a distributed virtual switch
108   community.vmware.vmware_tag_manager:
109     hostname: '{{ vcenter_hostname }}'
110     username: '{{ vcenter_username }}'
111     password: '{{ vcenter_password }}'
112     validate_certs: no
113     tag_names:
114       - Sample_Tag_0003
115     object_name: Switch_0001
116     object_type: DistributedVirtualSwitch
117     state: add
118   delegate_to: localhost
119 
120 - name: Add tags to a distributed virtual portgroup
121   community.vmware.vmware_tag_manager:
122     hostname: '{{ vcenter_hostname }}'
123     username: '{{ vcenter_username }}'
124     password: '{{ vcenter_password }}'
125     validate_certs: no
126     tag_names:
127       - Sample_Tag_0004
128     object_name: Switch_0001:Portgroup_0001
129     object_type: DistributedVirtualPortgroup
130     state: add
131   delegate_to: localhost
132 '''
133 
134 RETURN = r'''
135 tag_status:
136     description: metadata about tags related to object configuration
137     returned: on success
138     type: list
139     sample: {
140         "current_tags": [
141             "backup",
142             "security"
143         ],
144         "desired_tags": [
145             "security"
146         ],
147         "previous_tags": [
148             "backup",
149             "security"
150         ]
151     }
152 '''
153 from ansible.module_utils.basic import AnsibleModule
154 from ansible_collections.community.vmware.plugins.module_utils.vmware_rest_client import VmwareRestClient
155 from ansible_collections.community.vmware.plugins.module_utils.vmware import (PyVmomi, find_dvs_by_name, find_dvspg_by_name)
156 try:
157     from com.vmware.vapi.std_client import DynamicID
158     from com.vmware.vapi.std.errors_client import Error
159 except ImportError:
160     pass
161 
162 
163 class VmwareTagManager(VmwareRestClient):
164     def __init__(self, module):
165         """
166         Constructor
167         """
168         super(VmwareTagManager, self).__init__(module)
169         self.pyv = PyVmomi(module=module)
170 
171         self.object_type = self.params.get('object_type')
172         self.object_name = self.params.get('object_name')
173         self.managed_object = None
174 
175         if self.object_type == 'VirtualMachine':
176             self.managed_object = self.pyv.get_vm_or_template(self.object_name)
177 
178         if self.object_type == 'Datacenter':
179             self.managed_object = self.pyv.find_datacenter_by_name(self.object_name)
180 
181         if self.object_type == 'ClusterComputeResource':
182             self.managed_object = self.pyv.find_cluster_by_name(self.object_name)
183 
184         if self.object_type == 'HostSystem':
185             self.managed_object = self.pyv.find_hostsystem_by_name(self.object_name)
186 
187         if self.object_type == 'DistributedVirtualSwitch':
188             self.managed_object = find_dvs_by_name(self.pyv.content, self.object_name)
189             self.object_type = 'VmwareDistributedVirtualSwitch'
190 
191         if self.object_type == 'DistributedVirtualPortgroup':
192             dvs_name, pg_name = self.object_name.split(":", 1)
193             dv_switch = find_dvs_by_name(self.pyv.content, dvs_name)
194             if dv_switch is None:
195                 self.module.fail_json(msg="A distributed virtual switch with name %s does not exist" % dvs_name)
196             self.managed_object = find_dvspg_by_name(dv_switch, pg_name)
197 
198         if self.managed_object is None:
199             self.module.fail_json(msg="Failed to find the managed object for %s with type %s" % (self.object_name, self.object_type))
200 
201         if not hasattr(self.managed_object, '_moId'):
202             self.module.fail_json(msg="Unable to find managed object id for %s managed object" % self.object_name)
203 
204         self.dynamic_managed_object = DynamicID(type=self.object_type, id=self.managed_object._moId)
205 
206         self.tag_service = self.api_client.tagging.Tag
207         self.category_service = self.api_client.tagging.Category
208         self.tag_association_svc = self.api_client.tagging.TagAssociation
209 
210         self.tag_names = self.params.get('tag_names')
211 
212     def ensure_state(self):
213         """
214         Manage the internal state of tags
215 
216         """
217         results = dict(
218             changed=False,
219             tag_status=dict(),
220         )
221         changed = False
222         action = self.params.get('state')
223         available_tag_obj = self.get_tags_for_object(tag_service=self.tag_service,
224                                                      tag_assoc_svc=self.tag_association_svc,
225                                                      dobj=self.dynamic_managed_object)
226 
227         _temp_prev_tags = ["%s:%s" % (tag['category_name'], tag['name']) for tag in self.get_tags_for_dynamic_obj(self.dynamic_managed_object)]
228         results['tag_status']['previous_tags'] = _temp_prev_tags
229         results['tag_status']['desired_tags'] = self.tag_names
230 
231         # Check if category and tag combination exists as per user request
232         removed_tags_for_set = False
233         for tag in self.tag_names:
234             category_obj, category_name, tag_name = None, None, None
235             if isinstance(tag, dict):
236                 tag_name = tag.get('tag')
237                 category_name = tag.get('category')
238                 if category_name:
239                     # User specified category
240                     category_obj = self.search_svc_object_by_name(self.category_service, category_name)
241                     if not category_obj:
242                         self.module.fail_json(msg="Unable to find the category %s" % category_name)
243             elif isinstance(tag, str):
244                 if ":" in tag:
245                     # User specified category
246                     category_name, tag_name = tag.split(":", 1)
247                     category_obj = self.search_svc_object_by_name(self.category_service, category_name)
248                     if not category_obj:
249                         self.module.fail_json(msg="Unable to find the category %s" % category_name)
250                 else:
251                     # User specified only tag
252                     tag_name = tag
253 
254             if category_name:
255                 tag_obj = self.get_tag_by_category(tag_name=tag_name, category_name=category_name)
256             else:
257                 tag_obj = self.get_tag_by_name(tag_name=tag_name)
258 
259             if not tag_obj:
260                 self.module.fail_json(msg="Unable to find the tag %s" % tag_name)
261 
262             if action in ('add', 'present'):
263                 if tag_obj not in available_tag_obj:
264                     # Tag is not already applied
265                     try:
266                         self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)
267                         changed = True
268                     except Error as error:
269                         self.module.fail_json(msg="%s" % self.get_error_message(error))
270 
271             elif action == 'set':
272                 # Remove all tags first
273                 try:
274                     if not removed_tags_for_set:
275                         for av_tag in available_tag_obj:
276                             self.tag_association_svc.detach(tag_id=av_tag.id, object_id=self.dynamic_managed_object)
277                         removed_tags_for_set = True
278                     self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)
279                     changed = True
280                 except Error as error:
281                     self.module.fail_json(msg="%s" % self.get_error_message(error))
282 
283             elif action in ('remove', 'absent'):
284                 if tag_obj in available_tag_obj:
285                     try:
286                         self.tag_association_svc.detach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)
287                         changed = True
288                     except Error as error:
289                         self.module.fail_json(msg="%s" % self.get_error_message(error))
290 
291         _temp_curr_tags = ["%s:%s" % (tag['category_name'], tag['name']) for tag in self.get_tags_for_dynamic_obj(self.dynamic_managed_object)]
292         results['tag_status']['current_tags'] = _temp_curr_tags
293         results['changed'] = changed
294         self.module.exit_json(**results)
295 
296 
297 def main():
298     argument_spec = VmwareRestClient.vmware_client_argument_spec()
299     argument_spec.update(
300         tag_names=dict(type='list', required=True),
301         state=dict(type='str', choices=['absent', 'add', 'present', 'remove', 'set'], default='add'),
302         object_name=dict(type='str', required=True),
303         object_type=dict(type='str', required=True, choices=['VirtualMachine', 'Datacenter', 'ClusterComputeResource',
304                                                              'HostSystem', 'DistributedVirtualSwitch',
305                                                              'DistributedVirtualPortgroup']),
306     )
307     module = AnsibleModule(argument_spec=argument_spec)
308 
309     vmware_tag_manager = VmwareTagManager(module)
310     vmware_tag_manager.ensure_state()
311 
312 
313 if __name__ == '__main__':
314     main()
315
```
--- 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_tag_manager.py b/plugins/modules/vmware_tag_manager.py
--- a/plugins/modules/vmware_tag_manager.py
+++ b/plugins/modules/vmware_tag_manager.py
@@ -47,7 +47,7 @@
       description:
       - Type of object to work with.
       required: True
-      choices: [ VirtualMachine, Datacenter, ClusterComputeResource, HostSystem, DistributedVirtualSwitch, DistributedVirtualPortgroup ]
+      choices: [ VirtualMachine, Datacenter, ClusterComputeResource, HostSystem, DistributedVirtualSwitch, DistributedVirtualPortgroup, Datastore, DatastoreCluster, ResourcePool, Folder ]
       type: str
     object_name:
       description:
@@ -175,12 +175,25 @@
         if self.object_type == 'VirtualMachine':
             self.managed_object = self.pyv.get_vm_or_template(self.object_name)
 
+        if self.object_type == 'Folder':
+            self.managed_object = self.pyv.find_folder_by_name(self.object_name)
+
         if self.object_type == 'Datacenter':
             self.managed_object = self.pyv.find_datacenter_by_name(self.object_name)
 
+        if self.object_type == 'Datastore':
+            self.managed_object = self.pyv.find_datastore_by_name(self.object_name)
+
+        if self.object_type == 'DatastoreCluster':
+            self.managed_object = self.pyv.find_datastore_cluster_by_name(self.object_name)
+            self.object_type = 'StoragePod'
+
         if self.object_type == 'ClusterComputeResource':
             self.managed_object = self.pyv.find_cluster_by_name(self.object_name)
 
+        if self.object_type == 'ResourcePool':
+            self.managed_object = self.pyv.find_resource_pool_by_name(self.object_name)
+
         if self.object_type == 'HostSystem':
             self.managed_object = self.pyv.find_hostsystem_by_name(self.object_name)
 
@@ -302,7 +315,8 @@
         object_name=dict(type='str', required=True),
         object_type=dict(type='str', required=True, choices=['VirtualMachine', 'Datacenter', 'ClusterComputeResource',
                                                              'HostSystem', 'DistributedVirtualSwitch',
-                                                             'DistributedVirtualPortgroup']),
+                                                             'DistributedVirtualPortgroup', 'Datastore', 'ResourcePool',
+                                                             'Folder', 'DatastoreCluster']),
     )
     module = AnsibleModule(argument_spec=argument_spec)

{"golden_diff": "diff --git a/plugins/modules/vmware_tag_manager.py b/plugins/modules/vmware_tag_manager.py\n--- a/plugins/modules/vmware_tag_manager.py\n+++ b/plugins/modules/vmware_tag_manager.py\n@@ -47,7 +47,7 @@\n       description:\n       - Type of object to work with.\n       required: True\n-      choices: [ VirtualMachine, Datacenter, ClusterComputeResource, HostSystem, DistributedVirtualSwitch, DistributedVirtualPortgroup ]\n+      choices: [ VirtualMachine, Datacenter, ClusterComputeResource, HostSystem, DistributedVirtualSwitch, DistributedVirtualPortgroup, Datastore, DatastoreCluster, ResourcePool, Folder ]\n       type: str\n     object_name:\n       description:\n@@ -175,12 +175,25 @@\n         if self.object_type == 'VirtualMachine':\n             self.managed_object = self.pyv.get_vm_or_template(self.object_name)\n \n+        if self.object_type == 'Folder':\n+            self.managed_object = self.pyv.find_folder_by_name(self.object_name)\n+\n         if self.object_type == 'Datacenter':\n             self.managed_object = self.pyv.find_datacenter_by_name(self.object_name)\n \n+        if self.object_type == 'Datastore':\n+            self.managed_object = self.pyv.find_datastore_by_name(self.object_name)\n+\n+        if self.object_type == 'DatastoreCluster':\n+            self.managed_object = self.pyv.find_datastore_cluster_by_name(self.object_name)\n+            self.object_type = 'StoragePod'\n+\n         if self.object_type == 'ClusterComputeResource':\n             self.managed_object = self.pyv.find_cluster_by_name(self.object_name)\n \n+        if self.object_type == 'ResourcePool':\n+            self.managed_object = self.pyv.find_resource_pool_by_name(self.object_name)\n+\n         if self.object_type == 'HostSystem':\n             self.managed_object = self.pyv.find_hostsystem_by_name(self.object_name)\n \n@@ -302,7 +315,8 @@\n         object_name=dict(type='str', required=True),\n         object_type=dict(type='str', required=True, choices=['VirtualMachine', 'Datacenter', 'ClusterComputeResource',\n                                                              'HostSystem', 'DistributedVirtualSwitch',\n-                                                             'DistributedVirtualPortgroup']),\n+                                                             'DistributedVirtualPortgroup', 'Datastore', 'ResourcePool',\n+                                                             'Folder', 'DatastoreCluster']),\n     )\n     module = AnsibleModule(argument_spec=argument_spec)\n", "issue": "vmware_tag_manager: allow tags to datastore clusters\n##### SUMMARY\r\nvmware_tag_manager: allow tags to be applied to datastore clusters much like they can to switches, vms, etc.\r\n\r\n##### ISSUE TYPE\r\n- Feature Idea\r\n\r\n##### COMPONENT NAME\r\nvmware_tag_manager\r\n\r\n##### ADDITIONAL INFORMATION\r\nAs a VI Admin, it is common to need to apply tags to all items in VMware in order to track information about those items.  Additionally, in the instance of datastore clusters, these are often used to create storage policies within the environment.\r\n\r\n```yaml\r\n- name: \"apply tags to datastore cluster\"\r\n  vmware_tag_manager:\r\n    hostname:        \"{{ vcenter_server }}\"\r\n    username:        \"{{ vcenter_username }}\"\r\n    password:        \"{{ vcenter_password }}\"\r\n    validate_certs:  false\r\n    tag_names:       [\"Storage:gold\"]\r\n    object_name:     \"High-Performance-Datastore-Cluster\"\r\n    object_type:     \"DatastoreCluster\"\r\n    state:           \"present\"\r\n```\r\n\r\n\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n# Copyright: (c) 2018, Ansible Project\n# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.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_tag_manager\nshort_description: Manage association of VMware tags with VMware objects\ndescription:\n- This module can be used to assign / remove VMware tags from the given VMware objects.\n- Tag feature is introduced in vSphere 6 version, so this module is not supported in the earlier versions of vSphere.\n- All variables and VMware object names are case sensitive.\nauthor:\n- Abhijeet Kasurde (@Akasurde)\n- Frederic Van Reet (@GBrawl)\nnotes:\n- Tested on vSphere 6.5\nrequirements:\n- python >= 2.6\n- PyVmomi\n- vSphere Automation SDK\noptions:\n    tag_names:\n      description:\n      - List of tag(s) to be managed.\n      - User can also specify category name by specifying colon separated value. For example, \"category_name:tag_name\".\n      - User can also specify tag and category as dict, when tag or category contains colon.\n        See example for more information. Added in version 2.10.\n      - User can skip category name if you have unique tag names.\n      required: True\n      type: list\n    state:\n      description:\n      - If C(state) is set to C(add) or C(present) will add the tags to the existing tag list of the given object.\n      - If C(state) is set to C(remove) or C(absent) will remove the tags from the existing tag list of the given object.\n      - If C(state) is set to C(set) will replace the tags of the given objects with the user defined list of tags.\n      default: add\n      choices: [ present, absent, add, remove, set ]\n      type: str\n    object_type:\n      description:\n      - Type of object to work with.\n      required: True\n      choices: [ VirtualMachine, Datacenter, ClusterComputeResource, HostSystem, DistributedVirtualSwitch, DistributedVirtualPortgroup ]\n      type: str\n    object_name:\n      description:\n      - Name of the object to work with.\n      - For DistributedVirtualPortgroups the format should be \"switch_name:portgroup_name\"\n      required: True\n      type: str\nextends_documentation_fragment:\n- community.vmware.vmware_rest_client.documentation\n\n'''\n\nEXAMPLES = r'''\n- name: Add tags to a virtual machine\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - Sample_Tag_0002\n      - Category_0001:Sample_Tag_0003\n    object_name: Fedora_VM\n    object_type: VirtualMachine\n    state: add\n  delegate_to: localhost\n\n- name: Specify tag and category as dict\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - tag: tag_0001\n        category: cat_0001\n      - tag: tag_0002\n        category: cat_0002\n    object_name: Fedora_VM\n    object_type: VirtualMachine\n    state: add\n  delegate_to: localhost\n\n- name: Remove a tag from a virtual machine\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - Sample_Tag_0002\n    object_name: Fedora_VM\n    object_type: VirtualMachine\n    state: remove\n  delegate_to: localhost\n\n- name: Add tags to a distributed virtual switch\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - Sample_Tag_0003\n    object_name: Switch_0001\n    object_type: DistributedVirtualSwitch\n    state: add\n  delegate_to: localhost\n\n- name: Add tags to a distributed virtual portgroup\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - Sample_Tag_0004\n    object_name: Switch_0001:Portgroup_0001\n    object_type: DistributedVirtualPortgroup\n    state: add\n  delegate_to: localhost\n'''\n\nRETURN = r'''\ntag_status:\n    description: metadata about tags related to object configuration\n    returned: on success\n    type: list\n    sample: {\n        \"current_tags\": [\n            \"backup\",\n            \"security\"\n        ],\n        \"desired_tags\": [\n            \"security\"\n        ],\n        \"previous_tags\": [\n            \"backup\",\n            \"security\"\n        ]\n    }\n'''\nfrom ansible.module_utils.basic import AnsibleModule\nfrom ansible_collections.community.vmware.plugins.module_utils.vmware_rest_client import VmwareRestClient\nfrom ansible_collections.community.vmware.plugins.module_utils.vmware import (PyVmomi, find_dvs_by_name, find_dvspg_by_name)\ntry:\n    from com.vmware.vapi.std_client import DynamicID\n    from com.vmware.vapi.std.errors_client import Error\nexcept ImportError:\n    pass\n\n\nclass VmwareTagManager(VmwareRestClient):\n    def __init__(self, module):\n        \"\"\"\n        Constructor\n        \"\"\"\n        super(VmwareTagManager, self).__init__(module)\n        self.pyv = PyVmomi(module=module)\n\n        self.object_type = self.params.get('object_type')\n        self.object_name = self.params.get('object_name')\n        self.managed_object = None\n\n        if self.object_type == 'VirtualMachine':\n            self.managed_object = self.pyv.get_vm_or_template(self.object_name)\n\n        if self.object_type == 'Datacenter':\n            self.managed_object = self.pyv.find_datacenter_by_name(self.object_name)\n\n        if self.object_type == 'ClusterComputeResource':\n            self.managed_object = self.pyv.find_cluster_by_name(self.object_name)\n\n        if self.object_type == 'HostSystem':\n            self.managed_object = self.pyv.find_hostsystem_by_name(self.object_name)\n\n        if self.object_type == 'DistributedVirtualSwitch':\n            self.managed_object = find_dvs_by_name(self.pyv.content, self.object_name)\n            self.object_type = 'VmwareDistributedVirtualSwitch'\n\n        if self.object_type == 'DistributedVirtualPortgroup':\n            dvs_name, pg_name = self.object_name.split(\":\", 1)\n            dv_switch = find_dvs_by_name(self.pyv.content, dvs_name)\n            if dv_switch is None:\n                self.module.fail_json(msg=\"A distributed virtual switch with name %s does not exist\" % dvs_name)\n            self.managed_object = find_dvspg_by_name(dv_switch, pg_name)\n\n        if self.managed_object is None:\n            self.module.fail_json(msg=\"Failed to find the managed object for %s with type %s\" % (self.object_name, self.object_type))\n\n        if not hasattr(self.managed_object, '_moId'):\n            self.module.fail_json(msg=\"Unable to find managed object id for %s managed object\" % self.object_name)\n\n        self.dynamic_managed_object = DynamicID(type=self.object_type, id=self.managed_object._moId)\n\n        self.tag_service = self.api_client.tagging.Tag\n        self.category_service = self.api_client.tagging.Category\n        self.tag_association_svc = self.api_client.tagging.TagAssociation\n\n        self.tag_names = self.params.get('tag_names')\n\n    def ensure_state(self):\n        \"\"\"\n        Manage the internal state of tags\n\n        \"\"\"\n        results = dict(\n            changed=False,\n            tag_status=dict(),\n        )\n        changed = False\n        action = self.params.get('state')\n        available_tag_obj = self.get_tags_for_object(tag_service=self.tag_service,\n                                                     tag_assoc_svc=self.tag_association_svc,\n                                                     dobj=self.dynamic_managed_object)\n\n        _temp_prev_tags = [\"%s:%s\" % (tag['category_name'], tag['name']) for tag in self.get_tags_for_dynamic_obj(self.dynamic_managed_object)]\n        results['tag_status']['previous_tags'] = _temp_prev_tags\n        results['tag_status']['desired_tags'] = self.tag_names\n\n        # Check if category and tag combination exists as per user request\n        removed_tags_for_set = False\n        for tag in self.tag_names:\n            category_obj, category_name, tag_name = None, None, None\n            if isinstance(tag, dict):\n                tag_name = tag.get('tag')\n                category_name = tag.get('category')\n                if category_name:\n                    # User specified category\n                    category_obj = self.search_svc_object_by_name(self.category_service, category_name)\n                    if not category_obj:\n                        self.module.fail_json(msg=\"Unable to find the category %s\" % category_name)\n            elif isinstance(tag, str):\n                if \":\" in tag:\n                    # User specified category\n                    category_name, tag_name = tag.split(\":\", 1)\n                    category_obj = self.search_svc_object_by_name(self.category_service, category_name)\n                    if not category_obj:\n                        self.module.fail_json(msg=\"Unable to find the category %s\" % category_name)\n                else:\n                    # User specified only tag\n                    tag_name = tag\n\n            if category_name:\n                tag_obj = self.get_tag_by_category(tag_name=tag_name, category_name=category_name)\n            else:\n                tag_obj = self.get_tag_by_name(tag_name=tag_name)\n\n            if not tag_obj:\n                self.module.fail_json(msg=\"Unable to find the tag %s\" % tag_name)\n\n            if action in ('add', 'present'):\n                if tag_obj not in available_tag_obj:\n                    # Tag is not already applied\n                    try:\n                        self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)\n                        changed = True\n                    except Error as error:\n                        self.module.fail_json(msg=\"%s\" % self.get_error_message(error))\n\n            elif action == 'set':\n                # Remove all tags first\n                try:\n                    if not removed_tags_for_set:\n                        for av_tag in available_tag_obj:\n                            self.tag_association_svc.detach(tag_id=av_tag.id, object_id=self.dynamic_managed_object)\n                        removed_tags_for_set = True\n                    self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)\n                    changed = True\n                except Error as error:\n                    self.module.fail_json(msg=\"%s\" % self.get_error_message(error))\n\n            elif action in ('remove', 'absent'):\n                if tag_obj in available_tag_obj:\n                    try:\n                        self.tag_association_svc.detach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)\n                        changed = True\n                    except Error as error:\n                        self.module.fail_json(msg=\"%s\" % self.get_error_message(error))\n\n        _temp_curr_tags = [\"%s:%s\" % (tag['category_name'], tag['name']) for tag in self.get_tags_for_dynamic_obj(self.dynamic_managed_object)]\n        results['tag_status']['current_tags'] = _temp_curr_tags\n        results['changed'] = changed\n        self.module.exit_json(**results)\n\n\ndef main():\n    argument_spec = VmwareRestClient.vmware_client_argument_spec()\n    argument_spec.update(\n        tag_names=dict(type='list', required=True),\n        state=dict(type='str', choices=['absent', 'add', 'present', 'remove', 'set'], default='add'),\n        object_name=dict(type='str', required=True),\n        object_type=dict(type='str', required=True, choices=['VirtualMachine', 'Datacenter', 'ClusterComputeResource',\n                                                             'HostSystem', 'DistributedVirtualSwitch',\n                                                             'DistributedVirtualPortgroup']),\n    )\n    module = AnsibleModule(argument_spec=argument_spec)\n\n    vmware_tag_manager = VmwareTagManager(module)\n    vmware_tag_manager.ensure_state()\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/vmware_tag_manager.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n# Copyright: (c) 2018, Ansible Project\n# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.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_tag_manager\nshort_description: Manage association of VMware tags with VMware objects\ndescription:\n- This module can be used to assign / remove VMware tags from the given VMware objects.\n- Tag feature is introduced in vSphere 6 version, so this module is not supported in the earlier versions of vSphere.\n- All variables and VMware object names are case sensitive.\nauthor:\n- Abhijeet Kasurde (@Akasurde)\n- Frederic Van Reet (@GBrawl)\nnotes:\n- Tested on vSphere 6.5\nrequirements:\n- python >= 2.6\n- PyVmomi\n- vSphere Automation SDK\noptions:\n    tag_names:\n      description:\n      - List of tag(s) to be managed.\n      - User can also specify category name by specifying colon separated value. For example, \"category_name:tag_name\".\n      - User can also specify tag and category as dict, when tag or category contains colon.\n        See example for more information. Added in version 2.10.\n      - User can skip category name if you have unique tag names.\n      required: True\n      type: list\n    state:\n      description:\n      - If C(state) is set to C(add) or C(present) will add the tags to the existing tag list of the given object.\n      - If C(state) is set to C(remove) or C(absent) will remove the tags from the existing tag list of the given object.\n      - If C(state) is set to C(set) will replace the tags of the given objects with the user defined list of tags.\n      default: add\n      choices: [ present, absent, add, remove, set ]\n      type: str\n    object_type:\n      description:\n      - Type of object to work with.\n      required: True\n      choices: [ VirtualMachine, Datacenter, ClusterComputeResource, HostSystem, DistributedVirtualSwitch, DistributedVirtualPortgroup, Datastore, DatastoreCluster, ResourcePool, Folder ]\n      type: str\n    object_name:\n      description:\n      - Name of the object to work with.\n      - For DistributedVirtualPortgroups the format should be \"switch_name:portgroup_name\"\n      required: True\n      type: str\nextends_documentation_fragment:\n- community.vmware.vmware_rest_client.documentation\n\n'''\n\nEXAMPLES = r'''\n- name: Add tags to a virtual machine\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - Sample_Tag_0002\n      - Category_0001:Sample_Tag_0003\n    object_name: Fedora_VM\n    object_type: VirtualMachine\n    state: add\n  delegate_to: localhost\n\n- name: Specify tag and category as dict\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - tag: tag_0001\n        category: cat_0001\n      - tag: tag_0002\n        category: cat_0002\n    object_name: Fedora_VM\n    object_type: VirtualMachine\n    state: add\n  delegate_to: localhost\n\n- name: Remove a tag from a virtual machine\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - Sample_Tag_0002\n    object_name: Fedora_VM\n    object_type: VirtualMachine\n    state: remove\n  delegate_to: localhost\n\n- name: Add tags to a distributed virtual switch\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - Sample_Tag_0003\n    object_name: Switch_0001\n    object_type: DistributedVirtualSwitch\n    state: add\n  delegate_to: localhost\n\n- name: Add tags to a distributed virtual portgroup\n  community.vmware.vmware_tag_manager:\n    hostname: '{{ vcenter_hostname }}'\n    username: '{{ vcenter_username }}'\n    password: '{{ vcenter_password }}'\n    validate_certs: no\n    tag_names:\n      - Sample_Tag_0004\n    object_name: Switch_0001:Portgroup_0001\n    object_type: DistributedVirtualPortgroup\n    state: add\n  delegate_to: localhost\n'''\n\nRETURN = r'''\ntag_status:\n    description: metadata about tags related to object configuration\n    returned: on success\n    type: list\n    sample: {\n        \"current_tags\": [\n            \"backup\",\n            \"security\"\n        ],\n        \"desired_tags\": [\n            \"security\"\n        ],\n        \"previous_tags\": [\n            \"backup\",\n            \"security\"\n        ]\n    }\n'''\nfrom ansible.module_utils.basic import AnsibleModule\nfrom ansible_collections.community.vmware.plugins.module_utils.vmware_rest_client import VmwareRestClient\nfrom ansible_collections.community.vmware.plugins.module_utils.vmware import (PyVmomi, find_dvs_by_name, find_dvspg_by_name)\ntry:\n    from com.vmware.vapi.std_client import DynamicID\n    from com.vmware.vapi.std.errors_client import Error\nexcept ImportError:\n    pass\n\n\nclass VmwareTagManager(VmwareRestClient):\n    def __init__(self, module):\n        \"\"\"\n        Constructor\n        \"\"\"\n        super(VmwareTagManager, self).__init__(module)\n        self.pyv = PyVmomi(module=module)\n\n        self.object_type = self.params.get('object_type')\n        self.object_name = self.params.get('object_name')\n        self.managed_object = None\n\n        if self.object_type == 'VirtualMachine':\n            self.managed_object = self.pyv.get_vm_or_template(self.object_name)\n\n        if self.object_type == 'Folder':\n            self.managed_object = self.pyv.find_folder_by_name(self.object_name)\n\n        if self.object_type == 'Datacenter':\n            self.managed_object = self.pyv.find_datacenter_by_name(self.object_name)\n\n        if self.object_type == 'Datastore':\n            self.managed_object = self.pyv.find_datastore_by_name(self.object_name)\n\n        if self.object_type == 'DatastoreCluster':\n            self.managed_object = self.pyv.find_datastore_cluster_by_name(self.object_name)\n            self.object_type = 'StoragePod'\n\n        if self.object_type == 'ClusterComputeResource':\n            self.managed_object = self.pyv.find_cluster_by_name(self.object_name)\n\n        if self.object_type == 'ResourcePool':\n            self.managed_object = self.pyv.find_resource_pool_by_name(self.object_name)\n\n        if self.object_type == 'HostSystem':\n            self.managed_object = self.pyv.find_hostsystem_by_name(self.object_name)\n\n        if self.object_type == 'DistributedVirtualSwitch':\n            self.managed_object = find_dvs_by_name(self.pyv.content, self.object_name)\n            self.object_type = 'VmwareDistributedVirtualSwitch'\n\n        if self.object_type == 'DistributedVirtualPortgroup':\n            dvs_name, pg_name = self.object_name.split(\":\", 1)\n            dv_switch = find_dvs_by_name(self.pyv.content, dvs_name)\n            if dv_switch is None:\n                self.module.fail_json(msg=\"A distributed virtual switch with name %s does not exist\" % dvs_name)\n            self.managed_object = find_dvspg_by_name(dv_switch, pg_name)\n\n        if self.managed_object is None:\n            self.module.fail_json(msg=\"Failed to find the managed object for %s with type %s\" % (self.object_name, self.object_type))\n\n        if not hasattr(self.managed_object, '_moId'):\n            self.module.fail_json(msg=\"Unable to find managed object id for %s managed object\" % self.object_name)\n\n        self.dynamic_managed_object = DynamicID(type=self.object_type, id=self.managed_object._moId)\n\n        self.tag_service = self.api_client.tagging.Tag\n        self.category_service = self.api_client.tagging.Category\n        self.tag_association_svc = self.api_client.tagging.TagAssociation\n\n        self.tag_names = self.params.get('tag_names')\n\n    def ensure_state(self):\n        \"\"\"\n        Manage the internal state of tags\n\n        \"\"\"\n        results = dict(\n            changed=False,\n            tag_status=dict(),\n        )\n        changed = False\n        action = self.params.get('state')\n        available_tag_obj = self.get_tags_for_object(tag_service=self.tag_service,\n                                                     tag_assoc_svc=self.tag_association_svc,\n                                                     dobj=self.dynamic_managed_object)\n\n        _temp_prev_tags = [\"%s:%s\" % (tag['category_name'], tag['name']) for tag in self.get_tags_for_dynamic_obj(self.dynamic_managed_object)]\n        results['tag_status']['previous_tags'] = _temp_prev_tags\n        results['tag_status']['desired_tags'] = self.tag_names\n\n        # Check if category and tag combination exists as per user request\n        removed_tags_for_set = False\n        for tag in self.tag_names:\n            category_obj, category_name, tag_name = None, None, None\n            if isinstance(tag, dict):\n                tag_name = tag.get('tag')\n                category_name = tag.get('category')\n                if category_name:\n                    # User specified category\n                    category_obj = self.search_svc_object_by_name(self.category_service, category_name)\n                    if not category_obj:\n                        self.module.fail_json(msg=\"Unable to find the category %s\" % category_name)\n            elif isinstance(tag, str):\n                if \":\" in tag:\n                    # User specified category\n                    category_name, tag_name = tag.split(\":\", 1)\n                    category_obj = self.search_svc_object_by_name(self.category_service, category_name)\n                    if not category_obj:\n                        self.module.fail_json(msg=\"Unable to find the category %s\" % category_name)\n                else:\n                    # User specified only tag\n                    tag_name = tag\n\n            if category_name:\n                tag_obj = self.get_tag_by_category(tag_name=tag_name, category_name=category_name)\n            else:\n                tag_obj = self.get_tag_by_name(tag_name=tag_name)\n\n            if not tag_obj:\n                self.module.fail_json(msg=\"Unable to find the tag %s\" % tag_name)\n\n            if action in ('add', 'present'):\n                if tag_obj not in available_tag_obj:\n                    # Tag is not already applied\n                    try:\n                        self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)\n                        changed = True\n                    except Error as error:\n                        self.module.fail_json(msg=\"%s\" % self.get_error_message(error))\n\n            elif action == 'set':\n                # Remove all tags first\n                try:\n                    if not removed_tags_for_set:\n                        for av_tag in available_tag_obj:\n                            self.tag_association_svc.detach(tag_id=av_tag.id, object_id=self.dynamic_managed_object)\n                        removed_tags_for_set = True\n                    self.tag_association_svc.attach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)\n                    changed = True\n                except Error as error:\n                    self.module.fail_json(msg=\"%s\" % self.get_error_message(error))\n\n            elif action in ('remove', 'absent'):\n                if tag_obj in available_tag_obj:\n                    try:\n                        self.tag_association_svc.detach(tag_id=tag_obj.id, object_id=self.dynamic_managed_object)\n                        changed = True\n                    except Error as error:\n                        self.module.fail_json(msg=\"%s\" % self.get_error_message(error))\n\n        _temp_curr_tags = [\"%s:%s\" % (tag['category_name'], tag['name']) for tag in self.get_tags_for_dynamic_obj(self.dynamic_managed_object)]\n        results['tag_status']['current_tags'] = _temp_curr_tags\n        results['changed'] = changed\n        self.module.exit_json(**results)\n\n\ndef main():\n    argument_spec = VmwareRestClient.vmware_client_argument_spec()\n    argument_spec.update(\n        tag_names=dict(type='list', required=True),\n        state=dict(type='str', choices=['absent', 'add', 'present', 'remove', 'set'], default='add'),\n        object_name=dict(type='str', required=True),\n        object_type=dict(type='str', required=True, choices=['VirtualMachine', 'Datacenter', 'ClusterComputeResource',\n                                                             'HostSystem', 'DistributedVirtualSwitch',\n                                                             'DistributedVirtualPortgroup', 'Datastore', 'ResourcePool',\n                                                             'Folder', 'DatastoreCluster']),\n    )\n    module = AnsibleModule(argument_spec=argument_spec)\n\n    vmware_tag_manager = VmwareTagManager(module)\n    vmware_tag_manager.ensure_state()\n\n\nif __name__ == '__main__':\n    main()\n", "path": "plugins/modules/vmware_tag_manager.py"}]}