Home > Blogs > VMware Developer Blog


API Tutorial – Uploading an OVF Template to a Content Library

This blog post is a part of the blog series published by the Content Library team. Please find all the blog posts by the team at this link.

Previously, the Basic Life Cycle of a Content Library tutorial explained how to use Content Library APIs to create, update and delete libraries and library items. In this tutorial, we will further explore the APIs to upload content to a library item.

At the end of this tutorial you should be able to:


To be able to run the API sample for this tutorial, you will need:

  • A library and a library item created on the server.
  • An OVF template ready to upload.

Check out the resources provided with the API Samples.

The Content Library core APIs are agnostic of the type of content being uploaded. However, an internal type plugin system allows for a specific item type to have a special behavior. The only available type plugin in vSphere 6.0 is the OVF type plugin. This can easily be verified by calling the following API:

// list available types
for (Info type: client.type().list()) {
    print(type.getName());
}

The OVF type plugin will ensure that the OVF descriptors are properly validated. It will also help callers to identify which files are actually required by the uploaded OVF descriptor.

A Primer on the Content Library UpdateSession API

We have seen the UpdateSession API briefly in Basic Life Cycle of a Content Library tutorial when uploading an ISO. Now is the right time to learn more about the concepts introduced by the UpdateSession API.

As we know from the previous tutorial, any updates to the content of the library items need to be done via the UpdateSession API. The update session ensures the atomicity of the changes to the library item and allows a client to update the state of items without impacting other clients that are currently using the same item. To give an analogy from the database world, an update session is analogous to a database transaction.

At a High Level, the UpdateSession API Works as Follows:

  • To initiate the content update on a library item, a client creates an UpdateSession for that library item.
  • The client uses this update session to specify the required content updates for the library item.
    • If the client wants to add a file to a library item, he/she has to specify that through UpdateSession.File API.
    • If the client wants to remove a file from a library item, he/she needs to specify that through UpdateSession.File API.
  • The client calls UpdateSession.complete() API, once he/she is done with adding/removing files from the update session to achieve the desired content update.
    • This call tells the Content Library Service that the client is done with specifying the updates, and that the Content Library Service can start applying those changes to the library item.
    • The Content Library Service will perform the necessary updates to the library item, and the session object state will contain the status of this update operation.

The Update Session Can Have following States:

  • UpdateSessionModel.State.ACTIVE – The update session will be in this state from when it is created, till it is completed. Thus when the Content Library Service is making updates to the library item, the session will be in this state.
  • UpdateSessionModel.State.DONE – This is a terminal state for the update session, and it is reached when the Content Library Service successfully makes the updates to the library item. This state guarantees that the library item content is updated as per the specification in the update session.
  • UpdateSessionModel.State.ERROR – This is a terminal state for the update session, and it is reached when the Content Library Service cannot successfully apply the updates to the library item. Please note that, even after the failure of the update session, because of the atomicity guarantee provided by the UpdateSession API, the library item content will be same as it was before creating this update session.
  • UpdateSessionModel.State.CANCELED – This is a terminal state for the update session, and it is reached when the update session was canceled by the client by calling UpdateSession.cancel() API. Because of the atomicity guarantee provided by the UpdateSession API, the library item content will be same as it was before creating this update session.

The update session gives the client exclusive access to update the library item. This means, at any time, only one update session can be in the ACTIVE state for a library item. Only when the session has reached a terminal state (either by canceling it, or completing it with success or failure), another client can create an update session for this library item to make changes.

In the following sections, we will learn more on how to use these UpdateSession APIs to upload OVF templates to a content library.

Importing an OVF Template from a Remote URI

This is the easiest way to upload an OVF template. As mentioned in the tutorial Basic Life Cycle of a Content Library, all content updates are done by the UpdateSession API. The client will only need to add the OVF descriptor to the update session.

Create an Update Session

First, we create a new library item of type OVF. Next, to import files into this library item, we create an update session for it as follows:

// create an update session
UpdateSessionModel updateSessionModel = new UpdateSessionModel();
updateSessionModel.setLibraryItemId(itemId);
String sessionId = client.updateSession().create(null /*clientToken*/, updateSessionModel);

Add the OVF Descriptor to the Update Session

The next step after creating a session is to add the OVF descriptor to it. We specify the source type as SourceType.PULL. The uri property is required and points to the HTTP URL of the OVF descriptor. Based on this information, the Content Library OVF plugin will automatically fetch the OVF descriptor as well as all the required files (as specified in the provided OVF descriptor) for us. This behavior is a consequence of the type plugin system.

