/** * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * The Original Code is available at https://abicloud.svn.sourceforge.net/svnroot/abicloud * * The Initial Developer of the Original Code is Soluciones Grid, S.L. (www.abiquo.com), * Consell de Cent 296 principal 2ยบ, 08007 Barcelona, Spain. * No portions of the Code have been created by third parties. * All Rights Reserved. * * Contributor(s): * Telefonica Investigacion y Desarrollo S.A.U. (http://www.tid.es) * Emilio Vargas 6, 28043 Madrid, Spain. * */ package com.abiquo.ovf.section; import java.math.BigInteger; import javax.xml.bind.JAXBElement; import javax.xml.namespace.QName; import org.dmtf.schemas.ovf.envelope._1.DiskSectionType; import org.dmtf.schemas.ovf.envelope._1.EnvelopeType; import org.dmtf.schemas.ovf.envelope._1.FileType; import org.dmtf.schemas.ovf.envelope._1.SectionType; import org.dmtf.schemas.ovf.envelope._1.VirtualDiskDescType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.abiquo.ovf.OVFEnvelopeUtils; import com.abiquo.ovf.OVFReferenceUtils; import com.abiquo.ovf.exceptions.IdAlreadyExistsException; import com.abiquo.ovf.exceptions.IdNotFoundException; import com.abiquo.ovf.exceptions.InvalidSectionException; import com.abiquo.ovf.exceptions.RequiredAttributeException; import com.abiquo.ovf.exceptions.SectionException; import com.abiquo.ovf.exceptions.SectionNotPresentException; public class OVFDiskUtils { private final static Logger log = LoggerFactory.getLogger(OVFDiskUtils.class); /******************************************************************************* * DISK SECTION *******************************************************************************/ private final static String KVM_URI = "http://kvm"; private final static String VBOX_URI = "http://vbox"; private final static String XEN_URI = "http://xen"; private final static String VMWARE_URI = "http://www.vmware.com/specifications/vmdk.html#sparse"; private final static String VMWARE_COMPRESSED_URI = "http://www.vmware.com/specifications/vmdk.html#compressed"; private final static String VMWARE_STREAM_OPTIMIZED_URI = "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"; /** * Supported virtual disk formats. */ public enum DiskFormat { VMWARE, VMWARE_COMPRESSED, VBOX, XEN, KVM, HYPERV, QEMU, LXC, OPENVZ } // TODO vmware_compressed =?= streamOptimized // VMDX, OVF, RAW, BIN, VDI, QEMM??? public enum DiskSizeUnit { Bytes, KiloBytes, MegaBytes, GigaBytes } // TODO Bits, KiloBits, MegaBits, GigaBits, Words, DoubleWords, QuadWords /** * In order to meet the 5.2 clause an URI is required as disk format. ''the disk format shall be * given by a URI which identifies an unencumbered specification on how to interpret the disk * format'' TODO add for all the supported disk formats. */ public static String formatUri(DiskFormat format) { String formatUri; switch (format) { case VMWARE: formatUri = VMWARE_URI; break; case VMWARE_COMPRESSED: formatUri = VMWARE_COMPRESSED_URI; // streamOptimized break; /** * case VBOX: break; case XEN: break; case KVM: break; case HYPERV: break; case QEMU: * break; case LXC: break; case OPENVZ: break; * TODO for KVM as well **/ default: formatUri = "http://" + format.toString().toLowerCase(); // TODO as any vendor but vmware have URI // .... break; } return formatUri; } /** * Returns a DiskFormat object according to the format URI supplied. * * @param formatURI * @return * @throws RequiredAttributeException */ public static DiskFormat getDiskFormat(String formatURI) throws RequiredAttributeException { DiskFormat diskFormat = null; if(formatURI == null) { throw new RequiredAttributeException("No disk format URI supplied"); } if(formatURI.equals(VMWARE_URI) || formatURI.equals(VMWARE_STREAM_OPTIMIZED_URI)) { diskFormat = DiskFormat.VMWARE; }else if(formatURI.equals(VMWARE_COMPRESSED_URI)) { diskFormat = DiskFormat.VMWARE_COMPRESSED; }else if(formatURI.equals(KVM_URI)) { diskFormat = DiskFormat.KVM; }else if(formatURI.equals(VBOX_URI)) { diskFormat = DiskFormat.VBOX; }else if(formatURI.equals(XEN_URI)) { diskFormat = DiskFormat.XEN; } else { throw new RequiredAttributeException("No matching disk format found for the URI: " + formatURI); } return diskFormat; } private static BigInteger toBytes(long size, DiskSizeUnit unit) { BigInteger b1024 = BigInteger.valueOf(1024); BigInteger bytes = BigInteger.valueOf(size); switch (unit) { case GigaBytes: bytes = bytes.multiply(b1024); case MegaBytes: bytes = bytes.multiply(b1024); case KiloBytes: bytes = bytes.multiply(b1024); case Bytes: default: break; } return bytes; } /** * Adds a virtual disk description to mount an existing virtual image file on the provided OVF * envelope. If the fileId is not provided an empty disk is added. Create the DiskSection if * there is any DiskDesc on the OVF envelope. * * @param envelope, the envelope containing the fileId on its references section and where the * virtual disk description will be added. * @param diskId, the required desired disk identifier for the new virtual disk description. * @param fileID, the optional file identifier on the references section to be mounted on the * virtual disk. If any provided its an empty disk. * @param format, the required file image format (hypervisor type) * @param capacity, the required disk capacity. * @param capacityUnit, the optional capacity units. If none specified assumed bytes. * @param populatedSize, the optional expected used size of the disk. * @param parentRefDiskId, the optional parent disk, modified blocks in comparison to a parent * image. * @throw IdNotFound, it the provided fileId is not on the References section or the * parentRefDiskId is not on the Disk section. * @throw IdAlreadyExists, it the provided diskId is already on the Disk section. TODO capacity * can also be property on the envelope "${some.property}" from the ProductSection */ public static void addDiskDescription(EnvelopeType envelope, String diskId, String fileId, DiskFormat format, Long capacity, DiskSizeUnit capacityUnit, Long populatedSize, String parentRefDiskId) throws IdNotFoundException, IdAlreadyExistsException { VirtualDiskDescType disk; FileType file = null; BigInteger byteCapacity; // TODO assert diskId/capacity not null // TODO log info disk = new VirtualDiskDescType(); disk.setDiskId(diskId); disk.setFormat(formatUri(format)); // TODO use referenced file extension to infer if (fileId != null) { file = OVFReferenceUtils.getReferencedFile(envelope, fileId); disk.setFileRef(fileId); } if (capacityUnit != null & capacityUnit != DiskSizeUnit.Bytes) { disk.setCapacityAllocationUnits(capacityUnit.name()); byteCapacity = toBytes(capacity, capacityUnit); } else { byteCapacity = BigInteger.valueOf(capacity); } // TODO if(capacity.startsWith("$")) disk.setCapacity(String.valueOf(capacity)); // TODO check there aren't any disk with the same fileId. Also assert the same order with // fileId (references and Disk sections must match) if (populatedSize != null) { if (file == null) { log.error("An empty disk can not have expected used capacity"); } if (populatedSize > byteCapacity.longValue()) { // TODO fail log.error("The populate size ({}b) is higher than the capacity ({}b)", populatedSize, byteCapacity.longValue()); } disk.setPopulatedSize(populatedSize); } if (parentRefDiskId != null) { getDiskDescription(envelope, parentRefDiskId); disk.setParentRef(parentRefDiskId); } else if (file != null) { if (byteCapacity.longValue() < file.getSize().longValue()) { log.error("File size ({}) higher than the specified capacity ({})", file.getSize() .longValue(), byteCapacity.longValue()); } } addDisk(envelope, disk); } /** * Get a specific virtual disk description. * * @param envelope, the OVF envelope to be checked. * @param diskId, the desired disk identifier. * @throws IdNotFoundException if there is any disk with the required identifier on the envelope * disk section. */ public static VirtualDiskDescType getDiskDescription(EnvelopeType envelope, String diskId) throws IdNotFoundException { DiskSectionType sectionDisk; SectionType section; for (JAXBElement< ? extends SectionType> jxbSection : envelope.getSection()) { section = jxbSection.getValue(); if (section instanceof DiskSectionType) { sectionDisk = (DiskSectionType) section; for (VirtualDiskDescType vdisk : sectionDisk.getDisk()) { if (diskId.equals(vdisk.getDiskId())) { return vdisk; } }// disks }// disk section }// sections throw new IdNotFoundException("Virtual disk description id :" + diskId); } /** * TODO use envelope Adds a VirtualDiskDescription to an existing VirtualSystem. * * @throws IdAlreadyExists if the provided VirtualDiscDescription id is already on the * VirutalSystem's DiskSection. */ public static void addDisk(EnvelopeType envelope, VirtualDiskDescType vDisk) throws IdAlreadyExistsException { SectionType section; DiskSectionType sectionDisk = null; try { sectionDisk = OVFEnvelopeUtils.getSection(envelope, DiskSectionType.class); } catch (SectionNotPresentException e) { try { sectionDisk = OVFEnvelopeUtils.createSection(DiskSectionType.class, null); OVFEnvelopeUtils.addSection(envelope, sectionDisk); } catch (SectionException e1) { // from a SectionNotPresentException } } catch (InvalidSectionException invalid) { // Envelope can have disk section } for (VirtualDiskDescType vdd : sectionDisk.getDisk()) { if (vDisk.getDiskId().equalsIgnoreCase(vdd.getDiskId())) { final String msg = "The VirtualDiskDescription diskId" + vDisk.getDiskId(); throw new IdAlreadyExistsException(msg); } } sectionDisk.getDisk().add(vDisk); } /** * TODO use envelope Adds a VirtualDiskDescription to an existing VirtualSystem. * * @throws IdAlreadyExists if the provided VirtualDiscDescription id is already on the * VirutalSystem's DiskSection. */ public static void addDisk(DiskSectionType diskSection, VirtualDiskDescType vDisk) throws IdAlreadyExistsException { for (VirtualDiskDescType vdd : diskSection.getDisk()) { if (vDisk.getDiskId().equalsIgnoreCase(vdd.getDiskId())) { final String msg = "The VirtualDiskDescription diskId" + vDisk.getDiskId(); throw new IdAlreadyExistsException(msg); } } diskSection.getDisk().add(vDisk); } /** * Adds a virtual disk description to mount an existing virtual image file. If the fileId is not * provided an empty disk is added. Create the DiskSection if there is any DiskDesc on the OVF * envelope. This method do not assure fileId is on the EnvelopeReferenceSection (use * addDiskDescription instead) * * @param diskId, the required desired disk identifier for the new virtual disk description. * @param fileID, the optional file identifier on the references section to be mounted on the * virtual disk. If any provided its an empty disk. * @param format, the required file image format (hypervisor type) * @param capacity, the required disk capacity. * @param capacityUnit, the optional capacity units. If none specified assumed bytes. * @param populatedSize, the optional expected used size of the disk. * @param parentRefDiskId, the optional parent disk, modified blocks in comparison to a parent * image. * @throw IdNotFound, it the provided fileId is not on the References section or the * parentRefDiskId is not on the Disk section. * @throw IdAlreadyExists, it the provided diskId is already on the Disk section. TODO capacity * can also be property on the envelope "${some.property}" from the ProductSection */ public static VirtualDiskDescType createDiskDescription(String diskId, String fileId, DiskFormat format, Long capacity, DiskSizeUnit capacityUnit, Long populatedSize, String parentRefDiskId) { VirtualDiskDescType disk; BigInteger byteCapacity; // TODO assert diskId/capacity not null // TODO log info disk = new VirtualDiskDescType(); disk.setDiskId(diskId); disk.setFormat(formatUri(format)); // TODO use referenced file extension to infer if (fileId != null) { // file = OVFReferenceUtils.getReferencedFile(envelope, fileId); disk.setFileRef(fileId); } if (capacityUnit != null & capacityUnit != DiskSizeUnit.Bytes) { disk.setCapacityAllocationUnits(capacityUnit.name()); byteCapacity = toBytes(capacity, capacityUnit); } else { byteCapacity = BigInteger.valueOf(capacity); } // TODO if(capacity.startsWith("$")) disk.setCapacity(String.valueOf(capacity)); // TODO check there aren't any disk with the same fileId. Also assert the same order with // fileId (references and Disk sections must match) if (populatedSize != null) { if (fileId == null) { log.error("An empty disk can not have expected used capacity"); } if (populatedSize > byteCapacity.longValue()) { // TODO fail log.error("The populate size ({}b) is higher than the capacity ({}b)", populatedSize, byteCapacity.longValue()); } disk.setPopulatedSize(populatedSize); } if (parentRefDiskId != null) { // getDiskDescription(envelope, parentRefDiskId); disk.setParentRef(parentRefDiskId); }/* * else if (file != null) { if (byteCapacity.longValue() < file.getSize().longValue()) { * log.error("File size ({}) higher than the specified capacity ({})", file.getSize() * .longValue(), byteCapacity.longValue()); } } */ return disk; } /** * Set other attributes to VirtualDiskDescType * * @param disk the VirtualDiskDescType we work with * @param key Key of the Map * @param value value of the key * @throws RequiredAttributeException if key or diskSection are null throws this method * @throws IdAlreadyExistsException if key already inserted */ public static void addOtherAttributes(VirtualDiskDescType disk, QName key, String value) throws RequiredAttributeException, IdAlreadyExistsException { if (disk == null || key == null) { throw new RequiredAttributeException("Some values are null!"); } if (disk.getOtherAttributes().get(key) != null) { throw new IdAlreadyExistsException("Key already exists"); } disk.getOtherAttributes().put(key, value); } }