-
Notifications
You must be signed in to change notification settings - Fork 339
/
Copy pathvmware_content_deploy_template.py
464 lines (429 loc) · 16.1 KB
/
vmware_content_deploy_template.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Ansible Project
# Copyright: (c) 2019, Pavan Bidkar <[email protected]>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = r'''
---
module: vmware_content_deploy_template
short_description: Deploy Virtual Machine from template stored in content library.
description:
- Module to deploy virtual machine from template in content library.
- Content Library feature is introduced in vSphere 6.0 version.
- vmtx templates feature is introduced in vSphere 67U1 and APIs for clone template from content library in 67U2.
- This module does not work with vSphere version older than 67U2.
- All variables and VMware object names are case sensitive.
author:
- Pavan Bidkar (@pgbidkar)
notes:
- Tested on vSphere 6.7 U3
requirements:
- python >= 2.6
- PyVmomi
- vSphere Automation SDK
options:
log_level:
description:
- The level of logging desired in this module.
type: str
required: False
default: 'normal'
choices: [ 'debug', 'info', 'normal' ]
version_added: '1.9.0'
template:
description:
- The name of template from which VM to be deployed.
type: str
required: True
aliases: ['template_src']
library:
description:
- The name of the content library from where the template resides.
type: str
required: False
aliases: ['content_library', 'content_library_src']
name:
description:
- The name of the VM to be deployed.
type: str
required: True
aliases: ['vm_name']
datacenter:
description:
- Name of the datacenter, where VM to be deployed.
type: str
required: True
datastore:
description:
- Name of the datastore to store deployed VM and disk.
- Required if I(datastore_cluster) is not provided.
type: str
required: False
datastore_cluster:
description:
- Name of the datastore cluster to store deployed VM and disk.
- Please make sure Storage DRS is active for recommended datastore from the given datastore cluster.
- If Storage DRS is not enabled, datastore with largest free storage space is selected.
- Required if I(datastore) is not provided.
type: str
required: False
version_added: '1.7.0'
folder:
description:
- Name of the folder in datacenter in which to place deployed VM.
type: str
default: 'vm'
host:
description:
- Name of the ESX Host in datacenter in which to place deployed VM.
- The host has to be a member of the cluster that contains the resource pool.
- Required with I(resource_pool) to find resource pool details. This will be used as additional
information when there are resource pools with same name.
type: str
required: False
resource_pool:
description:
- Name of the resource pool in datacenter in which to place deployed VM.
- Required if I(cluster) is not specified.
- For default or non-unique resource pool names, specify I(host) and I(cluster).
- C(Resources) is the default name of resource pool.
type: str
required: False
cluster:
description:
- Name of the cluster in datacenter in which to place deployed VM.
- Required if I(resource_pool) is not specified.
type: str
required: False
state:
description:
- The state of Virtual Machine deployed from template in content library.
- If set to C(present) and VM does not exists, then VM is created.
- If set to C(present) and VM exists, no action is taken.
- If set to C(poweredon) and VM does not exists, then VM is created with powered on state.
- If set to C(poweredon) and VM exists, no action is taken.
type: str
required: False
default: 'present'
choices: [ 'present', 'poweredon' ]
extends_documentation_fragment:
- community.vmware.vmware_rest_client.documentation
'''
EXAMPLES = r'''
- name: Deploy Virtual Machine from template in content library
community.vmware.vmware_content_deploy_template:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
template: rhel_test_template
datastore: Shared_NFS_Volume
folder: vm
datacenter: Sample_DC_1
name: Sample_VM
resource_pool: test_rp
state: present
delegate_to: localhost
- name: Deploy Virtual Machine from template in content library with PowerON State
community.vmware.vmware_content_deploy_template:
hostname: '{{ vcenter_hostname }}'
username: '{{ vcenter_username }}'
password: '{{ vcenter_password }}'
template: rhel_test_template
content_library: test_content_library
datastore: Shared_NFS_Volume
folder: vm
datacenter: Sample_DC_1
name: Sample_VM
resource_pool: test_rp
state: poweredon
delegate_to: localhost
'''
RETURN = r'''
vm_deploy_info:
description: Virtual machine deployment message and vm_id
returned: on success
type: dict
sample: {
"msg": "Deployed Virtual Machine 'Sample_VM'.",
"vm_id": "vm-1009"
}
'''
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.vmware.plugins.module_utils.vmware_rest_client import VmwareRestClient
from ansible_collections.community.vmware.plugins.module_utils.vmware import PyVmomi
from ansible.module_utils._text import to_native
HAS_VAUTOMATION_PYTHON_SDK = False
try:
from com.vmware.vcenter.vm_template_client import LibraryItems
from com.vmware.vapi.std.errors_client import Error
HAS_VAUTOMATION_PYTHON_SDK = True
except ImportError:
pass
class VmwareContentDeployTemplate(VmwareRestClient):
def __init__(self, module):
"""Constructor."""
super(VmwareContentDeployTemplate, self).__init__(module)
# Initialize member variables
self.module = module
self._pyv = PyVmomi(module=module)
self._template_service = self.api_client.vcenter.vm_template.LibraryItems
self._datacenter_id = None
self._datastore_id = None
self._library_item_id = None
self._folder_id = None
self._host_id = None
self._cluster_id = None
self._resourcepool_id = None
self.result = {}
# Turn on debug if not specified, but ANSIBLE_DEBUG is set
if self.module._debug:
self.warn('Enable debug output because ANSIBLE_DEBUG was set.')
self.params['log_level'] = 'debug'
self.log_level = self.params['log_level']
if self.log_level == 'debug':
# Turn on debugging
self.result['debug'] = {}
# Get parameters
self.template = self.params.get('template')
self.library = self.params.get('library')
self.vm_name = self.params.get('name')
self.datacenter = self.params.get('datacenter')
self.datastore = self.params.get('datastore')
self.datastore_cluster = self.params.get('datastore_cluster')
self.folder = self.params.get('folder')
self.resourcepool = self.params.get('resource_pool')
self.cluster = self.params.get('cluster')
self.host = self.params.get('host')
vm = self._pyv.get_vm()
if vm:
self.result['vm_deploy_info'] = dict(
msg="Virtual Machine '%s' already Exists." % self.vm_name,
vm_id=vm._moId,
)
self._fail(msg="Virtual Machine deployment failed")
def deploy_vm_from_template(self, power_on=False):
# Find the datacenter by the given datacenter name
self._datacenter_id = self.get_datacenter_by_name(self.datacenter)
if not self._datacenter_id:
self._fail(msg="Failed to find the datacenter %s" % self.datacenter)
# Find the datastore by the given datastore name
if self.datastore:
self._datastore_id = self.get_datastore_by_name(self.datacenter, self.datastore)
if not self._datastore_id:
self._fail(msg="Failed to find the datastore %s" % self.datastore)
# Find the datastore by the given datastore cluster name
if self.datastore_cluster and not self._datastore_id:
dsc = self._pyv.find_datastore_cluster_by_name(self.datastore_cluster)
if dsc:
self.datastore = self._pyv.get_recommended_datastore(dsc)
self._datastore_id = self.get_datastore_by_name(self.datacenter, self.datastore)
else:
self._fail(msg="Failed to find the datastore cluster %s" % self.datastore_cluster)
if not self._datastore_id:
self._fail(msg="Failed to find the datastore using either datastore or datastore cluster")
# Find the LibraryItem (Template) by the given LibraryItem name
if self.library:
self._library_item_id = self.get_library_item_from_content_library_name(
self.template, self.library
)
if not self._library_item_id:
self._fail(msg="Failed to find the library Item %s in content library %s" % (self.template, self.library))
else:
self._library_item_id = self.get_library_item_by_name(self.template)
if not self._library_item_id:
self._fail(msg="Failed to find the library Item %s" % self.template)
# Find the folder by the given FQPN folder name
# The FQPN is I(datacenter)/I(folder type)/folder name/... for
# example Lab/vm/someparent/myfolder is a vm folder in the Lab datacenter.
folder_obj = self._pyv.find_folder_by_fqpn(self.folder, self.datacenter, folder_type='vm')
if folder_obj:
self._folder_id = folder_obj._moId
if not self._folder_id:
self._fail(msg="Failed to find the folder %s" % self.folder)
# Find the Host by the given name
if self.host:
self._host_id = self.get_host_by_name(self.datacenter, self.host)
if not self._host_id:
self._fail(msg="Failed to find the Host %s" % self.host)
# Find the Cluster by the given Cluster name
if self.cluster:
self._cluster_id = self.get_cluster_by_name(self.datacenter, self.cluster)
if not self._cluster_id:
self._fail(msg="Failed to find the Cluster %s" % self.cluster)
cluster_obj = self.api_client.vcenter.Cluster.get(self._cluster_id)
self._resourcepool_id = cluster_obj.resource_pool
# Find the resourcepool by the given resourcepool name
if self.resourcepool and self.cluster and self.host:
self._resourcepool_id = self.get_resource_pool_by_name(self.datacenter, self.resourcepool, self.cluster, self.host)
if not self._resourcepool_id:
self._fail(msg="Failed to find the resource_pool %s" % self.resourcepool)
# Create VM placement specs
self.placement_spec = LibraryItems.DeployPlacementSpec(folder=self._folder_id)
if self._host_id:
self.placement_spec.host = self._host_id
if self._resourcepool_id:
self.placement_spec.resource_pool = self._resourcepool_id
if self._cluster_id:
self.placement_spec.cluster = self._cluster_id
self.vm_home_storage_spec = LibraryItems.DeploySpecVmHomeStorage(
datastore=to_native(self._datastore_id)
)
self.disk_storage_spec = LibraryItems.DeploySpecDiskStorage(
datastore=to_native(self._datastore_id)
)
self.deploy_spec = LibraryItems.DeploySpec(
name=self.vm_name,
placement=self.placement_spec,
vm_home_storage=self.vm_home_storage_spec,
disk_storage=self.disk_storage_spec,
powered_on=power_on
)
vm_id = ''
try:
vm_id = self._template_service.deploy(self._library_item_id, self.deploy_spec)
except Error as error:
self._fail(msg="%s" % self.get_error_message(error))
except Exception as err:
self._fail(msg="%s" % to_native(err))
if not vm_id:
self.result['vm_deploy_info'] = dict(
msg="Virtual Machine deployment failed",
vm_id=''
)
self._fail(msg="Virtual Machine deployment failed")
self.result['changed'] = True
self.result['vm_deploy_info'] = dict(
msg="Deployed Virtual Machine '%s'." % self.vm_name,
vm_id=vm_id,
)
self._exit()
#
# Wrap AnsibleModule methods
#
def _mod_debug(self):
if self.log_level == 'debug':
self.result['debug'] = dict(
datacenter_id=self._datacenter_id,
datastore_id=self._datastore_id,
library_item_id=self._library_item_id,
folder_id=self._folder_id,
host_id=self._host_id,
cluster_id=self._cluster_id,
resourcepool_id=self._resourcepool_id
)
def _fail(self, msg):
self._mod_debug()
self.module.fail_json(msg=msg, **self.result)
def _exit(self):
self._mod_debug()
self.module.exit_json(**self.result)
def main():
argument_spec = VmwareRestClient.vmware_client_argument_spec()
argument_spec.update(
log_level=dict(
type='str',
choices=[
'debug',
'info',
'normal',
],
default='normal'
),
state=dict(
type='str',
choices=[
'present',
'poweredon'
],
default='present'
),
template=dict(
type='str',
aliases=[
'template_src'
],
required=True
),
library=dict(
type='str',
aliases=[
'content_library',
'content_library_src',
],
required=False
),
name=dict(
type='str',
aliases=[
'vm_name'
],
required=True,
),
datacenter=dict(
type='str',
required=True
),
datastore=dict(
type='str',
required=False
),
datastore_cluster=dict(
type='str',
required=False
),
folder=dict(
type='str',
default='vm'
),
host=dict(
type='str',
required=False
),
resource_pool=dict(
type='str',
required=False
),
cluster=dict(
type='str',
required=False
),
)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
required_one_of=[
['datastore', 'datastore_cluster'],
['host', 'cluster'],
],
)
result = {'failed': False, 'changed': False}
vmware_contentlib_create = VmwareContentDeployTemplate(module)
if module.params['state'] == 'present':
if module.check_mode:
result.update(
vm_name=module.params['name'],
changed=True,
desired_operation='Create VM with PowerOff State',
)
module.exit_json(**result)
vmware_contentlib_create.deploy_vm_from_template()
elif module.params['state'] == 'poweredon':
if module.check_mode:
result.update(
vm_name=module.params['name'],
changed=True,
desired_operation='Create VM with PowerON State',
)
module.exit_json(**result)
vmware_contentlib_create.deploy_vm_from_template(power_on=True)
else:
result.update(
vm_name=module.params['name'],
changed=False,
desired_operation="State '%s' is not implemented" % module.params['state']
)
module.fail_json(**result)
if __name__ == '__main__':
main()