This repository was archived by the owner on Mar 6, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 187
Expand file tree
/
Copy pathvapp.py
More file actions
2178 lines (1850 loc) · 92.4 KB
/
Copy pathvapp.py
File metadata and controls
2178 lines (1850 loc) · 92.4 KB
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
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# VMware vCloud Director Python SDK
# Copyright (c) 2014-2018 VMware, Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from copy import deepcopy
from lxml import etree
from lxml import objectify
from pyvcloud.vcd.acl import Acl
from pyvcloud.vcd.client import E
from pyvcloud.vcd.client import E_OVF
from pyvcloud.vcd.client import EntityType
from pyvcloud.vcd.client import FenceMode
from pyvcloud.vcd.client import find_link
from pyvcloud.vcd.client import MetadataDomain
from pyvcloud.vcd.client import MetadataValueType
from pyvcloud.vcd.client import MetadataVisibility
from pyvcloud.vcd.client import NSMAP
from pyvcloud.vcd.client import RelationType
from pyvcloud.vcd.client import VCLOUD_STATUS_MAP
from pyvcloud.vcd.exceptions import EntityNotFoundException
from pyvcloud.vcd.exceptions import InvalidParameterException
from pyvcloud.vcd.exceptions import InvalidStateException
from pyvcloud.vcd.exceptions import OperationNotSupportedException
from pyvcloud.vcd.metadata import Metadata
from pyvcloud.vcd.utils import cidr_to_netmask
from pyvcloud.vcd.utils import generate_compute_policy_tags
from pyvcloud.vcd.vdc import VDC
from pyvcloud.vcd.vm import VM
DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024
class VApp(object):
def __init__(self, client, name=None, href=None, resource=None):
"""Constructor for VApp objects.
:param pyvcloud.vcd.client.Client client: the client that will be used
to make REST calls to vCD.
:param str name: name of the entity.
:param str href: URI of the entity.
:param lxml.objectify.ObjectifiedElement resource: object containing
EntityType.VAPP XML data representing the vApp.
"""
self.client = client
self.name = name
if href is None and resource is None:
raise InvalidParameterException(
"VApp initialization failed as arguments are either invalid "
"or None")
self.href = href
self.resource = resource
if resource is not None:
self.name = resource.get('name')
self.href = resource.get('href')
def get_resource(self):
"""Fetches the XML representation of the vApp from vCD.
Will serve cached response if possible.
:return: object containing EntityType.VAPP XML data representing the
vApp.
:rtype: lxml.objectify.ObjectifiedElement
"""
if self.resource is None:
self.reload()
return self.resource
def reload(self):
"""Reloads the resource representation of the vApp.
This method should be called in between two method invocations on the
VApp object, if the former call changes the representation of the
vApp in vCD.
"""
self.resource = self.client.get_resource(self.href)
if self.resource is not None:
self.name = self.resource.get('name')
self.href = self.resource.get('href')
def get_primary_ip(self, vm_name):
"""Fetch the primary ip of a vm (in the vApp) identified by its name.
:param str vm_name: name of the vm whose primary ip we want to
retrieve.
:return: ip address of the named vm.
:rtype: str
:raises: Exception: if the named vm or its NIC information can't be
found.
"""
self.get_resource()
if hasattr(self.resource, 'Children') and \
hasattr(self.resource.Children, 'Vm'):
for vm in self.resource.Children.Vm:
if vm_name == vm.get('name'):
items = vm.xpath(
'ovf:VirtualHardwareSection/ovf:Item',
namespaces=NSMAP)
for item in items:
connection = item.find('rasd:Connection', NSMAP)
if connection is not None:
return connection.get('{' + NSMAP['vcloud'] +
'}ipAddress')
raise Exception('can\'t find ip address')
def get_admin_password(self, vm_name):
"""Fetch the admin password of a named vm in the vApp.
:param str vm_name: name of the vm whose admin password we want to
retrieve.
:return: admin password of the named vm.
:rtype: str
:raises: EntityNotFoundException: if the named vm can't be found.
"""
self.get_resource()
if hasattr(self.resource, 'Children') and \
hasattr(self.resource.Children, 'Vm'):
for vm in self.resource.Children.Vm:
if vm_name == vm.get('name'):
if hasattr(vm, 'GuestCustomizationSection') and \
hasattr(vm.GuestCustomizationSection, 'AdminPassword'):
return vm.GuestCustomizationSection.AdminPassword.text
raise EntityNotFoundException('Can\'t find admin password')
def get_metadata(self):
"""Fetch metadata of the vApp.
:return: an object containing EntityType.METADATA XML data which
represents the metadata associated with the vApp.
:rtype: lxml.objectify.ObjectifiedElement
"""
self.get_resource()
return self.client.get_linked_resource(
self.resource, RelationType.DOWN, EntityType.METADATA.value)
def set_metadata(self,
domain,
visibility,
key,
value,
metadata_type=MetadataValueType.STRING.value):
"""Add a new metadata entry to the vApp.
If an entry with the same key exists, it will be updated with the new
value.
:param str domain: a value of SYSTEM places this MetadataEntry in the
SYSTEM domain. Omit or leave empty to place this MetadataEntry in
the GENERAL domain.
:param str visibility: must be one of the values specified in
MetadataVisibility enum.
:param str key: an arbitrary key name. Length cannot exceed 256 UTF-8
characters.
:param str value: value of the metadata entry.
:param str metadata_type: one of the types specified in
client.MetadataValueType enum.
:return: an object of type EntityType.TASK XML which represents
the asynchronous task that is updating the metadata on the vApp.
"""
metadata = Metadata(client=self.client, resource=self.get_metadata())
return metadata.set_metadata(
key=key,
value=value,
domain=MetadataDomain(domain),
visibility=MetadataVisibility(visibility),
metadata_value_type=MetadataValueType(metadata_type),
use_admin_endpoint=False)
def set_multiple_metadata(self,
key_value_dict,
domain=MetadataDomain.GENERAL,
visibility=MetadataVisibility.READ_WRITE,
metadata_value_type=MetadataValueType.STRING):
"""Add multiple new metadata entries to the vApp.
If an entry with the same key exists, it will be updated with the new
value. All entries must have the same value type and will be written to
the same domain with identical visibility.
:param dict key_value_dict: a dict containing key-value pairs to be
added/updated.
:param client.MetadataDomain domain: domain where the new entries would
be put.
:param client.MetadataVisibility visibility: visibility of the metadata
entries.
:param client.MetadataValueType metadata_value_type:
:return: an object of type EntityType.TASK XML which represents
the asynchronous task that is updating the metadata on the vApp.
"""
metadata = Metadata(client=self.client, resource=self.get_metadata())
return metadata.set_multiple_metadata(
key_value_dict=key_value_dict,
domain=MetadataDomain(domain),
visibility=MetadataVisibility(visibility),
metadata_value_type=MetadataValueType(metadata_value_type),
use_admin_endpoint=False)
def remove_metadata(self, key, domain=MetadataDomain.GENERAL):
"""Remove a metadata entry from the vApp.
:param str key: key of the metadata to be removed.
:param client.MetadataDomain domain: domain of the entry to be removed.
:return: an object of type EntityType.TASK XML which represents
the asynchronous task that is deleting the metadata on the vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises: AccessForbiddenException: If there is no metadata entry
corresponding to the key provided.
"""
metadata = Metadata(client=self.client, resource=self.get_metadata())
return metadata.remove_metadata(
key=key, domain=domain, use_admin_endpoint=False)
def get_vm_moid(self, vm_name):
"""Fetch the moref of a named vm in the vApp.
:param str vm_name: name of the vm whose moref we want to retrieve.
:return: moref of the named vm.
:rtype: str
:raises: EntityNotFoundException: if the named vm can't be found.
"""
vapp = self.get_resource()
if hasattr(vapp, 'Children') and hasattr(vapp.Children, 'Vm'):
for vm in vapp.Children.Vm:
if vm.get('name') == vm_name:
env = vm.xpath('ovfenv:Environment', namespaces=NSMAP)
if len(env) > 0:
return env[0].get('{' + NSMAP['ve'] + '}vCenterId')
return None
def set_lease(self, deployment_lease=0, storage_lease=0):
"""Update lease settings of the vApp.
:param int deployment_lease: length of deployment lease in seconds.
:param int storage_lease: length of storage lease in seconds.
:return: an object containing EntityType.LEASE_SETTINGS XML data which
represents the updated lease settings of the vApp.
:rtype: lxml.objectify.ObjectifiedElement
"""
self.get_resource()
new_section = self.resource.LeaseSettingsSection
new_section.DeploymentLeaseInSeconds = deployment_lease
new_section.StorageLeaseInSeconds = storage_lease
objectify.deannotate(new_section)
etree.cleanup_namespaces(new_section)
return self.client.put_resource(
self.resource.get('href') + '/leaseSettingsSection/', new_section,
EntityType.LEASE_SETTINGS.value)
def get_lease(self):
"""Fetch lease settings data of the vApp.
:return: an dictionary containing LEASE_SETTINGS Data of the vApp.
:rtype: dict
"""
self.get_resource()
lease_setting = self.resource.LeaseSettingsSection
result = {}
if hasattr(lease_setting, 'DeploymentLeaseInSeconds'):
result['DeploymentLeaseInSeconds'] = \
lease_setting.DeploymentLeaseInSeconds
if hasattr(lease_setting, 'StorageLeaseInSeconds'):
result['StorageLeaseInSeconds'] = \
lease_setting.StorageLeaseInSeconds
if hasattr(lease_setting, 'StorageLeaseExpiration'):
result['StorageLeaseExpiration'] = \
lease_setting.StorageLeaseExpiration
return result
def change_owner(self, href):
"""Change the ownership of vApp to a given user.
:param str href: href of the new owner.
"""
self.get_resource()
new_owner = self.resource.Owner
new_owner.User.set('href', href)
objectify.deannotate(new_owner)
etree.cleanup_namespaces(new_owner)
return self.client.put_resource(
self.resource.get('href') + '/owner/', new_owner,
EntityType.OWNER.value)
def get_power_state(self, vapp_resource=None):
"""Returns the status of the vApp.
:param lxml.objectify.ObjectifiedElement vapp_resource: object
containing EntityType.VAPP XML data representing the vApp whose
power state we want to retrieve.
:return: The status of the vApp, the semantics of the value returned is
captured in pyvcloud.vcd.client.VCLOUD_STATUS_MAP
:rtype: int
"""
if vapp_resource is None:
vapp_resource = self.get_resource()
return int(vapp_resource.get('status'))
def is_powered_on(self, vapp_resource=None):
"""Checks if a vApp is powered on or not.
:param lxml.objectify.ObjectifiedElement vapp_resource: object
containing EntityType.VAPP XML data representing the vApp whose
power state we want to check.
:return: True if the vApp is powered on else False.
:rtype: bool
"""
return self.get_power_state(vapp_resource) == 4
def is_powered_off(self, vapp_resource=None):
"""Checks if a vApp is powered off or not.
:param lxml.objectify.ObjectifiedElement vapp_resource: object
containing EntityType.VAPP XML data representing the vApp whose
power state we want to check.
:return: True if the vApp is powered off else False.
:rtype: bool
"""
return self.get_power_state(vapp_resource) == 8
def is_suspended(self, vapp_resource=None):
"""Checks if a vApp is suspended or not.
:param lxml.objectify.ObjectifiedElement vapp_resource: object
containing EntityType.VAPP XML data representing the vApp whose
power state we want to check.
:return: True if the vApp is suspended else False.
:rtype: bool
"""
return self.get_power_state(vapp_resource) == 3
def is_deployed(self, vapp_resource=None):
"""Checks if a vApp is deployed or not.
:param lxml.objectify.ObjectifiedElement vapp_resource: object
containing EntityType.VAPP XML data representing the vApp whose
power state we want to check.
:return: True if the vApp is deployed else False.
:rtype: bool
"""
return self.get_power_state(vapp_resource) == 2
def _perform_power_operation(self,
rel,
operation_name,
media_type=None,
contents=None):
"""Perform a power operation on the vApp.
Perform one of the following power operations on the vApp.
Power on, Power off, Deploy, Undeploy, Shutdown, Reboot, Power reset.
:param pyvcloud.vcd.client.RelationType rel: relation of the link in
the vApp resource that will be triggered for the power operation.
:param str operation_name: name of the power operation to perform. This
value will be used while logging error messages (if any).
:param str media_type: media type of the link in
the vApp resource that will be triggered for the power operation.
:param lxml.objectify.ObjectifiedElement contents: payload for the
linked operation.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task that is tracking the power operation on the
vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises OperationNotSupportedException: if the power operation can't be
performed on the vApp.
"""
vapp_resource = self.get_resource()
try:
return self.client.post_linked_resource(vapp_resource, rel,
media_type, contents)
except OperationNotSupportedException:
power_state = self.get_power_state(vapp_resource)
raise OperationNotSupportedException(
'Can\'t {0} vApp. Current state of vApp: {1}.'.format(
operation_name, VCLOUD_STATUS_MAP[power_state]))
def deploy(self, power_on=None, force_customization=None):
"""Deploys the vApp.
Deploying the vApp will allocate all resources assigned to the vApp.
TODO: Add lease_deployment_seconds param after PR 2036925 is fixed.
https://jira.eng.vmware.com/browse/VCDA-465
:param bool power_on: specifies whether to power on/off vApp/vm
on deployment. True by default, unless otherwise specified.
:param str lease_deployment_seconds: deployment lease in seconds.
:param bool force_customization: True, instructs vCD to force
customization on deployment. False, no action is performed.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task that is deploying the vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises OperationNotSupportedException: if the vApp can't be deployed.
"""
deploy_vapp_params = E.DeployVAppParams()
if power_on is not None:
deploy_vapp_params.set('powerOn', str(power_on).lower())
if force_customization is not None:
deploy_vapp_params.set('forceCustomization',
str(force_customization).lower())
return self._perform_power_operation(
rel=RelationType.DEPLOY,
operation_name='deploy',
media_type=EntityType.DEPLOY.value,
contents=deploy_vapp_params)
def undeploy(self, action='default'):
"""Undeploys the vApp.
:param str action: specifies the action to be applied to all vms in the
vApp. Accepted values are
- powerOff: power off the virtual machines.
- suspend: suspend the virtual machines.
- shutdown: shut down the virtual machines.
- force: attempt to power off the virtual machines. Failures in
undeploying the virtual machine or associated networks are
ignored. All references to the vApp and its vms are removed
from the database.
- default: use the actions, order, and delay specified in the
StartupSection.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task that is undeploying the vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises OperationNotSupportedException: if the vApp can't be
undeployed.
"""
params = E.UndeployVAppParams(E.UndeployPowerAction(action))
return self._perform_power_operation(
rel=RelationType.UNDEPLOY,
operation_name='undeploy',
media_type=EntityType.UNDEPLOY.value,
contents=params)
def power_off(self):
"""Power off the vms in the vApp.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task that is powering off the vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises OperationNotSupportedException: if the vApp can't be powered
off.
"""
return self._perform_power_operation(
rel=RelationType.POWER_OFF, operation_name='power off')
def power_on(self):
"""Power on the vms in the vApp.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task that is powering on the vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises OperationNotSupportedException: if the vApp can't be powered
on.
"""
return self._perform_power_operation(
rel=RelationType.POWER_ON, operation_name='power on')
def shutdown(self):
"""Shutdown the vApp.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task shutting down the vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises OperationNotSupportedException: if the vApp can't be shutdown.
"""
return self._perform_power_operation(
rel=RelationType.POWER_SHUTDOWN, operation_name='shutdown')
def power_reset(self):
"""Power resets the vms in the vApp.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task resetting the vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises OperationNotSupportedException: if the vApp can't be power
reset.
"""
return self._perform_power_operation(
rel=RelationType.POWER_RESET, operation_name='power reset')
def reboot(self):
"""Reboots the vms in the vApp.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task rebooting the vApp.
:rtype: lxml.objectify.ObjectifiedElement
:raises OperationNotSupportedException: if the vApp can't be rebooted.
"""
return self._perform_power_operation(
rel=RelationType.POWER_REBOOT, operation_name='reboot')
def connect_vm(self, mode='DHCP', reset_mac_address=False):
self.get_resource()
if hasattr(self.resource, 'Children') and \
hasattr(self.resource.Children, 'Vm') and \
len(self.resource.Children.Vm) > 0:
network_name = 'none'
for nc in self.resource.NetworkConfigSection.NetworkConfig:
if nc.get('networkName') != 'none':
network_name = nc.get('networkName')
break
self.resource.Children.Vm[
0].NetworkConnectionSection.NetworkConnection.set(
'network', network_name)
self.resource.Children.Vm[
0].NetworkConnectionSection.NetworkConnection.IsConnected = \
E.IsConnected('true')
if reset_mac_address:
self.resource.Children.Vm[0].NetworkConnectionSection.\
NetworkConnection.MACAddress = E.MACAddress('')
self.resource.Children.Vm[0].NetworkConnectionSection.\
NetworkConnection.IpAddressAllocationMode = \
E.IpAddressAllocationMode(mode.upper())
return self.client.put_linked_resource(
self.resource.Children.Vm[0].NetworkConnectionSection,
RelationType.EDIT, EntityType.NETWORK_CONNECTION_SECTION.value,
self.resource.Children.Vm[0].NetworkConnectionSection)
def attach_disk_to_vm(self, disk_href, vm_name):
"""Attach an independent disk to the vm with the given name.
:param str disk_href: href of the disk to be attached.
:param str vm_name: name of the vm to which the disk will be attached.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task of attaching the disk.
:rtype: lxml.objectify.ObjectifiedElement
:raises: EntityNotFoundException: if the named vm or disk cannot be
located.
"""
disk_attach_or_detach_params = E.DiskAttachOrDetachParams(
E.Disk(type=EntityType.DISK.value, href=disk_href))
vm = self.get_vm(vm_name)
return self.client.post_linked_resource(
vm, RelationType.DISK_ATTACH,
EntityType.DISK_ATTACH_DETACH_PARAMS.value,
disk_attach_or_detach_params)
def detach_disk_from_vm(self, disk_href, vm_name):
"""Detach the independent disk from the vm with the given name.
:param str disk_href: href of the disk to be detached.
:param str vm_name: name of the vm to which the disk will be detached.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task of dettaching the disk.
:rtype: lxml.objectify.ObjectifiedElement
:raises: EntityNotFoundException: if the named vm or disk cannot be
located.
"""
disk_attach_or_detach_params = E.DiskAttachOrDetachParams(
E.Disk(type=EntityType.DISK.value, href=disk_href))
vm = self.get_vm(vm_name)
return self.client.post_linked_resource(
vm, RelationType.DISK_DETACH,
EntityType.DISK_ATTACH_DETACH_PARAMS.value,
disk_attach_or_detach_params)
def get_all_vms(self):
"""Retrieve all the vms in the vApp.
:return: a list of lxml.objectify.ObjectifiedElement objects, where
each object contains EntityType.VM XML data and represents one vm.
:rtype: empty list or generator object
"""
self.get_resource()
if hasattr(self.resource, 'Children') and \
hasattr(self.resource.Children, 'Vm') and \
len(self.resource.Children.Vm) > 0:
return self.resource.Children.Vm
else:
return []
def get_vm(self, vm_name):
"""Retrieve the vm with the given name in this vApp.
:param str vm_name: name of the vm to be retrieved.
:return: an object contains EntityType.VM XML data that represents the
vm.
:rtype: lxml.objectify.ObjectifiedElement
:raises: EntityNotFoundException: if the named vm could not be found.
"""
for vm in self.get_all_vms():
if vm.get('name') == vm_name:
return vm
raise EntityNotFoundException('Can\'t find VM \'%s\'' % vm_name)
def add_disk_to_vm(self, vm_name, disk_size, disk_controller="lsilogic"):
"""Add a virtual disk to a virtual machine in the vApp.
It assumes that the vm has already at least one virtual hard disk
and will attempt to create another one with similar characteristics.
:param str vm_name: name of the vm to be customized.
:param int disk_size: size of the disk to be added, in MBs.
:param str disk_controller: name of the disk controller.
:return: an object containing EntityType.TASK XML data which represents
the asynchronous task that is creating the disk.
:rtype: lxml.objectify.ObjectifiedElement
:raises: EntityNotFoundException: if the named vm cannot be located.
occurred.
"""
disk_index = 0
last_disk = None
vm = self.get_vm(vm_name)
scsi_controller_bus_type = 6
# supported disk controllers to add
# default disk controller and addresses are,
# VirtualSCSI address 0
# lsilogicsas address 1
# lsilogic address 2
# buslogic address 3
scsi_disk_controllers = [
"VirtualSCSI", "lsilogic", "lsilogicsas", "buslogic"]
is_disk_controller_present = False
disks = self.client.get_resource(
vm.get('href') + '/virtualHardwareSection/disks')
for disk in disks.Item:
element_name = str(disk['{' + NSMAP['rasd'] + '}ElementName'])
# recording last disk to update as a new disk
if disk['{' + NSMAP['rasd'] + '}Description'] == 'Hard disk':
last_disk = disk
disk_index += 1
# updating default disk controller's address with existing
# disk controller's address if any
if "SCSI Controller" in str(element_name):
addr = int(disk['{' + NSMAP['rasd'] + '}Address'])
bus_type = disk['{' + NSMAP['rasd'] + '}ResourceSubType']
expected_addr = scsi_disk_controllers.index(bus_type)
scsi_disk_controllers[addr], scsi_disk_controllers[
expected_addr] = scsi_disk_controllers[expected_addr],\
scsi_disk_controllers[addr]
# look for SCSI disk controller if present
resource_type = int(disk['{' + NSMAP['rasd'] + '}ResourceType'])
if resource_type == scsi_controller_bus_type:
if disk_controller == str(disk[
'{' + NSMAP['rasd'] + '}ResourceSubType']):
is_disk_controller_present = True
new_disk = deepcopy(last_disk)
instance_id = int(str(last_disk[
'{' + NSMAP['rasd'] + '}InstanceID'])) + 1
address = int(str(last_disk[
'{' + NSMAP['rasd'] + '}AddressOnParent'])) + 1
if not is_disk_controller_present:
# create a new SCSI controller
address = scsi_disk_controllers.index(disk_controller)
new_disk_controller = self._create_scsi_disk_controller(
last_disk, disk_controller)
new_disk_controller['{' + NSMAP['rasd'] + '}Address'] = address
new_disk['{' + NSMAP['rasd'] + '}Parent'] = new_disk_controller[
'{' + NSMAP['rasd'] + '}InstanceID']
disks.append(new_disk_controller)
# create a new disk
new_disk['{' + NSMAP['rasd'] + '}AddressOnParent'] = address
new_disk[
'{' + NSMAP['rasd'] + '}ElementName'] = 'Hard disk %s' % disk_index
new_disk[
'{' + NSMAP['rasd'] + '}InstanceID'] = instance_id
new_disk[
'{' + NSMAP['rasd'] + '}VirtualQuantity'] = disk_size * 1024 * 1024
new_disk['{' + NSMAP['rasd'] + '}HostResource'].set(
'{' + NSMAP['vcloud'] + '}capacity', str(disk_size))
new_disk['{' + NSMAP['rasd'] + '}HostResource'].set(
'{' + NSMAP['vcloud'] + '}busSubType', disk_controller)
new_disk['{' + NSMAP['rasd'] + '}HostResource'].set(
'{' + NSMAP['vcloud'] + '}busType', str(scsi_controller_bus_type))
disks.append(new_disk)
return self.client.put_resource(
vm.get('href') + '/virtualHardwareSection/disks',
disks, EntityType.RASD_ITEMS_LIST.value)
def _create_scsi_disk_controller(self, last_disk, disk_controller):
"""Create a new SCSI disk controller to a virtual machine in the vApp.
:param lxml.objectify.ObjectifiedElement last_disk: A disk object
already attached to a VM.
:param str disk_controller: name of the disk controller.
:rtype: lxml.objectify.ObjectifiedElement
"""
new_disk_controller = deepcopy(last_disk)
new_disk_controller['{' + NSMAP['rasd'] + '}ResourceType'] = 6
new_disk_controller[
'{' + NSMAP['rasd'] + '}ResourceSubType'] = disk_controller
return new_disk_controller
def get_access_settings(self):
"""Get the access settings of the vApp.
:return: an object containing EntityType.CONTROL_ACCESS_PARAMS which
represents the access control list of the vApp.
:rtype: lxml.objectify.ObjectifiedElement
"""
acl = Acl(self.client, self.get_resource())
return acl.get_access_settings()
def add_access_settings(self, access_settings_list=None):
"""Add access settings to the vApp.
:param list access_settings_list: list of dictionaries, where each
dictionary represents a single access setting. The dictionary
structure is as follows,
- type: (str): type of the subject. One of 'org' or 'user'.
- name: (str): name of the user or org.
- access_level: (str): access_level of the particular subject.
Allowed values are 'ReadOnly', 'Change' or 'FullControl'.
:return: an object containing EntityType.CONTROL_ACCESS_PARAMS XML
data representing the updated Access Control List of the vApp.
:rtype: lxml.objectify.ObjectifiedElement
"""
acl = Acl(self.client, self.get_resource())
return acl.add_access_settings(access_settings_list)
def remove_access_settings(self,
access_settings_list=None,
remove_all=False):
"""Remove access settings from the vApp.
:param list access_settings_list: list of dictionaries, where each
dictionary represents a single access setting. The dictionary
structure is as follows,
- type: (str): type of the subject. One of 'org' or 'user'.
- name: (str): name of the user or org.
:param bool remove_all: True, if the entire Access Control List of the
vApp should be removed, else False.
:return: an object containing EntityType.CONTROL_ACCESS_PARAMS XML
data representing the updated access control setting of the vdc.
:rtype: lxml.objectify.ObjectifiedElement`
"""
acl = Acl(self.client, self.get_resource())
return acl.remove_access_settings(access_settings_list, remove_all)
def share_with_org_members(self, everyone_access_level='ReadOnly'):
"""Share the vApp to all members of the organization.
:param everyone_access_level: (str) : access level when sharing the
vApp with everyone. Allowed values are 'ReadOnly', 'Change', or
'FullControl'. Default value is 'ReadOnly'.
:return: an object containing EntityType.CONTROL_ACCESS_PARAMS XML
data representing the updated access control setting of the vdc.
:rtype: lxml.objectify.ObjectifiedElement
"""
acl = Acl(self.client, self.get_resource())
return acl.share_with_org_members(everyone_access_level)
def unshare_from_org_members(self):
"""Unshare the vApp from all members of current organization.
:return: an object containing EntityType.CONTROL_ACCESS_PARAMS XML
data representing the updated access control setting of the vdc.
:rtype: lxml.objectify.ObjectifiedElement
"""
acl = Acl(self.client, self.get_resource())
return acl.unshare_from_org_members()
def get_all_networks(self):
"""Helper method that returns the list of networks defined in the vApp.
:return: a smart xpath string that represents the list of vApp
networks.
:rtype: xpath string
"""
self.get_resource()
return self.resource.xpath(
'//ovf:NetworkSection/ovf:Network',
namespaces={'ovf': NSMAP['ovf']})
def get_vapp_network_name(self, index=0):
"""Returns the name of the network defined in the vApp by index.
:param int index: index of the vApp network to retrieve. 0 if omitted.
:return: name of the requested network.
:rtype: str
:raises: EntityNotFoundException: if the named network could not be
found.
"""
networks = self.get_all_networks()
if networks is None or len(networks) < index + 1:
raise EntityNotFoundException(
'Can\'t find the specified vApp network')
return networks[index].get('{' + NSMAP['ovf'] + '}name')
def to_sourced_item(self, spec):
"""Creates a vm SourcedItem from a vm specification.
:param dict spec: a dictionary containing
- vapp: (resource): (required) source vApp or vAppTemplate
resource.
- source_vm_name: (str): (required) source vm name.
- target_vm_name: (str): (optional) target vm name.
- hostname: (str): (optional) target guest hostname.
- password: (str): (optional) the administrator password of the vm.
- password_auto: (bool): (optional) auto generate administrator
password.
- password_reset: (bool): (optional) True, if the administrator
password for this vm must be reset after first use.
- cust_script: (str): (optional) script to run on guest
customization.
- network: (str): (optional) name of the vApp network to connect.
If omitted, the vm won't be connected to any network.
- storage_profile: (str): (optional) the name of the storage
profile to be used for this vm.
- sizing_policy_href: (str): (optional) sizing policy used for
creating the VM
- placement_policy_href: (str): (optional) placement policy used
for creating the VM
:return: an object containing SourcedItem XML element.
:rtype: lxml.objectify.ObjectifiedElement
"""
source_vapp = VApp(self.client, resource=spec['vapp'])
source_vm_resource = source_vapp.get_vm(spec['source_vm_name'])
sourced_item = E.SourcedItem(
E.Source(
href=source_vm_resource.get('href'),
id=source_vm_resource.get('id'),
name=source_vm_resource.get('name'),
type=source_vm_resource.get('type')))
vm_general_params = E.VmGeneralParams()
if 'target_vm_name' in spec:
vm_general_params.append(E.Name(spec['target_vm_name']))
vm_instantiation_param = E.InstantiationParams()
if 'network' in spec:
primary_index = int(source_vm_resource.NetworkConnectionSection.
PrimaryNetworkConnectionIndex.text)
if 'ip_allocation_mode' in spec:
ip_allocation_mode = spec['ip_allocation_mode']
else:
ip_allocation_mode = 'DHCP'
vm_instantiation_param.append(
E.NetworkConnectionSection(
E_OVF.Info(),
E.NetworkConnection(
E.NetworkConnectionIndex(primary_index),
E.IsConnected(True),
E.IpAddressAllocationMode(ip_allocation_mode.upper()),
network=spec['network'])))
needs_customization = 'disk_size' in spec or 'password' in spec or \
'cust_script' in spec or 'hostname' in spec
if needs_customization:
guest_customization_param = E.GuestCustomizationSection(
E_OVF.Info(),
E.Enabled(True),
)
if 'password' in spec:
guest_customization_param.append(E.AdminPasswordEnabled(True))
guest_customization_param.append(E.AdminPasswordAuto(False))
guest_customization_param.append(
E.AdminPassword(spec['password']))
else:
if 'password_auto' in spec:
guest_customization_param.append(
E.AdminPasswordEnabled(True))
guest_customization_param.append(E.AdminPasswordAuto(True))
else:
guest_customization_param.append(
E.AdminPasswordEnabled(False))
if 'password_reset' in spec:
guest_customization_param.append(
E.ResetPasswordRequired(spec['password_reset']))
if 'cust_script' in spec:
guest_customization_param.append(
E.CustomizationScript(spec['cust_script']))
if 'hostname' in spec:
guest_customization_param.append(
E.ComputerName(spec['hostname']))
vm_instantiation_param.append(guest_customization_param)
vm_general_params.append(E.NeedsCustomization(needs_customization))
sourced_item.append(vm_general_params)
sourced_item.append(vm_instantiation_param)
if 'storage_profile' in spec:
sp = spec['storage_profile']
storage_profile = E.StorageProfile(
href=sp.get('href'),
id=sp.get('href').split('/')[-1],
type=sp.get('type'),
name=sp.get('name'))
sourced_item.append(storage_profile)
vdc_compute_policy_element, compute_policy_element = \
generate_compute_policy_tags(float(self.client.get_api_version()),
sizing_policy_href=spec.get('sizing_policy_href'), # noqa: E501
placement_policy_href=spec.get('placement_policy_href')) # noqa: E501
if vdc_compute_policy_element is not None:
sourced_item.append(vdc_compute_policy_element)
if compute_policy_element is not None:
sourced_item.append(compute_policy_element)
return sourced_item
def add_vms(self,
specs,
deploy=True,
power_on=True,
all_eulas_accepted=None,