/** * 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.filesystem; import static com.abiquo.am.services.TemplateConventions.END_OF_FILE_MARK; import static com.abiquo.am.services.TemplateConventions.FORMATS_PATH; import static com.abiquo.am.services.TemplateConventions.OVF_BUNDLE_IMPORTED_PREFIX; import static com.abiquo.am.services.TemplateConventions.TEMPLATE_STATUS_DOWNLOADING_MARK; import static com.abiquo.am.services.TemplateConventions.TEMPLATE_STATUS_ERROR_MARK; import static com.abiquo.am.services.TemplateConventions.createBundleOvfId; import static com.abiquo.am.services.TemplateConventions.getBundleMasterOvfId; import static com.abiquo.am.services.TemplateConventions.getBundleSnapshot; import static com.abiquo.am.services.TemplateConventions.getRelativeTemplatePath; import static com.abiquo.am.services.TemplateConventions.getTemplatePath; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.util.Scanner; import java.util.Set; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.dmtf.schemas.ovf.envelope._1.EnvelopeType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.abiquo.am.exceptions.AMError; import com.abiquo.am.services.TemplateConventions; import com.abiquo.am.services.filesystem.filters.BundleImageFileFilter; import com.abiquo.am.services.filesystem.filters.FormatsFilter; import com.abiquo.appliancemanager.exceptions.AMException; import com.abiquo.appliancemanager.transport.TemplateStateDto; import com.abiquo.appliancemanager.transport.TemplateStatusEnumType; import com.abiquo.ovfmanager.ovf.OVFReferenceUtils; import com.abiquo.ovfmanager.ovf.xml.OVFSerializer; public class TemplateFileSystem { private final static Logger LOG = LoggerFactory.getLogger(TemplateFileSystem.class); /** * if (diskId == null || diskId.length() == 0) { }// remove all the package */ public static EnvelopeType getEnvelope(final String enterpriseRepositoryPath, final String ovfId) { EnvelopeType envelope; String ovfPath = enterpriseRepositoryPath + getRelativeTemplatePath(ovfId); File ovfFile = new File(ovfPath); if (!ovfFile.exists()) { throw new AMException(AMError.TEMPLATE_NOT_FOUND, ovfId); } FileInputStream fileIs = null; try { fileIs = new FileInputStream(ovfFile); envelope = OVFSerializer.getInstance().readXMLEnvelope(fileIs); } catch (Exception e) { throw new AMException(AMError.TEMPLATE_MALFORMED, ovfId, e); } finally { try { if (fileIs != null) { fileIs.close(); } } catch (IOException e) { e.printStackTrace(); } } return envelope; } public static TemplateStateDto getTemplateStatus(final String enterpriseRepositoryPath, final String ovfId) { final TemplateStateDto state = new TemplateStateDto(); state.setOvfId(ovfId); if (!TemplateConventions.isValidOVFLocation(ovfId)) { state.setStatus(TemplateStatusEnumType.ERROR); state.setErrorCause(AMError.TEMPLATE_INVALID_LOCATION.toString()); return state; } final boolean isInstance = TemplateConventions.isBundleOvfId(ovfId); final String packagePath = getTemplatePath(enterpriseRepositoryPath, ovfId); if (isInstance) { final String snapshot = ovfId.substring(ovfId.lastIndexOf('/') + 1, ovfId.indexOf("-snapshot-")); final String masterPath = TemplateConventions.getTemplatePath(enterpriseRepositoryPath, TemplateConventions.getBundleMasterOvfId(ovfId)); final File folder = new File(masterPath); if (folder.list(new FilenameFilter() { @Override public boolean accept(final File file, final String name) { return name.startsWith(snapshot); } }).length == 0) { state.setStatus(TemplateStatusEnumType.NOT_DOWNLOAD); } else { state.setStatus(TemplateStatusEnumType.DOWNLOAD); } return state; } final String ovfEnvelopePath = FilenameUtils.concat(enterpriseRepositoryPath, getRelativeTemplatePath(ovfId)); File errorMarkFile = new File(packagePath + TEMPLATE_STATUS_ERROR_MARK); if (errorMarkFile.exists()) { state.setStatus(TemplateStatusEnumType.ERROR); state.setErrorCause(readErrorMarkFile(errorMarkFile)); } else if (new File(packagePath + TEMPLATE_STATUS_DOWNLOADING_MARK).exists()) { state.setStatus(TemplateStatusEnumType.DOWNLOADING); } else if (!new File(ovfEnvelopePath).exists()) { state.setStatus(TemplateStatusEnumType.NOT_DOWNLOAD); } else { state.setStatus(TemplateStatusEnumType.DOWNLOAD); } return state; } /** * synch to avoid multiple changes on the package folder (before 'clearOVFStatusMarks'). * * @throws RepositoryException */ public static synchronized void createTemplateStatusMarks( final String enterpriseRepositoryPath, final String ovfId, final TemplateStatusEnumType status, final String errorMsg) { final String packagePath = getTemplatePath(enterpriseRepositoryPath, ovfId); clearTemplateStatusMarks(packagePath); File mark = null; boolean errorCreate = false; try { switch (status) { case DOWNLOAD: // after clean the prev. marks, nothing to do. break; case NOT_DOWNLOAD: // once the OVF envelope (.ovf) is deleted its NOT_FOUND break; case DOWNLOADING: mark = new File(packagePath + '/' + TEMPLATE_STATUS_DOWNLOADING_MARK); errorCreate = !mark.createNewFile(); break; case ERROR: mark = new File(packagePath + '/' + TEMPLATE_STATUS_ERROR_MARK); errorCreate = !mark.createNewFile(); if (!errorCreate) { FileWriter fileWriter = new FileWriter(mark); fileWriter.append(errorMsg); fileWriter.close(); } break; default: throw new AMException(AMError.TEMPLATE_UNKNOW_STATUS, status.name()); }// switch } catch (IOException ioe) { throw new AMException(AMError.TEMPLATE_CHANGE_STATUS, mark.getAbsoluteFile() .getAbsolutePath()); } if (errorCreate) { throw new AMException(AMError.TEMPLATE_CHANGE_STATUS, mark.getAbsoluteFile() .getAbsolutePath()); } } private static void clearTemplateStatusMarks(final String packagePath) { File errorMarkFile = new File(packagePath + TEMPLATE_STATUS_ERROR_MARK); File downloadingMarkFile = new File(packagePath + TEMPLATE_STATUS_DOWNLOADING_MARK); if (errorMarkFile.exists()) { if (!errorMarkFile.delete()) { throw new AMException(AMError.TEMPLATE_CHANGE_STATUS, "removing " + errorMarkFile.getAbsolutePath()); } } if (downloadingMarkFile.exists()) { if (!downloadingMarkFile.delete()) { throw new AMException(AMError.TEMPLATE_CHANGE_STATUS, "removing " + downloadingMarkFile.getAbsolutePath()); } } } private static String readErrorMarkFile(final File errorMarkFile) { assert errorMarkFile.exists(); Scanner errorMarkReader = null; try { errorMarkReader = new Scanner(errorMarkFile).useDelimiter(END_OF_FILE_MARK); } catch (FileNotFoundException e) { // was checked } return errorMarkReader.next(); } public static File getFileByPath(final String filePath) { File f = new File(filePath); if (!f.exists()) { throw new AMException(AMError.DISK_FILE_NOT_FOUND, filePath); } return f; } public static void copyFileToTemplatePath(final String packagePath, final File file) { // Transfer the upload content into the repository file system final String filePath = packagePath + file.getName(); File f = new File(filePath); // TODO check do not exist and can be created try { FileUtils.moveFile(file, f); } catch (IOException e) { throw new AMException(AMError.DISK_FILE_MOVE, packagePath, e); } } private static void createTemplateFormatsFolder(final String packagePath) { final String formatsPath = packagePath + '/' + FORMATS_PATH; File formatsFolder = new File(formatsPath); if (!formatsFolder.exists()) // !! XXX assume if the enterpriseRepositoryPath can be written then also all the package // and formats path. { if (!formatsFolder.mkdir()) { // throw new AMException(AMError.TEMPLATE_INSTALL, "creating format folder"); } } } public static void createTemplateFolder(final String enterpriseRepositoryPath, final String ovfId) { final String packagePath = getTemplatePath(enterpriseRepositoryPath, ovfId); // final String packageName = repository.getOVFPackageName(ovfId); // should be equals to concatenation // just create the folder File packFile = new File(packagePath); if (!packFile.exists()) { if (!packFile.mkdirs()) { // throw new AMException(AMError.TEMPLATE_INSTALL, packagePath); } } createTemplateFormatsFolder(packagePath); } /** * Write the OVF envelope file to the OVF package folder. * * @param envelpePath, path on the Enterprise Repository identifying the current OVF. * @param envelope, the OVF envelope document to be write. * @param description, the OVF description associated. Change its OVFFile attribute to AM's * internal repository relative path. * @throws RepositoryException, if some error occurs during this process, The package already * was being deployed. */ public static void writeOVFEnvelopeToTemplateFolder(final String envelopePath, final EnvelopeType envelope) { File envelopeFile = new File(envelopePath); if (envelopeFile.exists()) { throw new AMException(AMError.TEMPLATE_INSTALL, "already exist"); } try { if (!envelopeFile.createNewFile()) { throw new AMException(AMError.TEMPLATE_INSTALL, envelopePath); } } catch (IOException e) { throw new AMException(AMError.TEMPLATE_INSTALL, envelopePath); } FileOutputStream envelopeStream = null; try { envelopeStream = new FileOutputStream(envelopeFile); OVFSerializer.getInstance().writeXML(envelope, envelopeStream); } catch (Exception e1) // IOException or XMLException { throw new AMException(AMError.TEMPLATE_INSTALL, envelopePath, e1); } finally { if (envelopeStream != null) { try { envelopeStream.close(); } catch (IOException e) { throw new AMException(AMError.TEMPLATE_INSTALL, envelopePath); } } }// finally } public static void deleteBundleConversion(final String packagePath, final String fileName) { final String formatsPath = packagePath + "formats"; String[] formatFiles = new File(formatsPath).list(new FormatsFilter(fileName)); for (String formatFile : formatFiles) { final String filePath = formatsPath + formatFile; final File f = new File(filePath); // file should exist, just retrieved from filenamefilter if (!f.delete()) { final String cause = String.format("Can not delte the converted disk at [%s]", filePath); LOG.error(cause); } } } public static void deleteTemplate(final String packagePath) { File packageFile = new File(packagePath); String[] bundles = packageFile.list(new BundleImageFileFilter()); if (bundles == null || bundles.length == 0) { LOG.debug("There are any bundle, deleting all the folder"); try { FileUtils.deleteDirectory(packageFile); } catch (IOException e) { // caused by .nfs temp files (try retry in 5s) if (e instanceof FileNotFoundException) { try { Thread.sleep(5000); } catch (InterruptedException e1) { e1.printStackTrace(); } try { FileUtils.deleteDirectory(packageFile); } catch (IOException e1) { throw new AMException(AMError.TEMPLATE_DELETE, packageFile.getAbsolutePath(), e1); } }// nfs issue } return; } else { final StringBuffer stBuffer = new StringBuffer(); stBuffer.append("The selected Template has snapshot instances associates:"); for (String bund : bundles) { stBuffer.append("\n" + bund); } stBuffer.append("\n It can not be deleted."); throw new AMException(AMError.TEMPLATE_DELETE_INSTANCES, stBuffer.toString()); } } /** * imported bundles do not use the ''enterpriserepopath'' */ public static void deleteImportedBundle(final String BASE_REPO_PATH, final String ovfId) { final String path = ovfId.substring(OVF_BUNDLE_IMPORTED_PREFIX.length(), ovfId.lastIndexOf('/')); final String absPath = BASE_REPO_PATH + path; File importBundleDir = new File(absPath); if (!importBundleDir.exists() || !importBundleDir.isDirectory()) { throw new AMException(AMError.TEMPLATE_SNAPSHOT_IMPORT_NOT_EXIST, ovfId); } try { FileUtils.deleteDirectory(importBundleDir); } catch (IOException e) { LOG.error("Can not delete the bundle of an imported virtual machine, on folder {}", absPath); } } public static void deleteBundle(final String enterpriseRepositoryPath, final String ovfId) { final String masterOvf = getBundleMasterOvfId(ovfId); final String snapshot = getBundleSnapshot(ovfId); final String packagePath = getTemplatePath(enterpriseRepositoryPath, ovfId); final EnvelopeType envelope = getEnvelope(enterpriseRepositoryPath, masterOvf); Set<String> fileLocations = OVFReferenceUtils.getAllReferencedFileLocations(envelope); for (String fileLocation : fileLocations) { final String absoultePath = packagePath + fileLocation; final String bundleAbsoultePath = createBundleOvfId(absoultePath, snapshot); File file = new File(bundleAbsoultePath); if (!file.exists()) { LOG.warn("Path [{}] not exist, try to remove files starting with [{}]", absoultePath, snapshot); String[] filesStarting = new File(packagePath).list(new FormatsFilter(snapshot)); if (filesStarting != null) { for (String fileStart : filesStarting) { File fileStartFile = new File(packagePath + fileStart); if (!fileStartFile.delete()) { LOG.error("Try to remove the path [{}] but is not possible", fileStart); } } } } else { if (!file.delete()) { LOG.error("Try to remove the path [{}] but is not possible", absoultePath); } } final String relativePath = bundleAbsoultePath.substring(bundleAbsoultePath.lastIndexOf('/') + 1); deleteBundleConversion(packagePath, relativePath); }// all files final String envelopePath = enterpriseRepositoryPath + getRelativeTemplatePath(ovfId); new File(envelopePath).delete(); } }