class OvfImportExport(SampleBase): """ Demonstrates the workflow to import an OVF package into the content library, as well as download of an OVF template from the content library. Note: the workflow needs an existing VC DS with available storage. """ def __init__(self): SampleBase.__init__(self, self.__doc__) self.servicemanager = None self.client = None self.helper = None self.datastore_name = None self.lib_name = "demo-lib" self.local_library = None self.lib_item_name = "simpleVmTemplate" self.library_item = None def _options(self): self.argparser.add_argument('-datastorename', '--datastorename', help='The name of the datastore.') def _setup(self): self.datastore_name = self.args.datastorename assert self.datastore_name is not None if not self.servicemanager: self.servicemanager = self.get_service_manager() self.client = ClsApiClient(self.servicemanager) self.helper = ClsApiHelper(self.client, self.skip_verification) def _execute(self): # Find the datastore by the given datastore name using property collector self.datastore_id = get_datastore_id(service_manager=self.servicemanager, datastore_name=self.datastore_name) assert self.datastore_id is not None print('DataStore: {0} ID: {1}'.format(self.datastore_name, self.datastore_id)) # Build the storage backing for the library to be created storage_backings = [] storage_backing = StorageBacking(type=StorageBacking.Type.DATASTORE, datastore_id=self.datastore_id) storage_backings.append(storage_backing) # Build the specification for the library to be created create_spec = LibraryModel() create_spec.name = self.lib_name create_spec.description = "Local library backed by VC datastore" create_spec.type = create_spec.LibraryType.LOCAL create_spec.storage_backings = storage_backings # Create a local content library backed the VC datastore using vAPIs library_id = self.client.local_library_service.create(create_spec=create_spec, client_token=generate_random_uuid()) print('Local library created: ID: {0}'.format(library_id)) self.local_library = self.client.local_library_service.get(library_id) # Create a new library item in the content library for uploading the files self.library_item_id = self.helper.create_library_item(library_id=self.local_library.id, item_name=self.lib_item_name, item_description='Sample simple VM template', item_type='ovf') assert self.library_item_id is not None assert self.client.library_item_service.get(self.library_item_id) is not None print('Library item created id: {0}'.format(self.library_item_id)) # Upload a VM template to the CL ovf_files_map = self.helper.get_ovf_files_map(ClsApiHelper.SIMPLE_OVF_RELATIVE_DIR) self.helper.upload_files(library_item_id=self.library_item_id, files_map=ovf_files_map) print('Uploaded ovf and vmdk files to library item {0}'.format(self.library_item_id)) # Download the library item from the CL temp_dir = tempfile.mkdtemp(prefix='simpleVmTemplate-') print('Downloading library item {0} to directory {1}'.format(self.library_item_id, temp_dir)) downloaded_files_map = self.helper.download_files(library_item_id=self.library_item_id, directory=temp_dir) assert len(downloaded_files_map) == len(ovf_files_map) def _cleanup(self): if self.local_library: self.client.local_library_service.delete(library_id=self.local_library.id) print('Deleted Library Id: {0}'.format(self.local_library.id))
class VmtxPublish(SampleBase): """ Demonstrates the VMTX push sync workflow to publish and subscribe VMTX items. Note: the workflow needs an existing VC datastore with available storage. """ SYNC_TIMEOUT_SEC = 60 def __init__(self): SampleBase.__init__(self, self.__doc__) self.servicemanager = None self.client = None self.helper = None self.datastore_name = None self.resource_pool_id = None self.folder_id = None self.pub_libs_to_clean = [] self.sub_libs_to_clean = [] self.vms_to_clean = [] def _options(self): self.argparser.add_argument('-datacentername', '--datacentername', required=True, help='The name of the datacenter') self.argparser.add_argument('-datastorename', '--datastorename', required=True, help='The name of the datastore.') self.argparser.add_argument('-clustername', '--clustername', required=True, help='The name of the cluster to be used.') self.argparser.add_argument('-foldername', '--foldername', required=True, help='The name of the folder in the ' 'datacenter for creating a subscription') def _setup(self): self.datastore_name = self.args.datastorename self.cluster_name = self.args.clustername self.folder_name = self.args.foldername self.datacenter_name = self.args.datacentername self.servicemanager = self.get_service_manager() self.datastore_id = get_datastore_id( service_manager=self.servicemanager, datastore_name=self.datastore_name) self.client = ClsApiClient(self.servicemanager) self.helper = ClsApiHelper(self.client, self.skip_verification) session = get_unverified_session() if self.skip_verification else None self.vsphere_client = create_vsphere_client(server=self.server, username=self.username, password=self.password, session=session) self.folder_id = get_folder(self.vsphere_client, self.datacenter_name, self.folder_name) self.storage_backings = self.helper.create_storage_backings( self.servicemanager, self.datastore_name) cluster_obj = get_obj(self.servicemanager.content, [vim.ClusterComputeResource], self.cluster_name) self.resource_pool_id = cluster_obj.resourcePool._GetMoId() def _execute(self): self.create_new_subscription() self.create_subscription_from_existing_subscribed_library() def create_new_subscription(self): """ Sample code for creating a new subscription for VMTX templates """ # Create a published library and a new subscription pub_lib_name = "pub_lib_new_" + str(uuid.uuid4()) pub_lib_id = self.create_published_library(pub_lib_name).id self.pub_libs_to_clean.append(pub_lib_id) sub_lib_name = "sub_lib_new_" + str(uuid.uuid4()) subscription_id = self.create_subscription_new(pub_lib_id, sub_lib_name) # Get the subscribed library associated with the subscription subscription_info = self.client.subscriptions.get( pub_lib_id, subscription_id) sub_lib = self.client.library_service.get( subscription_info.subscribed_library) self.sub_libs_to_clean.append(sub_lib.id) # - Create a VMTX item on the published library # - Push-synchronize the subscription and verify the sync vm_name = "sample_vm_new_" + str(uuid.uuid4()) vmtx_item_name = "sample_vmtx_item_existing_" + str(uuid.uuid4()) vmtx_item_id = self.create_vmtx_item(pub_lib_id, vm_name, vmtx_item_name) self.client.local_library_service.publish(pub_lib_id) assert self.verify_vmtx_sync(sub_lib, vmtx_item_id) def create_subscription_from_existing_subscribed_library(self): """ Sample code for converting existing Subscribed library to use a VMTX subscription """ # Create a published library and get its publish URL pub_lib_name = "pub_lib_existing_" + str(uuid.uuid4()) pub_lib = self.create_published_library(pub_lib_name) self.pub_libs_to_clean.append(pub_lib.id) pub_lib_url = pub_lib.publish_info.publish_url # Create a subscribed library sub_lib_name = "sub_lib_existing_" + str(uuid.uuid4()) sub_lib = self.create_subscribed_library(pub_lib_url, sub_lib_name) self.create_subscription_for_existing_subscribed_library( pub_lib.id, sub_lib.id) # - Create a VMTX item on the published library # - Push-synchronize the subscription and verify the sync vm_name = "sample_vm_existing_" + str(uuid.uuid4()) vmtx_item_name = "sample_vmtx_item_existing_" + str(uuid.uuid4()) vmtx_item_id = self.create_vmtx_item(pub_lib.id, vm_name, vmtx_item_name) self.client.local_library_service.publish(pub_lib.id) assert self.verify_vmtx_sync(sub_lib, vmtx_item_id) def create_vmtx_item(self, pub_lib_id, vm_name, vmtx_item_name): # Upload OVF, create a VM, and use that VM to create a VMTX item ovf_item_id = self.create_ovf_template_item(pub_lib_id) source_vmtx_vm_id = self.create_vm(ovf_item_id, vm_name) self.vms_to_clean.append(source_vmtx_vm_id) vmtx_item_id = self.create_vmtx_item_from_vm(pub_lib_id, source_vmtx_vm_id, vmtx_item_name) return vmtx_item_id def create_published_library(self, pub_lib_name): pub_info = PublishInfo() pub_info.published = True # VMTX sync needs the authentication to be turned off pub_info.authentication_method = PublishInfo.AuthenticationMethod.NONE pub_spec = LibraryModel() pub_spec.name = pub_lib_name pub_spec.description = "Sample Published library" pub_spec.publish_info = pub_info pub_spec.type = pub_spec.LibraryType.LOCAL pub_spec.storage_backings = self.storage_backings pub_lib_id = self.client.local_library_service.create( create_spec=pub_spec, client_token=generate_random_uuid()) print("Published library created, id: {0}".format(pub_lib_id)) pub_lib = self.client.library_service.get(pub_lib_id) return pub_lib def create_subscribed_library(self, pub_lib_url, sub_lib_name): # Build the subscription information using the publish URL of the published # library sub_info = SubscriptionInfo() sub_info.authentication_method = SubscriptionInfo.AuthenticationMethod.NONE # on_demand = False for library and item level publish # on_demand = True for only item level publish, the library level # publish will only sync the item metadata sub_info.on_demand = False sub_info.automatic_sync_enabled = True sub_info.subscription_url = pub_lib_url # Build the specification for the subscribed library sub_spec = LibraryModel() sub_spec.name = sub_lib_name sub_spec.type = sub_spec.LibraryType.SUBSCRIBED sub_spec.subscription_info = sub_info sub_spec.storage_backings = self.storage_backings sub_lib_id = self.client.subscribed_library_service.create( create_spec=sub_spec, client_token=generate_random_uuid()) self.sub_libs_to_clean.append(sub_lib_id) print("Subscribed library created, id: {0}".format(sub_lib_id)) sub_lib = self.client.subscribed_library_service.get(sub_lib_id) return sub_lib def create_subscription_new(self, pub_lib_id, sub_lib_name): # Create a new subscription. Such subscription is created on the published # library, and can be later used for a push-sync # # spec # +--subscribed_library # +--target: CREATE_NEW # +--subscribed_library: DO NOT SPECIFY as this is new # +--new_subscribed_library # +--name, description, automatic_sync_enabled, on_demand # +--location: LOCAL/REMOTE # +--subscribed_library_vcenter: (VcenterInfo) DO NOT SPECIFY for location=LOCAL # +--placement: # +--Resource pool and folder for the VM # +--network for the VM client_token = str(uuid.uuid4()) spec = Subscriptions.CreateSpec() subscribed_library = Subscriptions.CreateSpecSubscribedLibrary() subscribed_library.location = Subscriptions.Location.LOCAL subscribed_library.target = \ Subscriptions.CreateSpecSubscribedLibrary.Target.CREATE_NEW new_subscribed_library = Subscriptions.CreateSpecNewSubscribedLibrary() new_subscribed_library.name = sub_lib_name new_subscribed_library.description = "Sample subscribed library" backing = StorageBacking(StorageBacking.Type.DATASTORE, self.datastore_id) new_subscribed_library.storage_backings = [backing] new_subscribed_library.automatic_sync_enabled = False # on_demand = False for library and item level publish # on_demand = True for only item level publish, the library level # publish will only sync the item metadata new_subscribed_library.on_demand = False subscribed_library.new_subscribed_library = new_subscribed_library placement = Subscriptions.CreateSpecPlacement() placement.resource_pool = self.resource_pool_id placement.folder = self.folder_id # Setting network to null implies that the subscription will use the # same network as the source VM # Warning - this may lead to failure if the same network is not # available to the subscriber placement.network = None subscribed_library.placement = placement spec.subscribed_library = subscribed_library subscription_id = self.client.subscriptions.create( pub_lib_id, spec, client_token) print("Subscription created, id: {0}".format(subscription_id)) return subscription_id def create_subscription_for_existing_subscribed_library( self, pub_lib_id, sub_lib_id): # Create a subscription for an existing subscribed library. This subscription # and can be later used for a push-sync to that subscribed library # # spec # +--subscribed_library # +--target: USE_EXISTING # +--subscribed_library: ID of existing subscribed library # +--new_subscribed_library: DO NOT SPECIFY for target=USE_EXISTING # +--location: LOCAL/REMOTE # +--subscribed_library_vcenter: (VcenterInfo) DO NOT SPECIFY from location=LOCAL # +--placement: # +--Resource pool and folder for the VM # +--network for the VM client_token = str(uuid.uuid4()) spec = Subscriptions.CreateSpec() subscribed_library = Subscriptions.CreateSpecSubscribedLibrary() subscribed_library.target = \ Subscriptions.CreateSpecSubscribedLibrary.Target.USE_EXISTING subscribed_library.subscribed_library = sub_lib_id subscribed_library.location = "LOCAL" placement = Subscriptions.CreateSpecPlacement() placement.resource_pool = self.resource_pool_id placement.folder = self.folder_id # Setting network to null implies that the subscription will use the # same network as the source VM # Warning - this may lead to failure if the same network is not # available to the subscriber placement.network = None subscribed_library.placement = placement spec.subscribed_library = subscribed_library subscription_id = self.client.subscriptions.create( pub_lib_id, spec, client_token) print("Subscription created id: {0}".format(subscription_id)) return subscription_id def create_ovf_template_item(self, library_id): # Create an OVF item ovf_item_id = self.helper.create_library_item( library_id=library_id, item_name='sample-ovf-item', item_description='Sample OVF template', item_type='ovf') print('Library item created id: {0}'.format(ovf_item_id)) # Upload a VM template to the CL ovf_files_map = self.helper.get_ovf_files_map( ClsApiHelper.SIMPLE_OVF_RELATIVE_DIR) self.helper.upload_files(library_item_id=ovf_item_id, files_map=ovf_files_map) return ovf_item_id def create_vm(self, ovf_item_id, vm_name): # Deploy a VM using the given ovf template deployment_target = LibraryItem.DeploymentTarget( resource_pool_id=self.resource_pool_id) ovf_summary = self.client.ovf_lib_item_service.filter( ovf_library_item_id=ovf_item_id, target=deployment_target) vm_id = self.deploy_ovf_template(ovf_item_id, ovf_summary, deployment_target, vm_name) print("Virtual Machine created, id: {0}".format(vm_id)) return vm_id def deploy_ovf_template(self, lib_item_id, ovf_summary, deployment_target, vm_name): # Build the deployment spec deployment_spec = LibraryItem.ResourcePoolDeploymentSpec( name=vm_name, annotation=ovf_summary.annotation, accept_all_eula=True) # Deploy the ovf template result = self.client.ovf_lib_item_service.deploy( lib_item_id, deployment_target, deployment_spec, client_token=generate_random_uuid()) if result.succeeded: vm_id = result.resource_id.id return vm_id else: print('Deployment failed.') for error in result.error.errors: print('OVF error: {}'.format(error.message)) raise Exception('OVF deploy failed.') def create_vmtx_item_from_vm(self, library_id, source_vm_id, vmtx_item_name): # Create a VMTX item using the given VM as source create_spec = VmtxLibraryItem.CreateSpec() create_spec.source_vm = source_vm_id create_spec.library = library_id create_spec.name = vmtx_item_name create_spec.description = 'sample-vmtx-description' create_spec.placement = VmtxLibraryItem.CreatePlacementSpec() create_spec.placement.resource_pool = self.resource_pool_id vmtx_item_id = self.client.vmtx_service.create(create_spec) print("VMTX item created id: {0}".format(vmtx_item_id)) return vmtx_item_id def verify_vmtx_sync(self, sub_lib, vmtx_item_id): # Wait until the VMTX item in the subscription is synchronized with # the one in the published library start_time = time.time() while time.time() - start_time < self.SYNC_TIMEOUT_SEC: sub_item_ids = self.client.library_item_service.list(sub_lib.id) # Only vmtx item will be synced using the push mechanism, so check # the length to be one if len(sub_item_ids) == 1: source_id = self.client.library_item_service.get( sub_item_ids[0]).source_id # Verify that the source for the VMTX item in the subscribed # library is the VMTX item in the published library if source_id == vmtx_item_id: return True else: print("VMTX source item id mismatch") return False # Give some more time for sync time.sleep(1) print("Timed out while waiting for sync") return False def _cleanup(self): for lib_id in self.pub_libs_to_clean: print("deleting published library: {0}".format(lib_id)) self.client.local_library_service.delete(lib_id) for lib_id in self.sub_libs_to_clean: print("deleting subscribed library: {0}".format(lib_id)) self.client.subscribed_library_service.delete(lib_id) for vm_id in self.vms_to_clean: vm_obj = get_obj_by_moId(self.servicemanager.content, [vim.VirtualMachine], vm_id) delete_object(self.servicemanager.content, vm_obj)
class ContentUpdate(SampleBase): """ Demonstrates the workflow of updating a content library item. Note: the workflow needs an existing datastore (of type vmfs) with available storage. """ ISO_FILE_1 = 'test.iso' ISO_FILE_2 = 'test-2.iso' ISO_ITEM_NAME = 'test' def __init__(self): SampleBase.__init__(self, self.__doc__) self.servicemanager = None self.client = None self.helper = None self.datastore_name = None self.lib_name = "demo-lib" self.local_library = None def _options(self): self.argparser.add_argument('-datastorename', '--datastorename', help='The name of the datastore where ' 'the library will be created.') def _setup(self): self.datastore_name = self.args.datastorename assert self.datastore_name is not None self.servicemanager = self.get_service_manager() self.client = ClsApiClient(self.servicemanager) self.helper = ClsApiHelper(self.client, self.skip_verification) def _execute(self): storage_backings = self.helper.create_storage_backings( self.servicemanager, self.datastore_name) library_id = self.helper.create_local_library(storage_backings, self.lib_name) self.local_library = self.client.local_library_service.get(library_id) self.delete_and_upload_scenario(library_id) self.replace_scenario(library_id) def replace_scenario(self, library_id): """ :param library_id: the Iso item will be created, and then replaced in this library :return: None Content update scenario 2: Update ISO library item by creating an update session for the item, then adding the new ISO file using the same session file name into the update session, which will replace the existing ISO file upon session complete. """ iso_item_id = self.helper.create_library_item( library_id=library_id, item_name=self.ISO_ITEM_NAME, item_description='Sample iso file', item_type='iso') print('ISO Library item version (on creation) {0}:'.format( self.get_item_version(iso_item_id))) iso_files_map = self.helper.get_iso_file_map( item_filename=self.ISO_FILE_1, disk_filename=self.ISO_FILE_1) self.helper.upload_files(library_item_id=iso_item_id, files_map=iso_files_map) original_version = self.get_item_version(iso_item_id) print('ISO Library item version (on original content upload) {0}:'. format(original_version)) session_id = self.client.upload_service.create( create_spec=UpdateSessionModel(library_item_id=iso_item_id), client_token=generate_random_uuid()) # Use the same item filename (update endpoint, as it's a replace scenario) iso_files_map = self.helper.get_iso_file_map( item_filename=self.ISO_FILE_1, disk_filename=self.ISO_FILE_2) self.helper.upload_files_in_session(iso_files_map, session_id) self.client.upload_service.complete(session_id) self.client.upload_service.delete(session_id) updated_version = self.get_item_version(iso_item_id) print('ISO Library item version (after content update): {0}'.format( updated_version)) assert updated_version > original_version, 'content update should increase the version' def delete_and_upload_scenario(self, library_id): """ :param library_id: the OVF item will be created and updated in this library :return: None Content update scenario 1: Update OVF library item by creating an update session for the OVF item, removing all existing files in the session, then adding all new files into the same update session, and completing the session to finish the content update. """ # Create a new library item in the content library for uploading the files ovf_item_id = self.helper.create_library_item( library_id=library_id, item_name='demo-ovf-item', item_description='Sample simple VM template', item_type='ovf') assert ovf_item_id is not None print('Library item created id: {0}'.format(ovf_item_id)) print('OVF Library item version (at creation) {0}:'.format( self.get_item_version(ovf_item_id))) # Upload a VM template to the CL ovf_files_map = self.helper.get_ovf_files_map( ClsApiHelper.SIMPLE_OVF_RELATIVE_DIR) self.helper.upload_files(library_item_id=ovf_item_id, files_map=ovf_files_map) print('Uploaded ovf and vmdk files to library item {0}'.format( ovf_item_id)) original_version = self.get_item_version(ovf_item_id) print('OVF Library item version (on original content upload): {0}'. format(original_version)) # Create a new session and perform content update session_id = self.client.upload_service.create( create_spec=UpdateSessionModel(library_item_id=ovf_item_id), client_token=generate_random_uuid()) existing_files = self.client.upload_file_service.list(session_id) for file in existing_files: print('deleting {0}'.format(file.name)) self.client.upload_file_service.remove(session_id, file.name) ovf_files_map = self.helper.get_ovf_files_map( ovf_location=ClsApiHelper.PLAIN_OVF_RELATIVE_DIR) self.helper.upload_files_in_session(ovf_files_map, session_id) self.client.upload_service.complete(session_id) self.client.upload_service.delete(session_id) updated_version = self.get_item_version(ovf_item_id) print('OVF Library item version (after content update): {0}'.format( updated_version)) assert updated_version > original_version, 'content update should increase the version' def get_item_version(self, item_id): ovf_item_model = self.client.library_item_service.get(item_id) pre_update_version = ovf_item_model.content_version return pre_update_version def _cleanup(self): if self.local_library: self.client.local_library_service.delete( library_id=self.local_library.id) print('Deleted Library Id: {0}'.format(self.local_library.id))