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..0b6c46b95 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,30 @@ 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 + """ + 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')) + + def _get_vc_ref(self): + return self.get_resource().VCloudExtension[ + '{' + NSMAP['vmext'] + '}VimObjectRef'][ + '{' + NSMAP['vmext'] + '}VimServerRef'] + def get_access_settings(self): """Get the access settings of the vdc. diff --git a/pyvcloud/vcd/vm.py b/pyvcloud/vcd/vm.py index e7da5418b..8d55252f0 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. @@ -435,6 +449,75 @@ 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. + + :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 edit_name(self, name): + """Edit name of 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.strip() + ) + return self.client.post_linked_resource( + self.resource, RelationType.RECONFIGURE_VM, + EntityType.VM.value, params) + + def edit_hostname(self, hostname): + """Edit hostname (computer name) of 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 + section. + + :rtype: lxml.objectify.ObjectifiedElement + """ + 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.strip(), 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. @@ -654,31 +737,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.