blob: 85d4030b215b50a0964d181b27f2e776cd3eda47 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.agent.api.storage;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import com.cloud.configuration.Resource.ResourceType;
import com.cloud.exception.InternalErrorException;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import com.cloud.agent.api.to.DatadiskTO;
import com.cloud.utils.exception.CloudRuntimeException;
public class OVFHelper {
private static final Logger s_logger = Logger.getLogger(OVFHelper.class);
/**
* Get disk virtual size given its values on fields: 'ovf:capacity' and 'ovf:capacityAllocationUnits'
* @param capacity capacity
* @param allocationUnits capacity allocation units
* @return disk virtual size
*/
public static Long getDiskVirtualSize(Long capacity, String allocationUnits, String ovfFilePath) throws InternalErrorException {
if ((capacity != 0) && (allocationUnits != null)) {
long units = 1;
if (allocationUnits.equalsIgnoreCase("KB") || allocationUnits.equalsIgnoreCase("KiloBytes") || allocationUnits.equalsIgnoreCase("byte * 2^10")) {
units = ResourceType.bytesToKiB;
} else if (allocationUnits.equalsIgnoreCase("MB") || allocationUnits.equalsIgnoreCase("MegaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^20")) {
units = ResourceType.bytesToMiB;
} else if (allocationUnits.equalsIgnoreCase("GB") || allocationUnits.equalsIgnoreCase("GigaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^30")) {
units = ResourceType.bytesToGiB;
}
return capacity * units;
} else {
throw new InternalErrorException("Failed to read capacity and capacityAllocationUnits from the OVF file: " + ovfFilePath);
}
}
public List<DatadiskTO> getOVFVolumeInfo(final String ovfFilePath) {
if (StringUtils.isBlank(ovfFilePath)) {
return new ArrayList<DatadiskTO>();
}
ArrayList<OVFFile> vf = new ArrayList<OVFFile>();
ArrayList<OVFDisk> vd = new ArrayList<OVFDisk>();
File ovfFile = new File(ovfFilePath);
try {
final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(ovfFilePath));
NodeList disks = doc.getElementsByTagName("Disk");
NodeList files = doc.getElementsByTagName("File");
NodeList items = doc.getElementsByTagName("Item");
boolean toggle = true;
for (int j = 0; j < files.getLength(); j++) {
Element file = (Element)files.item(j);
OVFFile of = new OVFFile();
of._href = file.getAttribute("ovf:href");
if (of._href.endsWith("vmdk") || of._href.endsWith("iso")) {
of._id = file.getAttribute("ovf:id");
String size = file.getAttribute("ovf:size");
if (StringUtils.isNotBlank(size)) {
of._size = Long.parseLong(size);
} else {
String dataDiskPath = ovfFile.getParent() + File.separator + of._href;
File this_file = new File(dataDiskPath);
of._size = this_file.length();
}
of.isIso = of._href.endsWith("iso");
if (toggle && !of.isIso) {
of._bootable = true;
toggle = !toggle;
}
vf.add(of);
}
}
for (int i = 0; i < disks.getLength(); i++) {
Element disk = (Element)disks.item(i);
OVFDisk od = new OVFDisk();
String virtualSize = disk.getAttribute("ovf:capacity");
od._capacity = NumberUtils.toLong(virtualSize, 0L);
String allocationUnits = disk.getAttribute("ovf:capacityAllocationUnits");
od._diskId = disk.getAttribute("ovf:diskId");
od._fileRef = disk.getAttribute("ovf:fileRef");
od._populatedSize = NumberUtils.toLong(disk.getAttribute("ovf:populatedSize"));
if ((od._capacity != 0) && (allocationUnits != null)) {
long units = 1;
if (allocationUnits.equalsIgnoreCase("KB") || allocationUnits.equalsIgnoreCase("KiloBytes") || allocationUnits.equalsIgnoreCase("byte * 2^10")) {
units = ResourceType.bytesToKiB;
} else if (allocationUnits.equalsIgnoreCase("MB") || allocationUnits.equalsIgnoreCase("MegaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^20")) {
units = ResourceType.bytesToMiB;
} else if (allocationUnits.equalsIgnoreCase("GB") || allocationUnits.equalsIgnoreCase("GigaBytes") || allocationUnits.equalsIgnoreCase("byte * 2^30")) {
units = ResourceType.bytesToGiB;
}
od._capacity = od._capacity * units;
}
od._controller = getControllerType(items, od._diskId);
vd.add(od);
}
} catch (SAXException | IOException | ParserConfigurationException e) {
s_logger.error("Unexpected exception caught while parsing ovf file:" + ovfFilePath, e);
throw new CloudRuntimeException(e);
}
List<DatadiskTO> disksTO = new ArrayList<DatadiskTO>();
for (OVFFile of : vf) {
if (StringUtils.isBlank(of._id)){
s_logger.error("The ovf file info is incomplete file info");
throw new CloudRuntimeException("The ovf file info has incomplete file info");
}
OVFDisk cdisk = getDisk(of._id, vd);
if (cdisk == null && !of.isIso){
s_logger.error("The ovf file info has incomplete disk info");
throw new CloudRuntimeException("The ovf file info has incomplete disk info");
}
Long capacity = cdisk == null ? of._size : cdisk._capacity;
String controller = cdisk == null ? "" : cdisk._controller._name;
String controllerSubType = cdisk == null ? "" : cdisk._controller._subType;
String dataDiskPath = ovfFile.getParent() + File.separator + of._href;
File f = new File(dataDiskPath);
if (!f.exists() || f.isDirectory()) {
s_logger.error("One of the attached disk or iso does not exists " + dataDiskPath);
throw new CloudRuntimeException("One of the attached disk or iso as stated on OVF does not exists " + dataDiskPath);
}
disksTO.add(new DatadiskTO(dataDiskPath, capacity, of._size, of._id, of.isIso, of._bootable, controller, controllerSubType));
}
//check if first disk is an iso move it to the end
DatadiskTO fd = disksTO.get(0);
if (fd.isIso()) {
disksTO.remove(0);
disksTO.add(fd);
}
return disksTO;
}
private OVFDiskController getControllerType(final NodeList itemList, final String diskId) {
for (int k = 0; k < itemList.getLength(); k++) {
Element item = (Element)itemList.item(k);
NodeList cn = item.getChildNodes();
for (int l = 0; l < cn.getLength(); l++) {
if (cn.item(l) instanceof Element) {
Element el = (Element)cn.item(l);
if ("rasd:HostResource".equals(el.getNodeName())
&& (el.getTextContent().contains("ovf:/file/" + diskId) || el.getTextContent().contains("ovf:/disk/" + diskId))) {
Element oe = getParentNode(itemList, item);
Element voe = oe;
while (oe != null) {
voe = oe;
oe = getParentNode(itemList, voe);
}
return getController(voe);
}
}
}
}
return null;
}
private Element getParentNode(final NodeList itemList, final Element childItem) {
NodeList cn = childItem.getChildNodes();
String parent_id = null;
for (int l = 0; l < cn.getLength(); l++) {
if (cn.item(l) instanceof Element) {
Element el = (Element)cn.item(l);
if ("rasd:Parent".equals(el.getNodeName())) {
parent_id = el.getTextContent();
}
}
}
if (parent_id != null) {
for (int k = 0; k < itemList.getLength(); k++) {
Element item = (Element)itemList.item(k);
NodeList child = item.getChildNodes();
for (int l = 0; l < child.getLength(); l++) {
if (child.item(l) instanceof Element) {
Element el = (Element)child.item(l);
if ("rasd:InstanceID".equals(el.getNodeName()) && el.getTextContent().trim().equals(parent_id)) {
return item;
}
}
}
}
}
return null;
}
private OVFDiskController getController(Element controllerItem) {
OVFDiskController dc = new OVFDiskController();
NodeList child = controllerItem.getChildNodes();
for (int l = 0; l < child.getLength(); l++) {
if (child.item(l) instanceof Element) {
Element el = (Element)child.item(l);
if ("rasd:ElementName".equals(el.getNodeName())) {
dc._name = el.getTextContent();
}
if ("rasd:ResourceSubType".equals(el.getNodeName())) {
dc._subType = el.getTextContent();
}
}
}
return dc;
}
public void rewriteOVFFile(final String origOvfFilePath, final String newOvfFilePath, final String diskName) {
try {
final Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(origOvfFilePath));
NodeList disks = doc.getElementsByTagName("Disk");
NodeList files = doc.getElementsByTagName("File");
NodeList items = doc.getElementsByTagName("Item");
String keepfile = null;
List<Element> toremove = new ArrayList<Element>();
for (int j = 0; j < files.getLength(); j++) {
Element file = (Element)files.item(j);
String href = file.getAttribute("ovf:href");
if (diskName.equals(href)) {
keepfile = file.getAttribute("ovf:id");
} else {
toremove.add(file);
}
}
String keepdisk = null;
for (int i = 0; i < disks.getLength(); i++) {
Element disk = (Element)disks.item(i);
String fileRef = disk.getAttribute("ovf:fileRef");
if (keepfile == null) {
s_logger.info("FATAL: OVA format error");
} else if (keepfile.equals(fileRef)) {
keepdisk = disk.getAttribute("ovf:diskId");
} else {
toremove.add(disk);
}
}
for (int k = 0; k < items.getLength(); k++) {
Element item = (Element)items.item(k);
NodeList cn = item.getChildNodes();
for (int l = 0; l < cn.getLength(); l++) {
if (cn.item(l) instanceof Element) {
Element el = (Element)cn.item(l);
if ("rasd:HostResource".equals(el.getNodeName())
&& !(el.getTextContent().contains("ovf:/file/" + keepdisk) || el.getTextContent().contains("ovf:/disk/" + keepdisk))) {
toremove.add(item);
break;
}
}
}
}
for (Element rme : toremove) {
if (rme.getParentNode() != null) {
rme.getParentNode().removeChild(rme);
}
}
final StringWriter writer = new StringWriter();
final StreamResult result = new StreamResult(writer);
final TransformerFactory tf = TransformerFactory.newInstance();
final Transformer transformer = tf.newTransformer();
final DOMSource domSource = new DOMSource(doc);
transformer.transform(domSource, result);
PrintWriter outfile = new PrintWriter(newOvfFilePath);
outfile.write(writer.toString());
outfile.close();
} catch (SAXException | IOException | ParserConfigurationException | TransformerException e) {
s_logger.info("Unexpected exception caught while removing network elements from OVF:" + e.getMessage(), e);
throw new CloudRuntimeException(e);
}
}
OVFDisk getDisk(String fileRef, List<OVFDisk> disks) {
for (OVFDisk disk : disks) {
if (disk._fileRef.equals(fileRef)) {
return disk;
}
}
return null;
}
class OVFFile {
// <File ovf:href="i-2-8-VM-disk2.vmdk" ovf:id="file1" ovf:size="69120" />
public String _href;
public String _id;
public Long _size;
public boolean _bootable;
public boolean isIso;
}
class OVFDisk {
//<Disk ovf:capacity="50" ovf:capacityAllocationUnits="byte * 2^20" ovf:diskId="vmdisk2" ovf:fileRef="file2"
//ovf:format="http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" ovf:populatedSize="43319296" />
public Long _capacity;
public String _capacityUnit;
public String _diskId;
public String _fileRef;
public Long _populatedSize;
public OVFDiskController _controller;
}
class OVFDiskController {
public String _name;
public String _subType;
}
}