From 740572ac36bb8bdae20fe1be5d127e5ab1b7b182 Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Tue, 8 Jan 2019 15:54:32 +0100 Subject: [PATCH 1/8] Add methods query_vm_by_name, get_vc to vdc --- pyvcloud/vcd/client.py | 10 ++++----- pyvcloud/vcd/vdc.py | 46 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/pyvcloud/vcd/client.py b/pyvcloud/vcd/client.py index b77ca58bd..c19baf21c 100644 --- a/pyvcloud/vcd/client.py +++ b/pyvcloud/vcd/client.py @@ -301,7 +301,7 @@ class ResourceType(Enum): ORG_VDC_NETWORK = 'orgVdcNetwork' ORG_VDC_RESOURCE_POOL_RELATION = 'orgVdcResourcePoolRelation' ORG_VDC_STORAGE_PROFILE = 'orgVdcStorageProfile' - PORT_GROUP = 'portgroup' + PORT_GROUP = 'portGroup' PROVIDER_VDC = 'providerVdc' PROVIDER_VDC_RESOURCE_POOL_RELATION = 'providerVdcResourcePoolRelation' PROVIDER_VDC_STORAGE_PROFILE = 'providerVdcStorageProfile' @@ -376,14 +376,14 @@ class EntityType(Enum): EXTERNAL_NETWORK = 'application/vnd.vmware.admin.vmwexternalnet+xml' EXTERNAL_NETWORK_REFS = \ 'application/vnd.vmware.admin.vmwExternalNetworkReferences+xml' - HOST = 'application/vnd.vmware.admin.host+xml' - HOST_REFS = 'application/vnd.vmware.admin.vmwHostReferences+xml' - INSTANTIATE_VAPP_TEMPLATE_PARAMS = \ - 'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml' GUEST_CUSTOMIZATION_SECTION = \ 'application/vnd.vmware.vcloud.guestCustomizationSection+xml' HISTORIC_USAGE = \ 'application/vnd.vmware.vcloud.metrics.historicUsageSpec+xml' + HOST = 'application/vnd.vmware.admin.host+xml' + HOST_REFS = 'application/vnd.vmware.admin.vmwHostReferences+xml' + INSTANTIATE_VAPP_TEMPLATE_PARAMS = \ + 'application/vnd.vmware.vcloud.instantiateVAppTemplateParams+xml' JSON = 'application/json' LEASE_SETTINGS = 'application/vnd.vmware.vcloud.leaseSettingsSection+xml' MEDIA = 'application/vnd.vmware.vcloud.media+xml' diff --git a/pyvcloud/vcd/vdc.py b/pyvcloud/vcd/vdc.py index dedad40c1..325944def 100644 --- a/pyvcloud/vcd/vdc.py +++ b/pyvcloud/vcd/vdc.py @@ -86,6 +86,16 @@ def get_resource(self): self.reload() return self.resource + def get_resource_admin(self): + """Fetches the XML representation of the admin org vdc from vCD. + + :return: object containing EntityType.VDC_ADMIN XML data representing + the org vdc. + + :rtype: lxml.objectify.ObjectifiedElement + """ + return self.client.get_resource(self.href_admin) + def get_resource_href(self, name, entity_type=EntityType.VAPP): """Fetches href of a vApp in the org vdc from vCD. @@ -131,6 +141,28 @@ def reload(self): self.name = self.resource.get('name') self.href = self.resource.get('href') + def query_vm_by_name(self, name): + """Query a VM by name. + + :param str name: name of the VM. + + :return: object containing EntityType.VM XML data representing the VM. + + :rtype: lxml.objectify.ObjectifiedElement + + :raises: MissingRecordException: if the named VM can not be found. + :raises: MultipleRecordsException: if more than one VM with the provided + name are found. + """ + vdc_filter = ('vdc==%s' % self.href) + name_filter = ('name', name) + query_obj = self.client.get_typed_query( + ResourceType.ADMIN_VM.value, + qfilter=vdc_filter, + equality_filter=name_filter) + vm_href = query_obj.find_unique().get('href') + return self.client.get_resource(vm_href) + def get_vapp_href(self, name): name_filter = ('name', name) vdc_filter = 'vdc==%s' % self.href @@ -1003,6 +1035,20 @@ def delete_vdc(self): return self.client.delete_linked_resource(self.resource, RelationType.REMOVE, None) + def get_vc(self): + """Returns the vCenter where this vdc is located. + + :return: name of the vCenter server. + + :rtype: str + """ + self.get_resource() + + vim_server_ref = self.resource.VCloudExtension[ + '{' + NSMAP['vmext'] + '}VimObjectRef'][ + '{' + NSMAP['vmext'] + '}VimServerRef'] + return vim_server_ref.get('name') + def get_access_settings(self): """Get the access settings of the vdc. From 89806dd48025587162aa099a98e122f3d5a2a66a Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Tue, 8 Jan 2019 15:56:20 +0100 Subject: [PATCH 2/8] Add methods discard_suspended_state, set_name, set_hostname to vm --- pyvcloud/vcd/vm.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/pyvcloud/vcd/vm.py b/pyvcloud/vcd/vm.py index e7da5418b..7728ffc67 100644 --- a/pyvcloud/vcd/vm.py +++ b/pyvcloud/vcd/vm.py @@ -435,6 +435,61 @@ def power_reset(self): return self._perform_power_operation( rel=RelationType.POWER_RESET, operation_name='power reset') + def discard_suspended_state(self): + """Discard the suspended state of the vm. + + :return: an object containing EntityType.TASK XML data which represents + the asynchronous task that is discarding the vm's suspended state. + + :rtype: lxml.objectify.ObjectifiedElement + """ + self.get_resource() + return self.client.post_linked_resource( + self.resource, RelationType.DISCARD_SUSPENDED_STATE, None, None) + + def set_name(self, name): + """Set the name of the vm. + + :param str name: new name for the vm. + + :return: an object containing EntityType.TASK XML data which represents + the asynchronous task that is reconfiguring the vm. + + :rtype: lxml.objectify.ObjectifiedElement + """ + self.get_resource() + params = E.Vm( + name=name + ) + return self.client.post_linked_resource( + self.resource, RelationType.RECONFIGURE_VM, + EntityType.VM.value, params) + + def set_hostname(self, hostname): + """Set the hostname (computer name) of the vm. + + :param str hostname: new hostname for the vm. + + :return: an object containing EntityType.TASK XML data which represents + the asynchronous task that is updating the vm's guest customization + section. + + :rtype: lxml.objectify.ObjectifiedElement + """ + from lxml import objectify + + self.get_resource() + + # Get current values and overwrite only ComputerName + params = self.resource.GuestCustomizationSection + params.ComputerName = objectify.DataElement( + hostname, nsmap='', _pytype='') + + return self.client.put_resource( + self.resource.GuestCustomizationSection.get('href'), + params, + EntityType.GUEST_CUSTOMIZATION_SECTION.value) + def deploy(self, power_on=True, force_customization=False): """Deploys the vm. From e6c230c7246ccaa03fe05d5a4e78fb60477bc105 Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Tue, 8 Jan 2019 15:56:58 +0100 Subject: [PATCH 3/8] Add method set_name to vapp --- pyvcloud/vcd/vapp.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pyvcloud/vcd/vapp.py b/pyvcloud/vcd/vapp.py index 36ecc837c..0b5e4de79 100644 --- a/pyvcloud/vcd/vapp.py +++ b/pyvcloud/vcd/vapp.py @@ -554,6 +554,24 @@ def reboot(self): return self._perform_power_operation( rel=RelationType.POWER_REBOOT, operation_name='reboot') + def set_name(self, name): + """Set the name of the vApp. + + :param str name: new name for the vApp. + + :return: an object containing EntityType.TASK XML data which represents + the asynchronous task that is recomposing the vApp. + + :rtype: lxml.objectify.ObjectifiedElement + """ + self.get_resource() + params = E.RecomposeVAppParams( + name=name + ) + return self.client.post_linked_resource( + self.resource, RelationType.RECOMPOSE, + EntityType.RECOMPOSE_VAPP_PARAMS.value, params) + def connect_vm(self, mode='DHCP', reset_mac_address=False): self.get_resource() if hasattr(self.resource, 'Children') and \ From ce0683fc6fb0e86269e0ae7cb1488877e018f73d Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Tue, 8 Jan 2019 16:08:22 +0100 Subject: [PATCH 4/8] Fix linting findings --- pyvcloud/vcd/vdc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyvcloud/vcd/vdc.py b/pyvcloud/vcd/vdc.py index 325944def..e16ed094b 100644 --- a/pyvcloud/vcd/vdc.py +++ b/pyvcloud/vcd/vdc.py @@ -151,8 +151,8 @@ def query_vm_by_name(self, name): :rtype: lxml.objectify.ObjectifiedElement :raises: MissingRecordException: if the named VM can not be found. - :raises: MultipleRecordsException: if more than one VM with the provided - name are found. + :raises: MultipleRecordsException: if more than one VM with the + provided name are found. """ vdc_filter = ('vdc==%s' % self.href) name_filter = ('name', name) From be4d4d4f95dd67a086a19c3b3cb5410d2769cc14 Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Mon, 12 Aug 2019 10:33:49 +0200 Subject: [PATCH 5/8] Remove duplicated method discard_suspended_state --- pyvcloud/vcd/vm.py | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/pyvcloud/vcd/vm.py b/pyvcloud/vcd/vm.py index 7728ffc67..78b35178a 100644 --- a/pyvcloud/vcd/vm.py +++ b/pyvcloud/vcd/vm.py @@ -435,6 +435,18 @@ def power_reset(self): return self._perform_power_operation( rel=RelationType.POWER_RESET, operation_name='power reset') + def suspend(self): + """Suspend the vm. + + :return: an object containing EntityType.TASK XML data which represents + the asynchronous task that is suspending the VM. + + :rtype: lxml.objectify.ObjectifiedElement + """ + self.get_resource() + return self._perform_power_operation( + rel=RelationType.POWER_SUSPEND, operation_name='suspend') + def discard_suspended_state(self): """Discard the suspended state of the vm. @@ -709,31 +721,6 @@ def delete_nic(self, index): net_conn_section, RelationType.EDIT, EntityType.NETWORK_CONNECTION_SECTION.value, net_conn_section) - def suspend(self): - """Suspend the vm. - - :return: an object containing EntityType.TASK XML data which represents - the asynchronous task that is suspending the VM. - - :rtype: lxml.objectify.ObjectifiedElement - """ - self.get_resource() - return self._perform_power_operation( - rel=RelationType.POWER_SUSPEND, operation_name='suspend') - - def discard_suspended_state(self): - """Discard suspended state of the vm. - - :return: an object containing EntityType.TASK XML data which represents - the asynchronous task that is discarding suspended state - of VM. - - :rtype: lxml.objectify.ObjectifiedElement - """ - self.get_resource() - return self.client.post_linked_resource( - self.resource, RelationType.DISCARD_SUSPENDED_STATE, None, None) - def install_vmware_tools(self): """Install vmware tools in the vm. From 2f612396076ba04e58420a9b78ddcdd2a1f55eac Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Tue, 24 Sep 2019 21:57:01 +0200 Subject: [PATCH 6/8] Add method get_moid to vm --- pyvcloud/vcd/vm.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pyvcloud/vcd/vm.py b/pyvcloud/vcd/vm.py index 78b35178a..39c08978c 100644 --- a/pyvcloud/vcd/vm.py +++ b/pyvcloud/vcd/vm.py @@ -103,6 +103,20 @@ def get_vc(self): return record.VimServerRef.get('name') return None + def get_moid(self): + """Returns the vCenter MoRef of this vm. + + :return: MoRef of this vm. + + :rtype: str + """ + if hasattr(self.get_resource(), 'VCloudExtension'): + return self.get_resource().VCloudExtension[ + '{' + NSMAP['vmext'] + '}VmVimInfo'][ + '{' + NSMAP['vmext'] + '}VmVimObjectRef'][ + '{' + NSMAP['vmext'] + '}MoRef'].text + return None + def get_cpus(self): """Returns the number of CPUs in the vm. From 2b3181f1a9de76c4a7daeaa3f0dd6c88f9c80683 Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Tue, 24 Sep 2019 21:57:44 +0200 Subject: [PATCH 7/8] Add method get_vc_resource to vdc --- pyvcloud/vcd/vdc.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/pyvcloud/vcd/vdc.py b/pyvcloud/vcd/vdc.py index e16ed094b..0b6c46b95 100644 --- a/pyvcloud/vcd/vdc.py +++ b/pyvcloud/vcd/vdc.py @@ -1042,12 +1042,22 @@ def get_vc(self): :rtype: str """ - self.get_resource() + return self._get_vc_ref().get('name') + + def get_vc_resource(self): + """Returns the vCenter where this vdc is located as resource. + + :return: an object containing EntityType.VIRTUAL_CENTER XML data which + represents the vCenter of this vdc. + + :rtype: lxml.objectify.ObjectifiedElement + """ + return self.client.get_resource(self._get_vc_ref().get('href')) - vim_server_ref = self.resource.VCloudExtension[ + def _get_vc_ref(self): + return self.get_resource().VCloudExtension[ '{' + NSMAP['vmext'] + '}VimObjectRef'][ '{' + NSMAP['vmext'] + '}VimServerRef'] - return vim_server_ref.get('name') def get_access_settings(self): """Get the access settings of the vdc. From ba356f120e7c571261a664f28682b1c01fc48876 Mon Sep 17 00:00:00 2001 From: David Wettstein Date: Fri, 17 Jul 2020 10:04:32 +0200 Subject: [PATCH 8/8] Fix methods set_name --- pyvcloud/vcd/vapp.py | 18 ------------------ pyvcloud/vcd/vm.py | 24 +++++++++++++----------- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/pyvcloud/vcd/vapp.py b/pyvcloud/vcd/vapp.py index 0b5e4de79..36ecc837c 100644 --- a/pyvcloud/vcd/vapp.py +++ b/pyvcloud/vcd/vapp.py @@ -554,24 +554,6 @@ def reboot(self): return self._perform_power_operation( rel=RelationType.POWER_REBOOT, operation_name='reboot') - def set_name(self, name): - """Set the name of the vApp. - - :param str name: new name for the vApp. - - :return: an object containing EntityType.TASK XML data which represents - the asynchronous task that is recomposing the vApp. - - :rtype: lxml.objectify.ObjectifiedElement - """ - self.get_resource() - params = E.RecomposeVAppParams( - name=name - ) - return self.client.post_linked_resource( - self.resource, RelationType.RECOMPOSE, - EntityType.RECOMPOSE_VAPP_PARAMS.value, params) - def connect_vm(self, mode='DHCP', reset_mac_address=False): self.get_resource() if hasattr(self.resource, 'Children') and \ diff --git a/pyvcloud/vcd/vm.py b/pyvcloud/vcd/vm.py index 39c08978c..8d55252f0 100644 --- a/pyvcloud/vcd/vm.py +++ b/pyvcloud/vcd/vm.py @@ -473,28 +473,30 @@ def discard_suspended_state(self): return self.client.post_linked_resource( self.resource, RelationType.DISCARD_SUSPENDED_STATE, None, None) - def set_name(self, name): - """Set the name of the vm. + def edit_name(self, name): + """Edit name of the vm. - :param str name: new name for the vm. + :param str name: New name of the vm. It is mandatory. :return: an object containing EntityType.TASK XML data which represents the asynchronous task that is reconfiguring the vm. :rtype: lxml.objectify.ObjectifiedElement """ + if name is None or name.isspace(): + raise InvalidParameterException("Name can't be None or empty") self.get_resource() params = E.Vm( - name=name + name=name.strip() ) return self.client.post_linked_resource( self.resource, RelationType.RECONFIGURE_VM, EntityType.VM.value, params) - def set_hostname(self, hostname): - """Set the hostname (computer name) of the vm. + def edit_hostname(self, hostname): + """Edit hostname (computer name) of the vm. - :param str hostname: new hostname for the vm. + :param str hostname: new hostname of the vm. It is mandatory. :return: an object containing EntityType.TASK XML data which represents the asynchronous task that is updating the vm's guest customization @@ -502,14 +504,14 @@ def set_hostname(self, hostname): :rtype: lxml.objectify.ObjectifiedElement """ - from lxml import objectify - + if hostname is None or hostname.isspace(): + raise InvalidParameterException("Hostname can't be None or empty") self.get_resource() - # Get current values and overwrite only ComputerName + from lxml import objectify params = self.resource.GuestCustomizationSection params.ComputerName = objectify.DataElement( - hostname, nsmap='', _pytype='') + hostname.strip(), nsmap='', _pytype='') return self.client.put_resource( self.resource.GuestCustomizationSection.get('href'),