Skip to content

Instantly share code, notes, and snippets.

@vmwarecode
Created June 27, 2016 20:53
Show Gist options
  • Save vmwarecode/51065e9ce58c2c53539da13bc8833955 to your computer and use it in GitHub Desktop.
Save vmwarecode/51065e9ce58c2c53539da13bc8833955 to your computer and use it in GitHub Desktop.
PutVMFiles
/*
* ****************************************************************************
* Copyright VMware, Inc. 2010-2016. All Rights Reserved.
* ****************************************************************************
*
* This software is made available for use under the terms of the BSD
* 3-Clause license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 THE
* COPYRIGHT HOLDER 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.httpfileaccess;
import com.vmware.common.annotations.Action;
import com.vmware.common.annotations.Option;
import com.vmware.common.annotations.Sample;
import com.vmware.connection.ConnectedVimServiceBase;
import com.vmware.connection.KeepAlive;
import com.vmware.vim25.*;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.MessageContext;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.*;
/**
* <pre>
* PutVMFiles
*
* This sample puts VM files in specified Datacenter and
* Datastore and register and reconfigure the particular VM
*
* <b>Parameters:</b>
* url [required]: url of the web service.
* username [required]: username for the authentication
* password [required]: password for the authentication
* vmname [required]: Name of the virtual machine
* localpath [required]: Local path containing virtual machine files
* datacentername [required]: Name of the target datacenter
* datastorename [required]: Name of the target datastore
*
* <b>Command Line:</b>
* run.bat com.vmware.httpfileaccess.PutVMFiles
* --url [URLString] --username [username] --password [password]
* --vmname [VM name] --localpath [local path]
* --datacentername [datacenter name]
* --datastorename [datastore name]
* </pre>
*/
@Sample(
name = "put-vm-files",
description = "This sample puts VM files in specified Datacenter and Datastore" +
" and register and reconfigure the particular VM. The VM you use, should be downloaded" +
" from the vSphere you are uploading to. The name of the VM, VM folder, and VM disk files" +
" should all be the same. The name of the VM should be unique and unused on the Host." +
" This works best if you use a VM you obtained through GetVMFiles."
)
public class PutVMFiles extends ConnectedVimServiceBase {
private String cookieValue = "";
private ManagedObjectReference registeredVMRef = null;
boolean verbose = true;
String vmName = null;
String localPath = null;
String datacenter = null;
String datastore = null;
@Option(
name = "vmname",
description = "Name of the virutal machine to upload. " +
"Should be unique and unused. Should be the same as the name of the vm folder and vm file names.")
public void setVmName(final String vmName) {
this.vmName = vmName;
}
@Option(
name = "localpath",
description =
"Local path from which files will be copied. " +
"This should be the path holding the virtual machine folder but not the vm folder itself."
)
public void setLocalPath(final String localPath) {
// the program will look in here for a virtualmachine.vm folder
this.localPath = localPath;
}
@Option(name = "datacentername", description = "Name of the target datacenter")
public void setDatacenter(final String datacenter) {
this.datacenter = datacenter;
}
@Option(name = "datastorename", description = "Name of the target datastore")
public void setDatastore(final String datastore) {
this.datastore = datastore;
}
@Option(name = "verbose", required = false, description = "" +
"defaults to 'true' and prints more information, " +
"set to 'false' to print less.")
public void setVerbose(final Boolean verbosity) {
this.verbose = verbosity;
}
/**
* This method returns a boolean value specifying whether the Task is
* succeeded or failed.
*
* @param task ManagedObjectReference representing the Task.
* @return boolean value representing the Task result.
* @throws InvalidCollectorVersionFaultMsg
*
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
*/
boolean getTaskResultAfterDone(ManagedObjectReference task)
throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg,
InvalidCollectorVersionFaultMsg {
boolean retVal = false;
// info has a property - state for state of the task
Object[] result =
waitForValues.wait(task, new String[]{"info.state", "info.error"},
new String[]{"state"}, new Object[][]{new Object[]{
TaskInfoState.SUCCESS, TaskInfoState.ERROR}});
if (result[0].equals(TaskInfoState.SUCCESS)) {
retVal = true;
}
if (result[1] instanceof LocalizedMethodFault) {
throw new PutVMFilesException(
((LocalizedMethodFault) result[1]).getLocalizedMessage());
}
return retVal;
}
boolean customValidation() {
boolean validate = false;
try {
if (datacenter != null && datacenter.length() != 0
&& datastore != null && datastore.length() != 0 ) {
ManagedObjectReference dcmor =
getMOREFs.inContainerByType(serviceContent.getRootFolder(),
"Datacenter").get(datacenter);
if (dcmor != null) {
ManagedObjectReference ds =
getMOREFs.inContainerByType(dcmor, "Datastore").get(datastore);
if (ds == null) {
System.out.println("Specified Datastore with name " + datastore
+ " was not" + " found in specified Datacenter");
return validate;
}
validate = true;
} else {
System.out.println("Specified Datacenter with name " + datacenter
+ " not Found");
return validate;
}
}
} catch (RuntimeFaultFaultMsg runtimeFaultFaultMsg) {
throw new PutVMFilesException(runtimeFaultFaultMsg);
} catch (InvalidPropertyFaultMsg invalidPropertyFaultMsg) {
throw new PutVMFilesException(invalidPropertyFaultMsg);
}
return validate;
}
/**
* Lists out the subdirectories and files under the localDir you specified.
*
* @param dir - place on the file system to look
* @return - list of files under that location
*/
String[] getDirFiles(final File dir) {
if (dir.exists() && dir.isDirectory()) {
return dir.list();
} else {
throw new RuntimeException("Local Path Doesn't Exist: " + dir.toString());
}
}
@SuppressWarnings("unchecked")
void putVMFiles(final String remoteFilePath, final File localFile) {
final String url = connection.getUrl();
final String serviceUrl = url.substring(0, url.lastIndexOf("sdk") - 1);
String httpUrl =
serviceUrl + "/folder" + remoteFilePath + "?dcPath=" + datacenter
+ "&dsName=" + datastore;
httpUrl = httpUrl.replaceAll("\\ ", "%20");
System.out.printf("%nPutting VM File %s ", httpUrl);
final URL fileURL;
final HttpURLConnection conn;
try {
fileURL = new URL(httpUrl);
conn = (HttpURLConnection) fileURL.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setAllowUserInteraction(true);
} catch (MalformedURLException e) {
throw new PutVMFilesException(e);
} catch (IOException e) {
throw new PutVMFilesException(e);
}
// Maintain session
final List<String> cookies = (List<String>) headers.get("Set-cookie");
cookieValue = (cookies != null)?cookies.get(0):"";
final StringTokenizer tokenizer = new StringTokenizer(cookieValue, ";");
cookieValue = tokenizer.nextToken();
final String path = "$" + tokenizer.nextToken();
final String cookie = "$Version=\"1\"; " + cookieValue + "; " + path;
// set the cookie in the new request header
final Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("Cookie", Collections.singletonList(cookie));
((BindingProvider) vimPort).getRequestContext().put(
MessageContext.HTTP_REQUEST_HEADERS, map);
conn.setRequestProperty("Cookie", cookie);
conn.setRequestProperty("Content-Type", "application/octet-stream");
try {
conn.setRequestMethod("PUT");
} catch (ProtocolException e) {
throw new PutVMFilesException(e);
}
conn.setRequestProperty("Content-Length", "1024");
long fileLen = localFile.length();
System.out.println("File size is: " + fileLen);
// setChunkedStreamingMode to -1 turns off chunked mode
// setChunkedStreamingMode to 0 asks for system default
// NOTE:
// larger values mean faster connections at the
// expense of more heap consumption.
conn.setChunkedStreamingMode(0);
OutputStream out = null;
InputStream in = null;
try {
out = conn.getOutputStream();
in = new BufferedInputStream(new FileInputStream(localFile));
int bufLen = 9 * 1024;
byte[] buf = new byte[bufLen];
byte[] tmp = null;
int len = 0;
// this can take a very long time, so we do a keep-alive here.
Thread keepAlive = KeepAlive.keepAlive(vimPort, getServiceInstanceReference());
keepAlive.start();
final String[] spinner = new String[] {"\u0008/", "\u0008-", "\u0008\\", "\u0008|" };
System.out.printf(".");
int i = 0;
while ((len = in.read(buf, 0, bufLen)) != -1) {
tmp = new byte[len];
System.arraycopy(buf, 0, tmp, 0, len);
out.write(tmp, 0, len);
if (verbose) {
System.out.printf("%s", spinner[i++ % spinner.length]);
}
}
System.out.printf("\u0008");
keepAlive.interrupt();
} catch (FileNotFoundException e) {
throw new PutVMFilesException(e);
} catch (IOException e) {
throw new PutVMFilesException(e);
} finally {
try {
if(in!=null) in.close();
if(out!=null) out.close();
conn.getResponseCode();
} catch (IOException e) {
throw new PutVMFilesException(e);
}
conn.disconnect();
}
}
/**
* Copy contents of this directory up to the datastore
*
* @param dirName
* @throws IOException
*/
public void copyDir(String dirName) {
System.out.print("Copying The Virtual Machine To Host...");
File dir = new File(localPath,dirName);
String[] listOfFiles = getDirFiles(dir);
for (int i = 0; i < listOfFiles.length; i++) {
String remoteFilePath;
File localFile = new File(dir,listOfFiles[i]);
if (localFile.getAbsolutePath().indexOf("vdisk") != -1) {
remoteFilePath =
"/" + vmName + "/" + datastore + "/" + listOfFiles[i];
} else {
remoteFilePath = "/" + vmName + "/" + listOfFiles[i];
}
putVMFiles(remoteFilePath, localFile);
if (verbose) {
System.out.print("*");
}
}
System.out.println("...Done");
}
/**
* register the vmx (virtual machine file) we just placed.
*
* @return
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
* @throws OutOfBoundsFaultMsg
* @throws DuplicateNameFaultMsg
* @throws NotFoundFaultMsg
* @throws VmConfigFaultFaultMsg
* @throws InsufficientResourcesFaultFaultMsg
*
* @throws AlreadyExistsFaultMsg
* @throws InvalidDatastoreFaultMsg
* @throws FileFaultFaultMsg
* @throws InvalidStateFaultMsg
* @throws InvalidNameFaultMsg
* @throws InvalidCollectorVersionFaultMsg
*
*/
boolean registerVirtualMachine() throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg, OutOfBoundsFaultMsg, DuplicateNameFaultMsg, NotFoundFaultMsg, VmConfigFaultFaultMsg, InsufficientResourcesFaultFaultMsg, AlreadyExistsFaultMsg, InvalidDatastoreFaultMsg, FileFaultFaultMsg, InvalidStateFaultMsg, InvalidNameFaultMsg, InvalidCollectorVersionFaultMsg {
boolean registered = false;
System.out.print("Registering The Virtual Machine ...");
ManagedObjectReference hostmor = null;
// Get the Datacenter
final ManagedObjectReference dcmor =
getMOREFs.inContainerByType(serviceContent.getRootFolder(),
"Datacenter").get(datacenter);
// Get the Datastore
final ManagedObjectReference dsmor =
getMOREFs.inContainerByType(dcmor, "Datastore").get(datastore);
final List<DatastoreHostMount> hostmounts =
((ArrayOfDatastoreHostMount) getMOREFs.entityProps(dsmor,
new String[]{"host"}).get("host")).getDatastoreHostMount();
for (DatastoreHostMount datastoreHostMount : hostmounts) {
if (datastoreHostMount == null) {
throw new PutVMFilesException("datastore " + datastore + " has no host mounts!");
}
HostMountInfo mountInfo = datastoreHostMount.getMountInfo();
if (mountInfo == null) {
throw new PutVMFilesException("datastoreHostMount on " + datastore + " has no info!");
}
final Boolean accessible = mountInfo.isAccessible();
// the values "accessible" and "mounted" need not be set by the server.
final Boolean mounted = mountInfo.isMounted();
// if mounted is not set, assume it is true
if ((accessible != null && accessible) && (mounted == null || mounted)) {
hostmor = datastoreHostMount.getKey();
break;
}
if (verbose) {
System.out.print(".");
}
}
if (hostmor == null) {
throw new PutVMFilesException("No host connected to the datastore "
+ datastore);
}
final ManagedObjectReference crmor =
(ManagedObjectReference) getMOREFs.entityProps(hostmor,
new String[]{"parent"}).get("parent");
final ManagedObjectReference resourcePoolRef =
(ManagedObjectReference) getMOREFs.entityProps(crmor,
new String[]{"resourcePool"}).get("resourcePool");
final ManagedObjectReference vmFolderMor =
(ManagedObjectReference) getMOREFs.entityProps(dcmor,
new String[]{"vmFolder"}).get("vmFolder");
// Get The vmx path
final String vmxPath = "[" + datastore + "] " + vmName + "/" + vmName + ".vmx";
System.out.printf("...trying to register: %s ...", vmxPath);
// Registering The Virtual machine
final ManagedObjectReference taskmor =
vimPort.registerVMTask(vmFolderMor, vmxPath, vmName, false,
resourcePoolRef, hostmor);
if (getTaskResultAfterDone(taskmor)) {
System.out.print("*");
registered = true;
registeredVMRef =
(ManagedObjectReference) getMOREFs.entityProps(taskmor,
new String[]{"info.result"}).get("info.result");
System.out.print("VM registered with value "
+ registeredVMRef.getValue());
System.out.println("...Done.");
} else {
System.out.print("Some Exception While Registering The VM");
registered = false;
System.out.println(" FAILED!");
}
return registered;
}
/**
* Reconfigure the virtual machine we placed on the datastore
*
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
* @throws InvalidCollectorVersionFaultMsg
*
* @throws DuplicateNameFaultMsg
* @throws TaskInProgressFaultMsg
* @throws VmConfigFaultFaultMsg
* @throws InsufficientResourcesFaultFaultMsg
*
* @throws InvalidDatastoreFaultMsg
* @throws FileFaultFaultMsg
* @throws ConcurrentAccessFaultMsg
* @throws InvalidStateFaultMsg
* @throws InvalidNameFaultMsg
*/
void reconfigVirtualMachine() {
try {
System.out.println("ReConfigure The Virtual Machine ..........");
VirtualMachineFileInfo vmFileInfo = new VirtualMachineFileInfo();
vmFileInfo.setLogDirectory("[" + datastore + "] " + vmName);
vmFileInfo.setSnapshotDirectory("[" + datastore + "] " + vmName);
vmFileInfo.setSuspendDirectory("[" + datastore + "] " + vmName);
vmFileInfo.setVmPathName("[" + datastore + "] " + vmName + "/" + vmName
+ ".vmx");
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
vmConfigSpec.setFiles(vmFileInfo);
ManagedObjectReference taskmor =
vimPort.reconfigVMTask(registeredVMRef, vmConfigSpec);
if (getTaskResultAfterDone(taskmor)) {
System.out.println("ReConfigure The Virtual Machine .......... Done");
} else {
System.out.println("Some Exception While Reconfiguring The VM ");
}
} catch (Exception e) {
throw new PutVMFilesException(e);
}
}
/**
* Put files onto remote datastore
*
* @throws DuplicateNameFaultMsg
* @throws RuntimeFaultFaultMsg
* @throws TaskInProgressFaultMsg
* @throws InsufficientResourcesFaultFaultMsg
*
* @throws VmConfigFaultFaultMsg
* @throws InvalidDatastoreFaultMsg
* @throws InvalidPropertyFaultMsg
* @throws FileFaultFaultMsg
* @throws ConcurrentAccessFaultMsg
* @throws InvalidStateFaultMsg
* @throws InvalidCollectorVersionFaultMsg
*
* @throws InvalidNameFaultMsg
* @throws OutOfBoundsFaultMsg
* @throws NotFoundFaultMsg
* @throws AlreadyExistsFaultMsg
* @throws IOException
*/
void putVMFiles() throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg {
boolean validated = customValidation();
if (getMOREFs.inContainerByType(serviceContent.getRootFolder(), "VirtualMachine").containsKey(vmName)) {
throw new PutVMFilesException(String.format("A VM with the name %s already exists!", vmName));
}
if (validated) {
int found = 0;
String[] listOfDir = getDirFiles(new File(localPath));
if (listOfDir != null && listOfDir.length != 0) {
// Dumping All The Data
for (int i = 0; i < listOfDir.length; i++) {
if (!validateDir(listOfDir[i], localPath)) {
continue;
}
// made it here, we found something to upload
found++;
// go ahead and copy this up to the server
copyDir(listOfDir[i]);
// Register The Virtual Machine
boolean reconFlag = false;
try {
reconFlag = registerVirtualMachine();
//Reconfigure the disks
if (reconFlag) {
reconfigVirtualMachine();
}
} catch (Exception e) {
throw new PutVMFilesException(e);
}
}
}
if (found == 0) {
System.out.printf(
"There are no suitable VM Directories available at location %s " +
"did you use GetVMFiles first?",
this.localPath
);
System.out.println();
}
}
}
/**
* Checks a directory name against rules.
*
*
* @param directoryName - directory to examine
* @param localPath
* @return true if usable, false if not
*/
boolean validateDir(final String directoryName, final String localPath) {
// short-circut this method if no name set
if (directoryName == null) {
return false;
}
// using data-structure to avoid repeated calls
int message = 0;
final String[] messages = {
"",
String.format("The directory %s does not contain a matching %s.vmx file to register.%n",
directoryName, vmName),
String.format("Skipping: %s is a hidden name", directoryName),
String.format("Skipping: %s is not a directory.", directoryName),
String.format("Skipping: Name %s does not contain the --vmname %s", directoryName, vmName),
};
message = (!new File(new File(localPath,directoryName), String.format("%s.vmx", vmName)).exists()) ? 1 : message;
message = (directoryName.startsWith(".")) ? 2 : message;
message = (!new File(localPath,directoryName).isDirectory()) ? 3 : message;
message = (!directoryName.contains(vmName)) ? 4 : message;
if (verbose) {
System.out.println(messages[message]);
}
return message == 0;
}
/**
* main method
*
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
*/
@Action
public void run() throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg {
try {
putVMFiles();
} catch (PutVMFilesException cme) {
System.out.println(cme.getMessage());
}
}
/**
* For exceptions thrown internal to this sample only. Specifically for internal error handling.
*/
class PutVMFilesException extends RuntimeException {
private static final long serialVersionUID = 1L;
public PutVMFilesException(final String message) {
super(message);
}
public PutVMFilesException(final Throwable throwable) {
super(throwable);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment