/**
* This file is part of muCommander, http://www.mucommander.com
* Copyright (C) 2002-2016 Maxence Bernard
*
* muCommander is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* muCommander is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.mucommander.commons.file.protocol.vsphere;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import javax.net.ssl.HttpsURLConnection;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.soap.Detail;
import javax.xml.ws.soap.SOAPFaultException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.NodeList;
import com.mucommander.commons.file.AbstractFile;
import com.mucommander.commons.file.FileFactory;
import com.mucommander.commons.file.FileOperation;
import com.mucommander.commons.file.FilePermissions;
import com.mucommander.commons.file.FileURL;
import com.mucommander.commons.file.PermissionAccess;
import com.mucommander.commons.file.PermissionBits;
import com.mucommander.commons.file.PermissionType;
import com.mucommander.commons.file.UnsupportedFileOperation;
import com.mucommander.commons.file.UnsupportedFileOperationException;
import com.mucommander.commons.file.connection.ConnectionHandler;
import com.mucommander.commons.file.connection.ConnectionHandlerFactory;
import com.mucommander.commons.file.connection.ConnectionPool;
import com.mucommander.commons.file.protocol.FileProtocols;
import com.mucommander.commons.file.protocol.ProtocolFile;
import com.mucommander.commons.file.util.PathUtils;
import com.mucommander.commons.io.FileTransferError;
import com.mucommander.commons.io.FileTransferException;
import com.mucommander.commons.io.RandomAccessInputStream;
import com.mucommander.commons.io.RandomAccessOutputStream;
import com.mucommander.commons.io.StreamUtils;
import com.vmware.vim25.FileFaultFaultMsg;
import com.vmware.vim25.FileTransferInformation;
import com.vmware.vim25.GuestFileAttributes;
import com.vmware.vim25.GuestFileInfo;
import com.vmware.vim25.GuestFileType;
import com.vmware.vim25.GuestListFileInfo;
import com.vmware.vim25.GuestOperationsFaultFaultMsg;
import com.vmware.vim25.InvalidPropertyFaultMsg;
import com.vmware.vim25.InvalidStateFaultMsg;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.NamePasswordAuthentication;
import com.vmware.vim25.RuntimeFaultFaultMsg;
import com.vmware.vim25.TaskInProgressFaultMsg;
/**
* VSphereFile provides access to files on virtual machines on vSphere 5+
* servers.
*
* <p>
* The associated {@link FileURL} scheme is {@link FileProtocols#VSPHERE}. The
* host part of the URL designates the vSphere server. Credentials must be
* specified in the login and password parts as vSphere servers require a login
* and password. The path separator is '/', even on windows virtual machines.
*
* The first path of the path is the identifier of the guest VM (see below) we
* want to access. That part may also contain username and password for the
* guest VMs - though it is less secure, so it is better to use the GUI dialog.
*
* Note that the machine credentials cab also be set as a property named
* "guestCredentials" of this object.
*
* The identifier of a VM can be one of the following: - Instance UUID - BIOS
* UUID - IP address
*
* Note that even if you use IP to identify the machine, you don't need network
* connectivity for accessing the machine. all access is done via vSphere APIs.
*
* <p>
* Here are a few examples of valid vSphere URLs: <code>
* vsphere://vsphere5-server/501fc8db-f9dc-562b-6310-3c6b7ace2377/C:<br>
* vsphere://admin:pass@vsphere5-server/501fc8db-f9dc-562b-6310-3c6b7ace2377/C:<br>
* vsphere://admin:pass@vsphere5-server/guestadmin:guestpass@501fc8db-f9dc-562b-6310-3c6b7ace2377/C:<br>
* </code>
*
* <p>
* Access to vSphere files is provided by the <code>vim25</code> library
* distributed under the VMware Software Development Kit (SDK) License
* Agreement. See: http://www.vmware.com/go/vwssdk-redistribution-info
*
* @see ConnectionPool
* @author Yuval Kohavi <yuval.kohavi@intigua.com>
*/
public class VSphereFile extends ProtocolFile implements
ConnectionHandlerFactory {
private static final Logger LOGGER = LoggerFactory
.getLogger(VSphereFile.class);
public static final String GUEST_CREDENTIALS = "guestCredentials";
private static final String SEPARATOR = "/";
private String guestPass;
private String guestUser;
private String vmIdentifier;
private String pathInsideVm;
private ManagedObjectReference vm;
private NamePasswordAuthentication credentials;
private ManagedObjectReference guestOperationsManager;
private long size = -1;
private boolean isFile;
private boolean isSymLink;
private boolean isDir;
private long date;
private VSphereFile parent;
private String guestOsId;
public VSphereFile(FileURL url) throws IOException {
super(url);
VsphereConnHandler connHandler = null;
try {
setPath(url.getPath());
connHandler = getConnHandler();
guestOperationsManager = connHandler.getClient()
.getServiceContent().getGuestOperationsManager();
getMor(connHandler);
fixPathInVmIfNeeded(connHandler);
checkAttributues(connHandler);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (FileFaultFaultMsg e) {
translateandLogException(e);
} catch (GuestOperationsFaultFaultMsg e) {
translateandLogException(e);
} catch (InvalidStateFaultMsg e) {
translateandLogException(e);
} catch (TaskInProgressFaultMsg e) {
translateandLogException(e);
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} catch (URISyntaxException e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
}
private void releaseConnHandler(VsphereConnHandler connHandler) {
if (connHandler != null) {
connHandler.releaseLock();
}
}
public VSphereFile(FileURL url, VSphereFile related)
throws URISyntaxException, IOException, RuntimeFaultFaultMsg,
InvalidPropertyFaultMsg, FileFaultFaultMsg,
GuestOperationsFaultFaultMsg, InvalidStateFaultMsg,
TaskInProgressFaultMsg {
super(url);
setPath(url.getPath());
this.vm = related.vm;
this.guestOsId = related.guestOsId;
this.guestOperationsManager = related.guestOperationsManager;
fixPathInVmIfNeeded(null);
VsphereConnHandler connHandler = null;
try {
connHandler = getConnHandler();
checkAttributues(connHandler);
} finally {
releaseConnHandler(connHandler);
}
}
public VSphereFile(FileURL url, VSphereFile parent,
GuestFileInfo guestFileInfo) throws RuntimeFaultFaultMsg,
IOException, FileFaultFaultMsg, GuestOperationsFaultFaultMsg,
InvalidStateFaultMsg, TaskInProgressFaultMsg,
InvalidPropertyFaultMsg, URISyntaxException {
super(url);
setPath(url.getPath());
this.parent = parent;
this.vm = parent.vm;
this.guestOsId = parent.guestOsId;
this.guestOperationsManager = parent.guestOperationsManager;
fixPathInVmIfNeeded(null);
updateAttributes(guestFileInfo);
}
private void getMor(VsphereConnHandler connHandler)
throws RuntimeFaultFaultMsg {
vm = connHandler.getClient().findVmByIp(vmIdentifier);
if (vm == null) {
vm = connHandler.getClient().findVmByUuid(vmIdentifier, true);
}
if (vm == null) {
vm = connHandler.getClient().findVmByUuid(vmIdentifier, false);
}
if (vm == null) {
throw new IllegalArgumentException("Machine identifier "
+ vmIdentifier + " not found.");
}
}
private void fixPathInVmIfNeeded(VsphereConnHandler connHandler)
throws RuntimeFaultFaultMsg, RemoteException,
InvalidPropertyFaultMsg {
if (this.guestOsId == null) {
if (connHandler != null) {
this.guestOsId = (String) connHandler.getClient()
.getProperties(vm, "config.guestId")[0];
}
}
boolean isWin = false;
if (this.guestOsId != null) {
// is it a windows machine ?
// see possible values for guest id here:
// http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.GuestOsDescriptor.GuestOsIdentifier.html
isWin = guestOsId.startsWith("win");
}
if (pathInsideVm.isEmpty()) {
if (isWin) {
// we assume that "C:" is a good default for windows
pathInsideVm = "C:";
} else {
pathInsideVm = "/";
}
// set the url to reflect the path inside the vm
fileURL.setPath(fileURL.getPath() + pathInsideVm);
}
}
private void setPath(String path) throws URISyntaxException, IOException {
// path starts with a /
// get first component:
int index = path.indexOf('/', 1);
String first = path.substring(1, index);
String rest = path.substring(index + 1);
// the first part of the path is very similar to a url, due to the fact
// it
// may contain guest credentials. So I use the URI class to help me
// parse it.
// first as a url of its own. http = dymm
URI url2 = new URI("dummy://" + first);
String uinfo;
credentials = new NamePasswordAuthentication();
vmIdentifier = url2.getHost();
String guestCred = fileURL.getProperty(GUEST_CREDENTIALS);
if ((guestCred != null) && (!guestCred.isEmpty())) {
uinfo = guestCred;
} else {
uinfo = url2.getUserInfo();
}
if (uinfo == null) {
throw new IOException(
"No guest credentials provided. please start the connection from the UI");
}
int indexOf = uinfo.indexOf(":");
if (indexOf == -1) {
throw new IOException(
"No guest credentials provided. please start the connection from the UI");
}
guestUser = uinfo.substring(0, indexOf);
guestPass = uinfo.substring(indexOf + 1);
credentials.setInteractiveSession(false);
credentials.setUsername(guestUser);
credentials.setPassword(guestPass);
pathInsideVm = rest;
}
private ManagedObjectReference getFileManager(VsphereConnHandler connHandler)
throws RemoteException, InvalidPropertyFaultMsg,
RuntimeFaultFaultMsg {
ManagedObjectReference fileManager = (ManagedObjectReference) connHandler
.getClient().getProperties(guestOperationsManager,
"fileManager")[0];
return fileManager;
}
private void checkAttributues(VsphereConnHandler connHandler)
throws IOException, FileFaultFaultMsg,
GuestOperationsFaultFaultMsg, InvalidStateFaultMsg,
RuntimeFaultFaultMsg, TaskInProgressFaultMsg,
InvalidPropertyFaultMsg {
ManagedObjectReference fileManager = getFileManager(connHandler);
GuestListFileInfo res = null;
try {
res = connHandler
.getClient()
.getVimPort()
.listFilesInGuest(fileManager, vm, credentials,
getPathInVm(), null, null, null);
} catch (SOAPFaultException e) {
if (isFileNotFound(e)) {
return;
}
throw e;
}
if (res.getFiles().size() == 1) {
// only one result - it's a file
GuestFileInfo guestFileInfo = res.getFiles().get(0);
updateAttributes(guestFileInfo);
} else {
// more than one result - it's a directory.
// find the entry for "."
for (GuestFileInfo f : res.getFiles()) {
if (f.getPath().equals(".")) {
updateAttributes(f);
break;
}
}
}
}
private boolean isFileNotFound(SOAPFaultException e) {
Detail detail = e.getFault().getDetail();
NodeList childNodes = detail.getChildNodes();
for (int i = 0; i < childNodes.getLength(); ++i) {
// I hate soap...
if (childNodes.item(i).getNodeName().equals("FileNotFoundFault")) {
return true;
}
}
return false;
}
void updateAttributes(GuestFileInfo guestFileInfo) {
if (guestFileInfo.getType().equals(GuestFileType.FILE.value())) {
isFile = true;
size = guestFileInfo.getSize();
} else if (guestFileInfo.getType()
.equals(GuestFileType.SYMLINK.value())) {
isSymLink = true;
} else if (guestFileInfo.getType().equals(
GuestFileType.DIRECTORY.value())) {
isDir = true;
}
date = guestFileInfo.getAttributes().getModificationTime()
.toGregorianCalendar().getTimeInMillis();
}
private VsphereConnHandler getConnHandler() throws IOException {
VsphereConnHandler connHandler = (VsphereConnHandler) ConnectionPool
.getConnectionHandler(this, fileURL, true);
try {
connHandler.checkConnection();
} catch (RuntimeException e) {
releaseConnHandler(connHandler);
throw e;
} catch (IOException e) {
releaseConnHandler(connHandler);
throw e;
}
return connHandler;
}
@Override
public ConnectionHandler createConnectionHandler(FileURL location) {
return new VsphereConnHandler(location);
}
@Override
public long getDate() {
return date;
}
@Override
public void changeDate(long lastModified) throws IOException,
UnsupportedFileOperationException {
VsphereConnHandler connHandler = null;
try {
GuestFileAttributes gfa = new GuestFileAttributes();
gfa.setModificationTime(getTimeToXmlTime(lastModified));
connHandler = getConnHandler();
connHandler
.getClient()
.getVimPort()
.changeFileAttributesInGuest(getFileManager(connHandler),
vm, credentials, getPathInVm(), gfa);
} catch (FileFaultFaultMsg e) {
translateandLogException(e);
} catch (GuestOperationsFaultFaultMsg e) {
translateandLogException(e);
} catch (InvalidStateFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (TaskInProgressFaultMsg e) {
translateandLogException(e);
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} catch (DatatypeConfigurationException e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
}
private XMLGregorianCalendar getTimeToXmlTime(long lastModified)
throws DatatypeConfigurationException {
GregorianCalendar gc = new GregorianCalendar(
TimeZone.getTimeZone("UTC"));
gc.setTime(new Date(lastModified));
XMLGregorianCalendar xmlTime = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(gc);
return xmlTime;
}
@Override
public long getSize() {
return size;
}
@Override
public AbstractFile getParent() {
if (parent != null) {
return parent;
}
String rootPath = getRootPath();
if (rootPath.equals(pathInsideVm) || pathInsideVm.equals("/")) {
return null;
}
try {
parent = new VSphereFile(fileURL.getParent(), this);
} catch (URISyntaxException e) {
return null;
} catch (IOException e) {
return null;
} catch (RuntimeFaultFaultMsg e) {
return null;
} catch (InvalidPropertyFaultMsg e) {
return null;
} catch (FileFaultFaultMsg e) {
return null;
} catch (GuestOperationsFaultFaultMsg e) {
return null;
} catch (InvalidStateFaultMsg e) {
return null;
} catch (TaskInProgressFaultMsg e) {
return null;
}
return parent;
}
@Override
public void setParent(AbstractFile parent) {
this.parent = (VSphereFile) parent;
}
@Override
public boolean exists() {
return isDir || isFile || isSymLink;
}
@Override
public FilePermissions getPermissions() {
return isDir ? FilePermissions.DEFAULT_DIRECTORY_PERMISSIONS
: FilePermissions.DEFAULT_FILE_PERMISSIONS;
}
@Override
public PermissionBits getChangeablePermissions() {
return PermissionBits.EMPTY_PERMISSION_BITS;
}
@Override
@UnsupportedFileOperation
public void changePermission(PermissionAccess access, PermissionType permission, boolean enabled)
throws IOException, UnsupportedFileOperationException {
throw new UnsupportedFileOperationException(
FileOperation.CHANGE_PERMISSION);
}
@Override
public String getOwner() {
return null;
}
@Override
public boolean canGetOwner() {
return false;
}
@Override
public String getGroup() {
return null;
}
@Override
public boolean canGetGroup() {
return false;
}
@Override
public boolean isDirectory() {
return isDir;
}
@Override
public boolean isSymlink() {
return isSymLink;
}
@Override
public boolean isSystem() {
return false;
}
@Override
public AbstractFile[] ls() throws IOException,
UnsupportedFileOperationException {
List<GuestFileInfo> fileInfos = new ArrayList<GuestFileInfo>();
int index = 0;
VsphereConnHandler connHandler = null;
try {
connHandler = getConnHandler();
ManagedObjectReference fileManager = getFileManager(connHandler);
boolean haveRemaining;
do {
GuestListFileInfo res = connHandler
.getClient()
.getVimPort()
.listFilesInGuest(fileManager, vm, credentials,
getPathInVm(), index, null, null);
haveRemaining = (res.getRemaining() != 0);
fileInfos.addAll(res.getFiles());
index = fileInfos.size();
} while (haveRemaining);
String parentPath = PathUtils.removeTrailingSeparator(fileURL
.getPath()) + SEPARATOR;
Collection<AbstractFile> res = new ArrayList<AbstractFile>();
for (GuestFileInfo f : fileInfos) {
final String name = getFileName(f.getPath());
if (name.equals(".") || name.equals("..")) {
continue;
}
FileURL childURL = (FileURL) fileURL.clone();
childURL.setPath(parentPath + name);
AbstractFile newFile = new VSphereFile(childURL, this, f);
res.add(newFile);
}
return res.toArray(new AbstractFile[0]);
} catch (FileFaultFaultMsg e) {
translateandLogException(e);
} catch (GuestOperationsFaultFaultMsg e) {
translateandLogException(e);
} catch (InvalidStateFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (TaskInProgressFaultMsg e) {
translateandLogException(e);
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} catch (URISyntaxException e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
// we never get here..
return null;
}
private String getFileName(String path) {
// don't search for the slash as the last character.
int lastIndex = path.length() - 2;
// find the last "\\" or "/"
int index = Math.max(path.lastIndexOf('\\', lastIndex),
path.lastIndexOf('/', lastIndex));
if (index == -1) {
return path;
}
return path.substring(index + 1);
}
@Override
public void mkdir() throws IOException, UnsupportedFileOperationException {
VsphereConnHandler connHandler = null;
try {
connHandler = getConnHandler();
connHandler
.getClient()
.getVimPort()
.makeDirectoryInGuest(getFileManager(connHandler), vm,
credentials, getPathInVm(), false);
} catch (FileFaultFaultMsg e) {
translateandLogException(e);
} catch (GuestOperationsFaultFaultMsg e) {
translateandLogException(e);
} catch (InvalidStateFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (TaskInProgressFaultMsg e) {
translateandLogException(e);
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
}
@Override
public InputStream getInputStream() throws IOException,
UnsupportedFileOperationException {
VsphereConnHandler connHandler = null;
try {
connHandler = getConnHandler();
ManagedObjectReference fileManager = getFileManager(connHandler);
FileTransferInformation fileDlInfo = connHandler
.getClient()
.getVimPort()
.initiateFileTransferFromGuest(fileManager, vm,
credentials, getPathInVm());
String fileDlUrl = fileDlInfo.getUrl().replace("*",
connHandler.getClient().getServer());
// http://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java
URL website = new URL(fileDlUrl);
return website.openStream();
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (FileFaultFaultMsg e) {
translateandLogException(e);
} catch (GuestOperationsFaultFaultMsg e) {
translateandLogException(e);
} catch (InvalidStateFaultMsg e) {
translateandLogException(e);
} catch (TaskInProgressFaultMsg e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
return null;
}
private String getPathInVm() {
if (isWinPath()) {
return pathInsideVm.replace("/", "\\");
}
return pathInsideVm;
}
@Override
public void copyStream(InputStream in, boolean append, long length)
throws FileTransferException {
if ((append == true) || (length == -1)) {
super.copyStream(in, append, length);
} else {
try {
doCopyRemoteFileName(in, length);
} catch (IOException e) {
LOGGER.error("Failed copying stream", e);
throw new FileTransferException(FileTransferError.UNKNOWN);
}
}
}
private void doCopyRemoteFileName(InputStream in, long length)
throws IOException {
VsphereConnHandler connHandler = null;
try {
connHandler = getConnHandler();
ManagedObjectReference fileManager = getFileManager(connHandler);
copyFileToRemote(getPathInVm(), in, length, connHandler,
fileManager);
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (IOException e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
}
/**
* vSphere APIs require the file size when copying a file.
*
* mucommander enables file copy with known size, and also with unknown
* size. This class supports the unknown size use case. It saves all the
* data to a temp file and copies it on close, when its size is known.
*
*/
public class VSphereOutputStream extends FileOutputStream {
private ManagedObjectReference fileManager;
private VsphereConnHandler connHandler;
private String fileName;
private File tmpFile;
public VSphereOutputStream(VsphereConnHandler connHandler,
ManagedObjectReference fileManager, ManagedObjectReference vm,
NamePasswordAuthentication credentials, String fileName)
throws FileNotFoundException, IOException {
this(connHandler, fileManager, vm, credentials, File
.createTempFile("tmpVixCopy", ".tmp"), fileName);
}
private VSphereOutputStream(VsphereConnHandler connHandler,
ManagedObjectReference fileManager, ManagedObjectReference vm,
NamePasswordAuthentication credentials, File tmpFile,
String fileName) throws FileNotFoundException, IOException {
super(tmpFile);
this.tmpFile = tmpFile;
this.connHandler = connHandler;
this.fileManager = fileManager;
this.fileName = fileName;
}
@Override
public void close() throws IOException {
if (connHandler == null) {
return;
}
super.close();
InputStream in = new FileInputStream(tmpFile);
try {
copyFileToRemote(fileName, in, this.tmpFile.length(),
connHandler, fileManager);
} finally {
connHandler.releaseLock();
connHandler = null;
in.close();
tmpFile.delete();
}
}
}
private void copyFileToRemote(String fileName, InputStream in, long length,
VsphereConnHandler connHandler, ManagedObjectReference fileManager)
throws RemoteException, MalformedURLException, ProtocolException,
IOException {
try {
String fileUploadUrl = getFileUploadUrl(fileName, length,
connHandler, fileManager);
URLConnection conn = prepareConnection(fileUploadUrl, length);
sendFile(in, conn);
parseResponse(conn);
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (FileFaultFaultMsg e) {
translateandLogException(e);
} catch (GuestOperationsFaultFaultMsg e) {
translateandLogException(e);
} catch (InvalidStateFaultMsg e) {
translateandLogException(e);
} catch (TaskInProgressFaultMsg e) {
translateandLogException(e);
}
}
private void parseResponse(URLConnection conn) throws IOException {
int responseCode = 0;
String message = "";
if (conn instanceof HttpURLConnection) {
responseCode = ((HttpURLConnection) conn).getResponseCode();
message = ((HttpsURLConnection) conn).getResponseMessage();
}
if (responseCode != 200) {
throw new IOException("Failed to copy file using vsphere: "
+ message);
}
}
private void sendFile(InputStream in, URLConnection conn)
throws IOException {
conn.connect();
OutputStream out = conn.getOutputStream();
try {
StreamUtils.copyStream(in, out, IO_BUFFER_SIZE);
} finally {
out.close();
}
}
private URLConnection prepareConnection(String fileUploadUrl, long fileSize)
throws RemoteException, InvalidPropertyFaultMsg,
RuntimeFaultFaultMsg, FileFaultFaultMsg,
GuestOperationsFaultFaultMsg, InvalidStateFaultMsg,
TaskInProgressFaultMsg, MalformedURLException, IOException,
ProtocolException {
// http://stackoverflow.com/questions/3386832/upload-a-file-using-http-put-in-java
URL url = new URL(fileUploadUrl);
URLConnection conn = url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
if (conn instanceof HttpURLConnection) {
((HttpURLConnection) conn).setRequestMethod("PUT");
} else {
throw new IllegalStateException("Unknown connection type");
}
conn.setRequestProperty("Content-type", "application/octet-stream");
conn.setRequestProperty("Content-length", "" + fileSize);
return conn;
}
private String getFileUploadUrl(String remotePathName, long fileSize,
VsphereConnHandler connHandler, ManagedObjectReference fileManager)
throws RemoteException, InvalidPropertyFaultMsg,
RuntimeFaultFaultMsg, FileFaultFaultMsg,
GuestOperationsFaultFaultMsg, InvalidStateFaultMsg,
TaskInProgressFaultMsg {
GuestFileAttributes gfa = new GuestFileAttributes();
boolean override = true;
String fileUploadUrl = connHandler
.getClient()
.getVimPort()
.initiateFileTransferToGuest(fileManager, vm, credentials,
remotePathName, gfa, fileSize, override);
// replace * with the address of the server. see vsphere docs.
fileUploadUrl = fileUploadUrl.replace("*", connHandler.getClient()
.getServer());
return fileUploadUrl;
}
@Override
public OutputStream getOutputStream() throws IOException,
UnsupportedFileOperationException {
VsphereConnHandler connHandler = null;
try {
connHandler = getConnHandler();
ManagedObjectReference fileManager = getFileManager(connHandler);
VSphereOutputStream c = new VSphereOutputStream(connHandler,
fileManager, vm, credentials, getPathInVm());
// passed owner ship
connHandler = null;
return c;
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
throw new RuntimeException("Should never get here");
}
@Override
@UnsupportedFileOperation
public OutputStream getAppendOutputStream() throws IOException,
UnsupportedFileOperationException {
throw new UnsupportedFileOperationException(FileOperation.WRITE_FILE);
}
@Override
@UnsupportedFileOperation
public RandomAccessInputStream getRandomAccessInputStream()
throws IOException, UnsupportedFileOperationException {
throw new UnsupportedFileOperationException(
FileOperation.RANDOM_READ_FILE);
}
@Override
@UnsupportedFileOperation
public RandomAccessOutputStream getRandomAccessOutputStream()
throws IOException, UnsupportedFileOperationException {
throw new UnsupportedFileOperationException(
FileOperation.RANDOM_WRITE_FILE);
}
@Override
public void delete() throws IOException, UnsupportedFileOperationException {
VsphereConnHandler connHandler = null;
try {
connHandler = getConnHandler();
ManagedObjectReference fileManager = getFileManager(connHandler);
if (isDirectory()) {
connHandler
.getClient()
.getVimPort()
.deleteDirectoryInGuest(fileManager, vm, credentials,
getPathInVm(), false);
isDir = false;
} else {
connHandler
.getClient()
.getVimPort()
.deleteFileInGuest(fileManager, vm, credentials,
getPathInVm());
isFile = isSymLink = false;
}
} catch (FileFaultFaultMsg e) {
translateandLogException(e);
} catch (GuestOperationsFaultFaultMsg e) {
translateandLogException(e);
} catch (InvalidStateFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (TaskInProgressFaultMsg e) {
translateandLogException(e);
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
}
@Override
public void renameTo(AbstractFile destFile) throws IOException,
UnsupportedFileOperationException {
// can't copy\rename to a different host
// os might be windows, so can't rename with different case.
checkRenamePrerequisites(destFile, false, false);
VsphereConnHandler connHandler = null;
try {
connHandler = getConnHandler();
ManagedObjectReference fileManager = getFileManager(connHandler);
if (isDirectory()) {
connHandler
.getClient()
.getVimPort()
.moveDirectoryInGuest(fileManager, vm, credentials,
getPathInVm(),
((VSphereFile) destFile).getPathInVm());
} else {
connHandler
.getClient()
.getVimPort()
.moveFileInGuest(fileManager, vm, credentials,
getPathInVm(),
((VSphereFile) destFile).getPathInVm(), true);
}
} catch (FileFaultFaultMsg e) {
translateandLogException(e);
} catch (GuestOperationsFaultFaultMsg e) {
translateandLogException(e);
} catch (InvalidStateFaultMsg e) {
translateandLogException(e);
} catch (RuntimeFaultFaultMsg e) {
translateandLogException(e);
} catch (TaskInProgressFaultMsg e) {
translateandLogException(e);
} catch (InvalidPropertyFaultMsg e) {
translateandLogException(e);
} finally {
releaseConnHandler(connHandler);
}
}
private static void translateandLogException(Exception e)
throws IOException {
LOGGER.error("Error with vsphere remote ops", e);
throw new IOException(e);
}
@Override
@UnsupportedFileOperation
public void copyRemotelyTo(AbstractFile destFile) throws IOException,
UnsupportedFileOperationException {
throw new UnsupportedFileOperationException(FileOperation.COPY_REMOTELY);
}
@Override
@UnsupportedFileOperation
public long getFreeSpace() throws IOException,
UnsupportedFileOperationException {
throw new UnsupportedFileOperationException(
FileOperation.GET_FREE_SPACE);
}
@Override
@UnsupportedFileOperation
public long getTotalSpace() throws IOException,
UnsupportedFileOperationException {
throw new UnsupportedFileOperationException(
FileOperation.GET_TOTAL_SPACE);
}
@Override
public Object getUnderlyingFileObject() {
return null;
}
@Override
public AbstractFile getRoot() {
FileURL rootURL = (FileURL) getURL().clone();
String rootPath = getRootPath();
rootURL.setPath("/" + vmIdentifier + "/" + rootPath);
return FileFactory.getFile(rootURL);
}
private String getRootPath() {
if (isWinPath()) {
return getPathInVm().substring(0, 2);
}
return "";
}
private boolean isWinPath() {
return (pathInsideVm.length() >= 2) && (pathInsideVm.charAt(1) == ':');
}
}