How to Import and Export OVF Packages
This article is based on a similar one at vSphere Java API home page. At that time, one of VMware community members sent me an email for samples of using OvfManager APIs. Then I went to office on a Saturday writing two samples, which have been validated by several folks as “working” samples.
The purpose of the samples are to illustrate the vSphere APIs. Let’s take a look at them one by one.
First, ExportOvfToLocal.java. This sample shows how to download either a VM or vApp to your local machine. The typical flow is:
- Find the VM or vApp
- Call their exportVm() or exportVApp() methods and get HttpNfcLease
- Set lease time out
- Wait for HttpNfcLease until it’s ready
- From the HttpNfcLease.info property, find the all URLs from which you download the vmdk files
- Call OvfManager.createDescriptor() API to create the content of ovf and save it to a file along with downloaded vmdk files.
- Release the lease by calling httpNfcLeaseComplete() method
/*================================================================================
Copyright (c) 2008 VMware, Inc. All Rights Reserved.
<pre>Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of VMware, Inc. nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
================================================================================*/
package com.vmware.vim25.mo.samples.ovf;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import com.vmware.vim25.HttpNfcLeaseDeviceUrl;
import com.vmware.vim25.HttpNfcLeaseInfo;
import com.vmware.vim25.HttpNfcLeaseState;
import com.vmware.vim25.OvfCreateDescriptorParams;
import com.vmware.vim25.OvfCreateDescriptorResult;
import com.vmware.vim25.OvfFile;
import com.vmware.vim25.mo.HostSystem;
import com.vmware.vim25.mo.HttpNfcLease;
import com.vmware.vim25.mo.InventoryNavigator;
import com.vmware.vim25.mo.ManagedEntity;
import com.vmware.vim25.mo.ServiceInstance;
import com.vmware.vim25.mo.VirtualApp;
import com.vmware.vim25.mo.VirtualMachine;
/**
* Exports VMDK(s) and OVF Descriptor for a VM or a vApp.
* http://vijava.sf.net
* @author Steve Jin (sjin at vmware.com)
*/
public class ExportOvfToLocal
{
public static LeaseProgressUpdater leaseProgUpdater;
public static void main(String[] args) throws Exception
{
if (args.length != 7)
{
System.out.println("java ExportOvfToLocal <SdkUrl> <username> <password> <VappOrVmName> <hostip> <VirtualMachine|VirtualApp> <localDir>");
System.out.println("java ExportOvfToLocal https://10.20.152.74/sdk root password NewVM1 10.20.152.74 VirtualMachine C:\\Temp\\ovf\\");
return;
}
ServiceInstance si = new ServiceInstance(new URL(args[0]), args[1], args[2], true);
String vAppOrVmName = args[3];
String hostip = args[4];
String entityType = args[5];
String targetDir = args[6];
HostSystem host = (HostSystem) si.getSearchIndex().findByIp(null, hostip, false);
System.out.println("Host Name : " + host.getName());
System.out.println("Network : " + host.getNetworks()[0].getName());
System.out.println("Datastore : " + host.getDatastores()[0].getName());
InventoryNavigator iv = new InventoryNavigator(si.getRootFolder());
HttpNfcLease hnLease = null;
ManagedEntity me = null;
if (entityType.equals("VirtualApp"))
{
me = iv.searchManagedEntity("VirtualApp", vAppOrVmName);
hnLease = ((VirtualApp)me).exportVApp();
}
else
{
me = iv.searchManagedEntity("VirtualMachine", vAppOrVmName);
hnLease = ((VirtualMachine)me).exportVm();
}
// Wait until the HttpNfcLeaseState is ready
HttpNfcLeaseState hls;
for(;;)
{
hls = hnLease.getState();
if(hls == HttpNfcLeaseState.ready)
{
break;
}
if(hls == HttpNfcLeaseState.error)
{
si.getServerConnection().logout();
return;
}
}
System.out.println("HttpNfcLeaseState: ready ");
HttpNfcLeaseInfo httpNfcLeaseInfo = hnLease.getInfo();
httpNfcLeaseInfo.setLeaseTimeout(300*1000*1000);
printHttpNfcLeaseInfo(httpNfcLeaseInfo);
//Note: the diskCapacityInByte could be many time bigger than
//the total size of VMDK files downloaded.
//As a result, the progress calculated could be much less than reality.
long diskCapacityInByte = (httpNfcLeaseInfo.getTotalDiskCapacityInKB()) * 1024;
leaseProgUpdater = new LeaseProgressUpdater(hnLease, 5000);
leaseProgUpdater.start();
long alredyWrittenBytes = 0;
HttpNfcLeaseDeviceUrl[] deviceUrls = httpNfcLeaseInfo.getDeviceUrl();
if (deviceUrls != null)
{
OvfFile[] ovfFiles = new OvfFile[deviceUrls.length];
System.out.println("Downloading Files:");
for (int i = 0; i < deviceUrls.length; i++)
{
String deviceId = deviceUrls[i].getKey();
String deviceUrlStr = deviceUrls[i].getUrl();
String diskFileName = deviceUrlStr.substring(deviceUrlStr.lastIndexOf("/") + 1);
String diskUrlStr = deviceUrlStr.replace("*", hostip);
String diskLocalPath = targetDir + diskFileName;
System.out.println("File Name: " + diskFileName);
System.out.println("VMDK URL: " + diskUrlStr);
String cookie = si.getServerConnection().getVimService().getWsc().getCookie();
long lengthOfDiskFile = writeVMDKFile(diskLocalPath, diskUrlStr, cookie, alredyWrittenBytes, diskCapacityInByte);
alredyWrittenBytes += lengthOfDiskFile;
OvfFile ovfFile = new OvfFile();
ovfFile.setPath(diskFileName);
ovfFile.setDeviceId(deviceId);
ovfFile.setSize(lengthOfDiskFile);
ovfFiles[i] = ovfFile;
}
OvfCreateDescriptorParams ovfDescParams = new OvfCreateDescriptorParams();
ovfDescParams.setOvfFiles(ovfFiles);
OvfCreateDescriptorResult ovfCreateDescriptorResult =
si.getOvfManager().createDescriptor(me, ovfDescParams);
String ovfPath = targetDir + vAppOrVmName + ".ovf";
FileWriter out = new FileWriter(ovfPath);
out.write(ovfCreateDescriptorResult.getOvfDescriptor());
out.close();
System.out.println("OVF Desriptor Written to file: " + ovfPath);
}
System.out.println("Completed Downloading the files");
leaseProgUpdater.interrupt();
hnLease.httpNfcLeaseProgress(100);
hnLease.httpNfcLeaseComplete();
si.getServerConnection().logout();
}
private static void printHttpNfcLeaseInfo(HttpNfcLeaseInfo info)
{
System.out.println("######################## HttpNfcLeaseInfo ###########################");
System.out.println("Lease Timeout: " + info.getLeaseTimeout());
System.out.println("Total Disk capacity: " + info.getTotalDiskCapacityInKB());
HttpNfcLeaseDeviceUrl[] deviceUrlArr = info.getDeviceUrl();
if (deviceUrlArr != null)
{
int deviceUrlCount = 1;
for (HttpNfcLeaseDeviceUrl durl : deviceUrlArr)
{
System.out.println("HttpNfcLeaseDeviceUrl : "
+ deviceUrlCount++);
System.out.println(" Device URL Import Key: "
+ durl.getImportKey());
System.out.println(" Device URL Key: " + durl.getKey());
System.out.println(" Device URL : " + durl.getUrl());
System.out.println(" SSL Thumbprint : " + durl.getSslThumbprint());
}
}
else
{
System.out.println("No Device URLS Found");
}
}
private static long writeVMDKFile(String localFilePath, String diskUrl, String cookie,
long bytesAlreadyWritten, long totalBytes) throws IOException
{
HttpsURLConnection conn = getHTTPConnection(diskUrl, cookie);
InputStream in = conn.getInputStream();
OutputStream out = new FileOutputStream(new File(localFilePath));
byte[] buf = new byte[102400];
int len = 0;
long bytesWritten = 0;
while ((len = in.read(buf)) > 0)
{
out.write(buf, 0, len);
bytesWritten += len;
int percent = (int)(((bytesAlreadyWritten + bytesWritten) * 100) / totalBytes);
leaseProgUpdater.setPercent(percent);
System.out.println("written: " + bytesWritten);
}
in.close();
out.close();
return bytesWritten;
}
private static HttpsURLConnection getHTTPConnection(String urlStr, String cookieStr) throws IOException
{
HostnameVerifier hv = new HostnameVerifier()
{
public boolean verify(String urlHostName, SSLSession session)
{
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);
URL url = new URL(urlStr);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setAllowUserInteraction(true);
conn.setRequestProperty("Cookie", cookieStr);
conn.connect();
return conn;
}
}
Second, ImportLocalOvfVApp.java. This sample shows how to upload a VM or vApp. The flow is:
- Read in the *.ovf file and call OvfManager().createImportSpec() method
- From a ResoucePool, call its importVApp() method() and get HttpNfcLease object
- Wait until the lease is ready
- Upload the vmdk files using the URL provided by the lease object
- Release the lease by calling httpNfcLeaseComplete() method
Note: with VI Java API 5.0 GA, remove the line “ovfDescriptor = escapeSpecialChars(ovfDescriptor);” becuase escaping is added to underlying engine.
/*================================================================================
Copyright (c) 2008 VMware, Inc. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of VMware, Inc. nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
================================================================================*/
package com.vmware.vim25.mo.samples.ovf;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import com.vmware.vim25.*;
import com.vmware.vim25.mo.ComputeResource;
import com.vmware.vim25.mo.Folder;
import com.vmware.vim25.mo.HostSystem;
import com.vmware.vim25.mo.HttpNfcLease;
import com.vmware.vim25.mo.ResourcePool;
import com.vmware.vim25.mo.ServiceInstance;
/**
* Deploy VM or vApp from local disk to an ESX(i) server
* http://vijava.sf.net
* @author Steve Jin (sjin at vmware.com)
*/
public class ImportLocalOvfVApp
{
private static final int CHUCK_LEN = 64 * 1024;
public static LeaseProgressUpdater leaseUpdater;
public static void main(String[] args) throws Exception
{
if (args.length < 6)
{
System.out.println(
"java ImportLocalOvfVApp <targetURL> <username> <password> <hostip> <OVFFile LocalPath> <NewVMName>");
System.out.println(
"java ImportLocalOvfVApp https://10.20.140.47/sdk Administrator password 10.17.204.115 E:/Downloads/Nostalgia.ovf NewVM");
return;
}
ServiceInstance si = new ServiceInstance(new URL(args[0]), args[1], args[2], true);
String ovfLocal = args[4];
String hostip = args[3];
String newVmName = args[5];
HostSystem host = (HostSystem) si.getSearchIndex().findByIp(null, hostip, false);
System.out.println("Host Name : " + host.getName());
System.out.println("Network : " + host.getNetworks()[0].getName());
System.out.println("Datastore : " + host.getDatastores()[0].getName());
Folder vmFolder = (Folder) host.getVms()[0].getParent();
OvfCreateImportSpecParams importSpecParams = new OvfCreateImportSpecParams();
importSpecParams.setHostSystem(host.getMOR());
importSpecParams.setLocale("US");
importSpecParams.setEntityName(newVmName);
importSpecParams.setDeploymentOption("");
OvfNetworkMapping networkMapping = new OvfNetworkMapping();
networkMapping.setName("Network 1");
networkMapping.setNetwork(host.getNetworks()[0].getMOR()); // network);
importSpecParams.setNetworkMapping(new OvfNetworkMapping[] { networkMapping });
importSpecParams.setPropertyMapping(null);
String ovfDescriptor = readOvfContent(ovfLocal);
if (ovfDescriptor == null)
{
si.getServerConnection().logout();
return;
}
ovfDescriptor = escapeSpecialChars(ovfDescriptor);
System.out.println("ovfDesc:" + ovfDescriptor);
ResourcePool rp = ((ComputeResource)host.getParent()).getResourcePool();
OvfCreateImportSpecResult ovfImportResult = si.getOvfManager().createImportSpec(
ovfDescriptor, rp, host.getDatastores()[0], importSpecParams);
if(ovfImportResult==null)
{
si.getServerConnection().logout();
return;
}
long totalBytes = addTotalBytes(ovfImportResult);
System.out.println("Total bytes: " + totalBytes);
HttpNfcLease httpNfcLease = null;
httpNfcLease = rp.importVApp(ovfImportResult.getImportSpec(), vmFolder, host);
// Wait until the HttpNfcLeaseState is ready
HttpNfcLeaseState hls;
for(;;)
{
hls = httpNfcLease.getState();
if(hls == HttpNfcLeaseState.ready || hls == HttpNfcLeaseState.error)
{
break;
}
}
if (hls.equals(HttpNfcLeaseState.ready))
{
System.out.println("HttpNfcLeaseState: ready ");
HttpNfcLeaseInfo httpNfcLeaseInfo = (HttpNfcLeaseInfo) httpNfcLease.getInfo();
printHttpNfcLeaseInfo(httpNfcLeaseInfo);
leaseUpdater = new LeaseProgressUpdater(httpNfcLease, 5000);
leaseUpdater.start();
HttpNfcLeaseDeviceUrl[] deviceUrls = httpNfcLeaseInfo.getDeviceUrl();
long bytesAlreadyWritten = 0;
for (HttpNfcLeaseDeviceUrl deviceUrl : deviceUrls)
{
String deviceKey = deviceUrl.getImportKey();
for (OvfFileItem ovfFileItem : ovfImportResult.getFileItem())
{
if (deviceKey.equals(ovfFileItem.getDeviceId()))
{
System.out.println("Import key==OvfFileItem device id: " + deviceKey);
String absoluteFile = new File(ovfLocal).getParent() + File.separator + ovfFileItem.getPath();
String urlToPost = deviceUrl.getUrl().replace("*", hostip);
uploadVmdkFile(ovfFileItem.isCreate(), absoluteFile, urlToPost, bytesAlreadyWritten, totalBytes);
bytesAlreadyWritten += ovfFileItem.getSize();
System.out.println("Completed uploading the VMDK file:" + absoluteFile);
}
}
}
leaseUpdater.interrupt();
httpNfcLease.httpNfcLeaseProgress(100);
httpNfcLease.httpNfcLeaseComplete();
}
si.getServerConnection().logout();
}
public static long addTotalBytes(OvfCreateImportSpecResult ovfImportResult)
{
OvfFileItem[] fileItemArr = ovfImportResult.getFileItem();
long totalBytes = 0;
if (fileItemArr != null)
{
for (OvfFileItem fi : fileItemArr)
{
printOvfFileItem(fi);
totalBytes += fi.getSize();
}
}
return totalBytes;
}
private static void uploadVmdkFile(boolean put, String diskFilePath, String urlStr,
long bytesAlreadyWritten, long totalBytes) throws IOException
{
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String urlHostName, SSLSession session)
{
return true;
}
});
HttpsURLConnection conn = (HttpsURLConnection) new URL(urlStr).openConnection();
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setChunkedStreamingMode(CHUCK_LEN);
conn.setRequestMethod(put? "PUT" : "POST"); // Use a post method to write the file.
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "application/x-vnd.vmware-streamVmdk");
conn.setRequestProperty("Content-Length", Long.toString(new File(diskFilePath).length()));
BufferedOutputStream bos = new BufferedOutputStream(conn.getOutputStream());
BufferedInputStream diskis = new BufferedInputStream(new FileInputStream(diskFilePath));
int bytesAvailable = diskis.available();
int bufferSize = Math.min(bytesAvailable, CHUCK_LEN);
byte[] buffer = new byte[bufferSize];
long totalBytesWritten = 0;
while (true)
{
int bytesRead = diskis.read(buffer, 0, bufferSize);
if (bytesRead == -1)
{
System.out.println("Total bytes written: " + totalBytesWritten);
break;
}
totalBytesWritten += bytesRead;
bos.write(buffer, 0, bufferSize);
bos.flush();
System.out.println("Total bytes written: " + totalBytesWritten);
int progressPercent = (int) (((bytesAlreadyWritten + totalBytesWritten) * 100) / totalBytes);
leaseUpdater.setPercent(progressPercent);
}
diskis.close();
bos.flush();
bos.close();
conn.disconnect();
}
public static String readOvfContent(String ovfFilePath) throws IOException
{
StringBuffer strContent = new StringBuffer();
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(ovfFilePath)));
String lineStr;
while ((lineStr = in.readLine()) != null)
{
strContent.append(lineStr);
}
in.close();
return strContent.toString();
}
private static void printHttpNfcLeaseInfo(HttpNfcLeaseInfo info)
{
System.out.println("================ HttpNfcLeaseInfo ================");
HttpNfcLeaseDeviceUrl[] deviceUrlArr = info.getDeviceUrl();
for (HttpNfcLeaseDeviceUrl durl : deviceUrlArr)
{
System.out.println("Device URL Import Key: " + durl.getImportKey());
System.out.println("Device URL Key: " + durl.getKey());
System.out.println("Device URL : " + durl.getUrl());
System.out.println("Updated device URL: " + durl.getUrl());
}
System.out.println("Lease Timeout: " + info.getLeaseTimeout());
System.out.println("Total Disk capacity: " + info.getTotalDiskCapacityInKB());
System.out.println("==================================================");
}
private static void printOvfFileItem(OvfFileItem fi)
{
System.out.println("================ OvfFileItem ================");
System.out.println("chunkSize: " + fi.getChunkSize());
System.out.println("create: " + fi.isCreate());
System.out.println("deviceId: " + fi.getDeviceId());
System.out.println("path: " + fi.getPath());
System.out.println("size: " + fi.getSize());
System.out.println("==============================================");
}
public static String escapeSpecialChars(String str)
{
str = str.replaceAll("<", "<");
return str.replaceAll(">", ">"); // do not escape "&" -> "&", "\"" -> """
}
}
While you are downloading or uploading the files, you would also like to update the progress with the httpNfcLease.httpNfcLeaseProgress() API. This is done periodically in a separate thread with both samples. Note that the size calculation is not accurate, so you may find mismatch there.
/*================================================================================
Copyright (c) 2008 VMware, Inc. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of VMware, Inc. nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
================================================================================*/
package com.vmware.vim25.mo.samples.ovf;
import com.vmware.vim25.mo.HttpNfcLease;
/**
* http://vijava.sf.net
* @author Steve Jin (sjin at vmware.com)
*/
public class LeaseProgressUpdater extends Thread
{
private HttpNfcLease httpNfcLease = null;
private int progressPercent = 0;
private int updateInterval;
public LeaseProgressUpdater(HttpNfcLease httpNfcLease, int updateInterval)
{
this.httpNfcLease = httpNfcLease;
this.updateInterval = updateInterval;
}
public void run()
{
while (true)
{
try
{
httpNfcLease.httpNfcLeaseProgress(progressPercent);
Thread.sleep(updateInterval);
}
catch(InterruptedException ie)
{
break;
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
}
public void setPercent(int percent)
{
this.progressPercent = percent;
}
}
You can import an OVF package from a URL using vSphere Client. How to implement it using the API? Easy. Instead of reading from a local file, you read from the URL. I leave it to you to try out.
For more details, check out the code repository. I have tested both samples in a round trip: export a VM to a local disk, and then import it back. The imported VM works well.
Marcus Ludvigson from Lawson sent me an email with a fix for unfinished upload disk file issue that happens occasionally. You need to add the following two sections of code into uploadVmdkFile() method.
conn.setDoInput(true);
try
{
DataInputStream dis = new DataInputStream(conn.getInputStream());
dis.close();
} catch (Exception e) {}
Great Sample. Is there any C# code for the same functionality?
thank you
Thanks xmzhu! Sorry that I don’t have C# code, but you can port it easily. I know a company ported the samples to C#.
-Steve
I am using Perl SDK for some functionalities. Is there a similar code out there for Perl to deploy an OVF.
Functionality wise, VI Perl is very similar to VI Java. Therefore you can port the Java code to Perl. I don’t have Perl sample. You want to check with the VI Perl King @lamw (William Lam) if his vGhetto script library has such a sample.
-Steve
This is awesome. I bought the book. Anyway, for this particular code sample, will it work on a legacy 3.5 ESX host? Frank Yang says hi.
Chris,
Thanks a lot for your support on my book! Glad to know you work with Frank.
The code sample does not work on a legacy 3.5 because it relies on new APIs in vSphere 4.
Steve
Are you aware of a sample script that would export a VM in OVF 0.9 format off of a 3.5 ESX host?
How long is the typical wait time for the httpnfclease to change to the ready state?
I tried to implement the above in C and gSOAP, using VIM 2.5, but the available WSDL (in SDK 4.1.0-257238) does not seem to be useable at all. In particular the definition of VirtualMachineImportSpec is simply absent. As a result, gSOAP aborts verifying the response of CreateImportSpec() method. Man, Java programmers are so sneaky.
@Chris
Depends on the load, but should be nearly instantaneous. In most cases my poll loop returns after 1 RPC with “ready”.
Thanks! This was quite useful. You can expect support for downloading OVFs in rbvmomi and RVC (Ruby vSphere Console) in the not so distant future thanks to the examples on this page.
I’m new to VI java. when i tried to use ImportLocalOvfVApp.java, got the following error whle Creating the Import Spec. Am i missing any reference? Currently using vijava2120100824src.jar and dom4j-1.6.1.jar for reference. Could anyone help me in this?
Exception in thread “main” java.rmi.RemoteException: VI SDK invoke exception:org.dom4j.DocumentException: null Nested exception: null
at com.vmware.vim25.ws.WSClient.invoke(WSClient.java:181)
at com.vmware.vim25.ws.WSClient.invoke(WSClient.java:123)
at com.vmware.vim25.ws.VimStub.createImportSpec(VimStub.java:1193)
at com.vmware.vim25.mo.OvfManager.createImportSpec(OvfManager.java:69)
at com.vmware.vim25.mo.samples.ovf.ImportLocalOvfVApp.main(ImportLocalOvfVApp.java:75)
I don’t think you missed any reference as long as you had the two jar files. What is your target server version? Thanks!
Steve
I just got these error messages, too:
java.rmi.RemoteException: VI SDK invoke exception:org.dom4j.DocumentException: null Nested exception: null
at com.vmware.vim25.ws.WSClient.invoke(WSClient.java:181)
at com.vmware.vim25.ws.WSClient.invoke(WSClient.java:123)
at com.vmware.vim25.ws.VimStub.createImportSpec(VimStub.java:1193)
at com.vmware.vim25.mo.OvfManager.createImportSpec(OvfManager.java:69)
We have run this function many times, this is the first time I got this error message.
The server is ESXi 4.1.0, 348481
Hi Wendy,
What is different this time?
Steve
Hi, Steve
I don’t think there is any difference this time.
This is our production environment and we did not change the java code.
I used the same ovf file deploy a VM to the same server successfully a few hours age.
That’s why I am confused and have no idea about where to start to find the root cause.
Can you give me some hints? Is it possible that I accidentally changed some configuration of the server and make this error?
Thank you so much!
I just found the root cause.
There is an “&” in one of the parameters!!
the issue was in the ovf file used. There were some unsupported chars in the ovf file in license tag. After removing that it works fine. thanks
@amu
Hi Amu, You are very welcome. Glad it works for you now. -Steve
I have test this project. But when I upload vmdk , it always shows
Exception in thread “main” java.io.IOException: Error writing request body to server
at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.checkError(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.write(Unknown Source)
at java.io.BufferedOutputStream.write(Unknown Source)
at ImportLocalOvfVapp.uploadVmdkFile(ImportLocalOvfVapp.java:220)
at ImportLocalOvfVapp.main(ImportLocalOvfVapp.java:150)
Is there someone meeting the same error?
What version of JRE did you run? There is a Java bug in previous versions of JRE. Please check it out here:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6631048
Steve
Thanks for your answer.
I want to know if my ovf directory contains .iso files. And .ovf file contains the information of iso file too.
How do I upload this ovf template using this program?
Cause when I use this program to upload.It just write once and throw error. It always shows:
C:\Atmos\Atmos-file1.isoBuffersize65536
Total bytes written: 65536
Exception in thread “main” java.io.IOException: Error writing request body to server
at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.checkError(Unknown Source)
at sun.net.www.protocol.http.HttpURLConnection$StreamingOutputStream.write(Unknown Source)
at java.io.BufferedOutputStream.write(Unknown Source)
at ImportLocalOvfVApp.uploadVmdkFile(ImportLocalOvfVApp.java:249)
at ImportLocalOvfVApp.main(ImportLocalOvfVApp.java:166)
Should I modify the conn.setChunkedStreamingMode(CHUCK_LEN) or conn.setRequestProperty(“Content-Type”, “application/x-vnd.vmware-streamVmdk”) ?
Thanks
I think you can give it a try. Please let me know how it goes.
Steve
How to deal with the OVA file which already has OVF and VMDK bundeled in single file.
If i try to deploy OVA it throws “Exception in thread “main” java.lang.OutOfMemoryError: Java heap space” which is expected as it cannot read a huge 500mb file.
Is there any other alternative for reading OVA file?
Hi Amol,
According to this wiki page, the OVA is just a TAR that includes OVF and disk files. You can extract it before uploading. For the OutOfMemoryError, it’s another issue. Did you allocate a huge byte array? Thanks!
Steve
Hi Steve.
Thanks for the quick reply.
The problem was with “readOvfContent(ovfLocal)”, as per the code we read the content into “StringBuffer” which indeed throwed OutOfMemory error because of the OVA (OVF + VMDK) file , I used “jtar” api to extract the OVA file into temp directory and then processed the OVF file which worked for me. @Steve Jin
Thanks for letting me know. Glad it works for you. OVF is just a few KB, therefore the sample reads all.
Steve
A quick question:
I am beginning to hit following error almost everytime I do export:
java.lang.NullPointerException
at com.vmware.vim25.mo.OvfManager.createDescriptor(OvfManager.java:64)
at com.bmc.bco.build.vm.automation.vmware.VMInterface.createOvfDescriptor(VMInterface.java:286)
at com.bmc.bco.build.vm.automation.vmware.VMInterface.createOvfImage(VMInterface.java:275)
at com.bmc.bco.build.vm.automation.vmware.VMInterface.exportVM(VMInterface.java:207)
at com.bmc.bco.build.vm.automation.vmware.VMInterfaceThread.runMultiple(VMInterfaceThread.java:58)
at com.bmc.bco.build.vm.automation.vmware.VMInterfaceThread.run(VMInterfaceThread.java:39)
at com.bmc.bco.build.vm.automation.App.executeCommand(App.java:205)
at com.bmc.bco.build.vm.automation.App.processCommands(App.java:161)
at com.bmc.bco.build.vm.automation.App.doVMPrep(App.java:140)
at com.bmc.bco.build.vm.automation.App.main(App.java:72)
Exception in thread “Thread-1″ java.lang.RuntimeException: java.lang.NullPointerException
at com.bmc.bco.build.vm.automation.vmware.LeaseProgressUpdater.run(LeaseProgressUpdater.java:65)
Caused by: java.lang.NullPointerException
at com.vmware.vim25.mo.HttpNfcLease.httpNfcLeaseProgress(HttpNfcLease.java:92)
at com.bmc.bco.build.vm.automation.vmware.LeaseProgressUpdater.run(LeaseProgressUpdater.java:56)
I am pretty much clueless about the cause.
Also why is native export so fast compared to the sample code? How can one achieve the same speed?
Thanks!
What version of vijava are you using? You can debug into this since you can download API source code and watch the NullPointerException.
Good luck.
Steve
ok steve, I will do that. The version that I am using is the latest 5.0 version 520110926.
However I am still intrigued by the speed of the operation. A native export is very fast, the percentage completion quickly jumps 10% but via the API its really slow. Any clues/advice?
Thanks
Thanks for the information. Have you compared the total time? The GUI jumping 10% quickly is not really an objective comparison.
Steve
Usefull article, thank you. But could you please explain in detailes fix of unfinished disk upload, mentioned at the end? I’m implementing OVF export on C# and have encountered with such error. It would be easear to choose .Net analogue when proposed solution is understood.
I think it’s closely related to the Java API implementation. You may want to look into the C# API for a workaround.
Steve
Hi,
Anyone tried reading OVF template from ftp url? I am having issues updating the progress.
- acecat
Hey there,
I am successfully exporting into an ovf but I have an exception when importing it back:
com.vmware.vim25.InvalidType
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at java.lang.Class.newInstance0(Class.java:355)
at java.lang.Class.newInstance(Class.java:308)
at com.vmware.vim25.ws.XmlGen.fromXml(XmlGen.java:201)
at com.vmware.vim25.ws.XmlGen.parseSoapFault(XmlGen.java:80)
at com.vmware.vim25.ws.WSClient.invoke(WSClient.java:132)
at com.vmware.vim25.ws.VimStub.createImportSpec(VimStub.java:1193)
at com.vmware.vim25.mo.OvfManager.createImportSpec(OvfManager.java:69)
We are using vijava2120100824.
I’ve been trying to dig into vi code but I can’t find what type can be invalid …
Any help greatly appreciated!
Hey,
I’m following up my previous post.
I was working with an invalid HostSystem id. :s
It works now!
Thank you for the code!
Glad it worked for you. Steve
@Acecat
Figured out the issue. Thanks.
Steve,
VI Java interface is great. I have import and export both working. However, how do I programmatically reconfigure/change new VM parameters like number of CPU, memory, disk size, etc before importing. I was thinking about programmatically modify ovf file before importing. But are there a complete set of API’s like OvfCreateImportSpecParams.setEntityName( ) to set those other system parameters? That would make my life much easier. Directly modifying OVF file is really messy.
Thanks,
Min
Thanks Min,
Glad you had a good experience with vijava API. I see what you wanted, but I am not aware of such API to manipulate OVF file directly. In my opinion, the DMTF should offer such a tool.
Steve
Hi Steve,
Thanks for your fantastic posting, this example works very well in my local environment. I just wonder if there are some APIs I can use to “tar” the exported OVF files into OVA? Or I have to call the OVF converter tool to compress the OVF file after it is exported?
Thanks,
Terry
Hi Terry,
Glad it works well for you. Once you have the exported OVF files locally, I think you can use CLI to “tar” them and then change the suffix.
Steve
@Steve Jin
Thanks a lot for your quick response and confirming. I will try the CLI approach.
-Terry
Hi,
Thanks for your very informative article. With reference to this line of code:
//Note: the diskCapacityInByte could be many time bigger than
//the total size of VMDK files downloaded.
//As a result, the progress calculated could be much less than reality.
long diskCapacityInByte = (httpNfcLeaseInfo.getTotalDiskCapacityInKB()
Is there any way I can get the actual size of the VMDK files before I download them. This is very important for me.
Cheers,
Alan.
You can get it from the layoutEx property of the VirtualMachine to be downloaded.
Steve
Hi Steve,
Thank you for your reply. There is VirtualMachineFileLayoutExFileInfo[] in the layoutEx property that you mention but this gives me the list of flat VMDKs. What you actually export when downloading a VM is a list of sparse VMDKs which are usually smaller in size (how much smaller depends on how much of each disk is being used).
Alan.
Thanks for the info. This is highly possible with thin disks. How smaller in size did you see?
I checked one of my VMs – in the VirtualMachineFileLayoutFileInfo its size is
7,204,356,096; and in the Web based datastore browser, 17,179,869,184. The latter is close to what is shown in the vSphere Client as provisioned. I assume the downloading size will be equal or smaller than the first one.
Steve
@Steve Jin
Hi Steve,
In my case, the difference between the value shown in VirtualMachineFileLayoutFileInfo and the actual size of the downloaded VMDK is quite big as the VM is only using around 10% of the available disk space. And, as you said, the value shown in the datastore browser is of no help either in this case.
I also tried looking at the contentLength of the URL before downloading the VMDK, however this info is not always available (sometimes URL.getContentLength() returns -1).
It seems like there is no reliable way of getting the size of the VMDKs that are leased out before they are actually downloaded. You’d think vmware’s api would make this valuable info available. Oh well …
Alan
Hi Alan,
I don’t think VMware is going to change this anytime soon because the size is mainly used for estimating time which people don’t noramlly take too seriously. That being said, you may have a different reason for getting the exact size info, and things could be thus different.
Steve