// add the OVF file to the session
AddSpec fileSpec = new AddSpec();
fileSpec.setName("vm.ovf");
fileSpec.setSourceType(SourceType.PULL);
TransferEndpoint endpoint = new TransferEndpoint();
endpoint.setUri(URI.create("http://example.com/vm.ovf"));
fileSpec.setSourceEndpoint(endpoint);
client.updateSessionFile().add(sessionId, fileSpec);

Calling UpdateSession.complete()

Once the OVF descriptor is added to the update session, we can safely complete the session while the server will continue the work of pulling the content from the web server.

// complete the session
client.updateSession().complete(sessionId);

Monitoring the Update Session

We can monitor the session state if needed as follows (for example, to ensure no error happened during the import):

// get the session object
UpdateSessionModel updateSession = client.updateSession().get(sessionId);

// wait for upload to complete
while(updateSession.getState().equals(UpdateSessionModel.State.ACTIVE)) {
    // sleep for a while
    Thread.sleep(1000);
    // get current status
    updateSession = client.updateSession().get(sessionId);
}

// check the upload status
if (updateSession.getState().equals(UpdateSessionModel.State.DONE) {
    System.out.println("Upload completed successfully!");
} else if (updateSession.getState().equals(UpdateSessionModel.State.CANCELED) {
    System.out.println("Upload was canceled!");
} else if (updateSession.getState().equals(UpdateSessionModel.State.ERROR) {
    String errorMessage = updateSession.getErrorMessage().getDefaultMessage();
    System.out.println("Upload failed! Error is : " + errorMessage);
}

Uploading an OVF Template from the Local Machine

When uploading files from the local machine, the client needs to push the OVF descriptor as well as any other dependencies of the OVF descriptor. This method is slightly more complicated than the “Import from an HTTP URL” method, because here the client is responsible for streaming all the required files to the Content Library Service.

Create an Update Session

We proceed as usual by creating an update session to upload files into a newly created library item of type OVF:

// create an update session
updateSessionModel = new UpdateSessionModel();
updateSessionModel.setLibraryItemId(itemId);
sessionId = client.updateSession().create(null, updateSessionModel);

Add Required Files to the Update Session

Then we add the OVF descriptor to this session. This time we specify the source type as SourceType.PUSH. The Content Library Service will generate an upload URL which needs to be retrieved from fileInfo.getUploadEndpoint().getUri().

// add the OVF descriptor to the session
fileSpec = new AddSpec();
fileSpec.setName("ttylinux.ovf");
fileSpec.setSourceType(SourceType.PUSH);
com.vmware.content.library.item.updatesession.FileTypes.Info file =
        client.updateSessionFile().add(sessionId, fileSpec);

To do an HTTP PUT on the upload URL, we will use Apache HTTP client. We need a bit of boilerplate code to disable SSL checks on our HTTP client, to keep things as simple as possible for the purpose of this blog post.

SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        builder.build());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
        sslsf).build();

Now, we are ready to upload the file. Since the file is local, we can use the FileEntity to automatically stream the file to the Content Library Service (using the generated upload URL).
Once the OVF descriptor is pushed, we validate the OVF package requirements for the uploaded OVF descriptor. The ValidationResult will have the list of missing files if any. We then upload the missing files to the upload URLs generated by adding the files to the update session.

HttpPut request = new HttpPut(file.getUploadEndpoint().getUri());
HttpEntity content = new FileEntity(new File("/tmp/ovf/basic-debian/debian-box.ovf"));
request.setEntity(content);
HttpResponse response = httpclient.execute(request);
EntityUtils.consumeQuietly(response.getEntity());

// validate the OVF package requirements
ValidationResult result = client.updateSessionFile().validate(sessionId);
for (String missingFile : result.getMissingFiles()) {
    fileSpec = new AddSpec();
    fileSpec.setName(missingFile);
    fileSpec.setSourceType(SourceType.PUSH);
    file = client.updateSessionFile().add(sessionId, fileSpec);

    // get the upload URL for the missing file for pushing the contents
    request = new HttpPut(file.getUploadEndpoint().getUri());

    // we assume that all the required files for the given OVF package are
    // in the same directory as the OVF descriptor. this is the case in most situations
    content = new FileEntity(new File("/tmp/ovf/basic-debian/" + missingFile));
    request.setEntity(content);
    response = httpclient.execute(request);
    EntityUtils.consumeQuietly(response.getEntity());
}

// complete the session
client.updateSession().complete(sessionId);

As shown in the previous section, we can monitor the update session for the upload status.

To summarize, this tutorial explained the basics of the UpdateSession API and how to use it in two different ways to get an OVF template into a content library. In the next tutorial, we explore the APIs to Download Files from a Content Library Item.

More in This Series