/**
* Abiquo community edition
* cloud management application for hybrid clouds
* Copyright (C) 2008-2010 - Abiquo Holdings S.L.
*
* This application 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 under
* version 3 of the License
*
* This software 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 v.3 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.abiquo.am.services.download;
import static com.abiquo.am.services.TemplateConventions.getFileUrl;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.dmtf.schemas.ovf.envelope._1.ContentType;
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.MsgType;
import org.dmtf.schemas.ovf.envelope._1.ProductSectionType;
import org.dmtf.schemas.ovf.envelope._1.RASDType;
import org.dmtf.schemas.ovf.envelope._1.VirtualDiskDescType;
import org.dmtf.schemas.ovf.envelope._1.VirtualHardwareSectionType;
import org.dmtf.schemas.ovf.envelope._1.VirtualSystemCollectionType;
import org.dmtf.schemas.ovf.envelope._1.VirtualSystemType;
import org.dmtf.schemas.wbem.wscim._1.cim_schema._2.cim_resourceallocationsettingdata.ResourceType;
import org.dmtf.schemas.wbem.wscim._1.common.CimString;
import org.springframework.stereotype.Component;
import com.abiquo.am.exceptions.AMError;
import com.abiquo.appliancemanager.client.ExternalHttpConnection;
import com.abiquo.appliancemanager.exceptions.AMException;
import com.abiquo.appliancemanager.exceptions.DownloadException;
import com.abiquo.appliancemanager.transport.MemorySizeUnit;
import com.abiquo.model.enumerator.DiskFormatType;
import com.abiquo.ovfmanager.cim.CIMTypesUtils.CIMResourceTypeEnum;
import com.abiquo.ovfmanager.ovf.OVFEnvelopeUtils;
import com.abiquo.ovfmanager.ovf.exceptions.EmptyEnvelopeException;
import com.abiquo.ovfmanager.ovf.exceptions.InvalidSectionException;
import com.abiquo.ovfmanager.ovf.exceptions.RequiredAttributeException;
import com.abiquo.ovfmanager.ovf.exceptions.SectionAlreadyPresentException;
import com.abiquo.ovfmanager.ovf.exceptions.SectionNotPresentException;
import com.abiquo.ovfmanager.ovf.xml.OVFSerializer;
@Component
public class OVFDocumentFetch
{
/**
* XXX
*
* @throws DownloadException, if can not connect to the OVF location of if the envelope document
* is invalid.
*/
public EnvelopeType obtainEnvelope(final String ovfId)
{
EnvelopeType envelope;
InputStream evelopeStream = null;
final ExternalHttpConnection connection = new ExternalHttpConnection();
try
{
evelopeStream = connection.openConnection(ovfId);
envelope = OVFSerializer.getInstance().readXMLEnvelope(evelopeStream);
envelope = fixOVfDocument(ovfId, envelope);
checkEnvelopeIsValid(envelope);
}
catch (AMException ame)
{
throw ame;
}
catch (Exception e)
{
throw new AMException(AMError.TEMPLATE_INVALID, e);
}
finally
{
connection.releaseConnection();
try
{
if (evelopeStream != null)
{
evelopeStream.close();
}
}
catch (IOException e)
{
throw new AMException(AMError.TEMPLATE_INSTALL, //
"Can't close the http connection to " + ovfId,
e);
}
}
return envelope;
}
public EnvelopeType fixOVfDocument(final String ovfId, EnvelopeType envelope)
{
try
{
envelope = fixDiskFormtatUriAndFileSizes(envelope, ovfId);
envelope = fixMissingProductSection(envelope);
}
catch (Exception e)
{
throw new AMException(AMError.TEMPLATE_INVALID, e);
}
return envelope;
}
public EnvelopeType checkEnvelopeIsValid(final EnvelopeType envelope)
{
try
{
Map<String, VirtualDiskDescType> diskIdToDiskFormat =
new HashMap<String, VirtualDiskDescType>();
Map<String, FileType> fileIdToFileType = new HashMap<String, FileType>();
DiskSectionType diskSectionType =
OVFEnvelopeUtils.getSection(envelope, DiskSectionType.class);
if (diskSectionType.getDisk().size() != 1)
{
// more than one disk in disk section
throw new AMException(AMError.TEMPLATE_INVALID_MULTIPLE_DISKS);
}
else
{
int references = 0;
for (FileType fileType : envelope.getReferences().getFile())
{
fileIdToFileType.put(fileType.getId(), fileType);
if (diskSectionType.getDisk().get(0).getFileRef().equals(fileType.getId()))
{
references++;
}
}
if (references != 1)
{
// file referenced in diskSection isn't found in file references or found more
// than one
throw new AMException(AMError.TEMPLATE_INVALID_MULTIPLE_FILES);
}
}
// Create a hash
for (VirtualDiskDescType virtualDiskDescType : diskSectionType.getDisk())
{
diskIdToDiskFormat.put(virtualDiskDescType.getDiskId(), virtualDiskDescType);
}
// /
ContentType content = OVFEnvelopeUtils.getTopLevelVirtualSystemContent(envelope);
if (content instanceof VirtualSystemCollectionType)
{
throw new EmptyEnvelopeException("Current OVF description document includes a VirtualSystemCollection, "
+ "abicloud only deal with single virtual system based OVFs");
}
VirtualSystemType vsystem = (VirtualSystemType) content;
VirtualHardwareSectionType hardwareSectionType;
Integer cpu = null;
Long hd = null;
Long ram = null;
try
{
hardwareSectionType =
OVFEnvelopeUtils.getSection(vsystem, VirtualHardwareSectionType.class);
}
catch (InvalidSectionException e)
{
throw new SectionNotPresentException("VirtualHardware on a virtualSystem", e);
}
for (RASDType rasdType : hardwareSectionType.getItem())
{
ResourceType resourceType = rasdType.getResourceType();
if (resourceType == null) // empty rasd element
{
continue;
}
int resTnumeric = Integer.parseInt(resourceType.getValue());
// Get the information on the ram
if (CIMResourceTypeEnum.Processor.getNumericResourceType() == resTnumeric)
{
try
{
String cpuVal = rasdType.getVirtualQuantity().getValue().toString();
cpu = Integer.parseInt(cpuVal);
}
catch (Exception e)
{
throw new AMException(AMError.TEMPLATE_INVALID,
"Invalid CPU virtualQuantity");
}
}
else if (CIMResourceTypeEnum.Memory.getNumericResourceType() == resTnumeric)
{
try
{
BigInteger ramVal = rasdType.getVirtualQuantity().getValue();
ram = ramVal.longValue();
}
catch (Exception e)
{
throw new AMException(AMError.TEMPLATE_INVALID,
"Invalid RAM virtualQuantity");
}
if (rasdType.getAllocationUnits() != null
& rasdType.getAllocationUnits().getValue() != null)
{
final String allocationUnits = rasdType.getAllocationUnits().getValue();
final MemorySizeUnit ramSizeUnit = getMemoryUnitsFromOVF(allocationUnits);
}
}
else if (CIMResourceTypeEnum.Disk_Drive.getNumericResourceType() == resTnumeric)
{
// HD requirements are extracted from the associated Disk on ''hostResource''
String diskId = getVirtualSystemDiskId(rasdType.getHostResource());
if (!diskIdToDiskFormat.containsKey(diskId))
{
throw new RequiredAttributeException("Virtual System makes reference to an undeclared disk "
+ diskId);
}
VirtualDiskDescType diskDescType = diskIdToDiskFormat.get(diskId);
String capacity = diskDescType.getCapacity();
hd = Long.parseLong(capacity);
final String allocationUnits = diskDescType.getCapacityAllocationUnits();
final MemorySizeUnit hdSizeUnit = getMemoryUnitsFromOVF(allocationUnits);
}
}// rasd
if (cpu == null)
{
throw new RequiredAttributeException("Not CPU RASD element found on the envelope");
}
if (ram == null)
{
throw new RequiredAttributeException("Not RAM RASD element found on the envelope");
}
if (hd == null)
{
throw new RequiredAttributeException("Not HD RASD element found on the envelope");
}
}
catch (AMException amException)
{
throw amException;
}
catch (Exception e)
{
throw new AMException(AMError.TEMPLATE_INVALID, e);
}
return envelope;
}
private EnvelopeType fixDiskFormtatUriAndFileSizes(final EnvelopeType envelope,
final String ovfId) throws SectionNotPresentException, InvalidSectionException
{
DiskSectionType diskSection = OVFEnvelopeUtils.getSection(envelope, DiskSectionType.class);
if (diskSection.getDisk().size() != 1)
{
final String message =
"abicloud only supports single disk definition on the OVF, the current envelope contains multiple disk";
throw new InvalidSectionException(message);
}
VirtualDiskDescType vdisk = diskSection.getDisk().get(0);
String formatUri = vdisk.getFormat();
if (StringUtils.isEmpty(formatUri))
{
final String message = "Missing ''format'' attribute for the Disk element";
throw new InvalidSectionException(message);
}
DiskFormatType format = DiskFormatType.fromURI(formatUri);
if (format == null) // the format URI isn't on the abicloud enumeration. FIX it
{
// vbox/vmware
// http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized
// abiquo
// http://www.vmware.com/technical-resources/interfaces/vmdk_access.html#streamOptimized
if (formatUri.contains("interfaces/specifications/vmdk.html"))
{
formatUri =
formatUri.replace("interfaces/specifications/vmdk.html",
"technical-resources/interfaces/vmdk_access.html");
format = DiskFormatType.fromURI(formatUri);
if (format == null)
{
throw new InvalidSectionException(String.format(
"Invalid disk format type [%s]", formatUri));
}
vdisk.setFormat(formatUri);
}
}
try
{
for (FileType ftype : envelope.getReferences().getFile())
{
if (ftype.getSize() == null)
{
String fileUrl = getFileUrl(ftype.getHref(), ovfId);
Long size = new ExternalHttpConnection().headFile(fileUrl);
ftype.setSize(BigInteger.valueOf(size));
}
}
}
catch (Exception e)
{
throw new InvalidSectionException(String.format("Invalid File References section "
+ "(check all the files on the OVF document contains the ''size'' attribute):\n",
e.toString()));
}
return envelope;
}
/**
* If product section is not present use the VirtualSystemType to set it.
*
* @throws EmptyEnvelopeException
*/
private EnvelopeType fixMissingProductSection(final EnvelopeType envelope)
throws InvalidSectionException, EmptyEnvelopeException
{
ContentType contentType = OVFEnvelopeUtils.getTopLevelVirtualSystemContent(envelope);
if (!(contentType instanceof VirtualSystemType))
{
throw new InvalidSectionException("abicloud only suport single virtual system definition,"
+ " current OVF envelope defines a VirtualSystemCollection");
}
VirtualSystemType vsystem = (VirtualSystemType) contentType;
try
{
OVFEnvelopeUtils.getSection(vsystem, ProductSectionType.class);
}
catch (SectionNotPresentException e)
{
String vsystemName =
vsystem.getName() != null && !StringUtils.isEmpty(vsystem.getName().getValue())
? vsystem.getName().getValue() : vsystem.getId();
MsgType prod = new MsgType();
prod.setValue(vsystemName);
ProductSectionType product = new ProductSectionType();
product.setInfo(vsystem.getInfo());
product.setProduct(prod);
try
{
OVFEnvelopeUtils.addSection(vsystem, product);
}
catch (SectionAlreadyPresentException e1)
{
}
}
return envelope;
}
/**
* default is byte
*
* @throws RequiredAttributeException
*/
private static MemorySizeUnit getMemoryUnitsFromOVF(final String allocationUnits)
throws RequiredAttributeException
{
if (allocationUnits == null || "byte".equalsIgnoreCase(allocationUnits)
|| "bytes".equalsIgnoreCase(allocationUnits))
{
return MemorySizeUnit.BYTE;
}
else if ("byte * 2^10".equals(allocationUnits) || "KB".equalsIgnoreCase(allocationUnits)
|| "KILOBYTE".equalsIgnoreCase(allocationUnits)
|| "KILOBYTES".equalsIgnoreCase(allocationUnits)) // kb
{
return MemorySizeUnit.KILOBYTE;
}
else if ("byte * 2^20".equals(allocationUnits) || "MB".equalsIgnoreCase(allocationUnits)
|| "MEGABYTE".equalsIgnoreCase(allocationUnits)
|| "MEGABYTES".equalsIgnoreCase(allocationUnits)) // mb
{
return MemorySizeUnit.MEGABYTE;
}
else if ("byte * 2^30".equals(allocationUnits) || "GB".equalsIgnoreCase(allocationUnits)
|| "GIGABYTE".equalsIgnoreCase(allocationUnits)
|| "GIGABYTES".equalsIgnoreCase(allocationUnits)) // gb
{
return MemorySizeUnit.GIGABYTE;
}
else if ("byte * 2^40".equals(allocationUnits) || "TB".equalsIgnoreCase(allocationUnits)
|| "TERABYTE".equalsIgnoreCase(allocationUnits)
|| "TERABYTES".equalsIgnoreCase(allocationUnits)) // tb
{
return MemorySizeUnit.TERABYTE;
}
else
{
final String msg =
"Unknow disk capacityAllocationUnits factor [" + allocationUnits + "]";
throw new RequiredAttributeException(msg);
}
}
/**
* Decode CimStrings (on the OVF namespce) on the Disk RASD's HostResource attribute to delete
* the ''ovf://disk/'' prefix
**/
private static String getVirtualSystemDiskId(final List<CimString> cimStrs)
{
String cimStringVal = "";
for (CimString cimString : cimStrs)
{
cimStringVal = cimString.getValue();
if (cimStringVal.startsWith("ovf:/disk/"))
{
cimStringVal = cimStringVal.replace("ovf:/disk/", "");
break;
}
else if (cimStringVal.startsWith("/disk/"))
{
cimStringVal = cimStringVal.replace("/disk/", "");
break;
}
}
return cimStringVal;
}
}