/**
* 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.api.services.appslibrary;
import static com.abiquo.api.util.URIResolver.buildPath;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.ws.rs.core.MultivaluedMap;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.abiquo.api.exceptions.APIError;
import com.abiquo.api.resources.EnterpriseResource;
import com.abiquo.api.resources.EnterprisesResource;
import com.abiquo.api.resources.appslibrary.CategoriesResource;
import com.abiquo.api.resources.appslibrary.CategoryResource;
import com.abiquo.api.resources.appslibrary.DatacenterRepositoriesResource;
import com.abiquo.api.resources.appslibrary.DatacenterRepositoryResource;
import com.abiquo.api.resources.appslibrary.VirtualMachineTemplateResource;
import com.abiquo.api.resources.appslibrary.VirtualMachineTemplatesResource;
import com.abiquo.api.services.DefaultApiService;
import com.abiquo.api.services.EnterpriseService;
import com.abiquo.api.services.InfrastructureService;
import com.abiquo.api.services.UserService;
import com.abiquo.api.services.stub.AMServiceStub;
import com.abiquo.api.spring.security.SecurityService;
import com.abiquo.api.util.URIResolver;
import com.abiquo.model.enumerator.DiskFormatType;
import com.abiquo.model.enumerator.HypervisorType;
import com.abiquo.model.enumerator.Privileges;
import com.abiquo.model.enumerator.StatefulInclusion;
import com.abiquo.model.rest.RESTLink;
import com.abiquo.server.core.appslibrary.AppsLibraryRep;
import com.abiquo.server.core.appslibrary.Category;
import com.abiquo.server.core.appslibrary.VirtualImageConversionDAO;
import com.abiquo.server.core.appslibrary.VirtualMachineTemplate;
import com.abiquo.server.core.appslibrary.VirtualMachineTemplateDto;
import com.abiquo.server.core.cloud.VirtualDatacenter;
import com.abiquo.server.core.cloud.VirtualDatacenterRep;
import com.abiquo.server.core.cloud.VirtualMachineRep;
import com.abiquo.server.core.enterprise.DatacenterLimits;
import com.abiquo.server.core.enterprise.Enterprise;
import com.abiquo.server.core.infrastructure.Datacenter;
import com.abiquo.server.core.infrastructure.Repository;
import com.abiquo.server.core.infrastructure.RepositoryDAO;
import com.abiquo.tracer.ComponentType;
import com.abiquo.tracer.EventType;
import com.abiquo.tracer.SeverityType;
@Service
public class VirtualMachineTemplateService extends DefaultApiService
{
final private static Logger logger =
LoggerFactory.getLogger(VirtualMachineTemplateService.class);
@Autowired
private RepositoryDAO repositoryDao;
@Autowired
private InfrastructureService infrastructureService;
@Autowired
private EnterpriseService enterpriseService;
@Autowired
private AppsLibraryRep appsLibraryRep;
@Autowired
private VirtualMachineRep virtualMachineRep;
@Autowired
private CategoryService categoryService;
@Autowired
private VirtualDatacenterRep virtualDatacenterRep;
@Autowired
private SecurityService securityService;
@Autowired
private UserService userService;
@Autowired
private AMServiceStub am;
public VirtualMachineTemplateService()
{
}
public VirtualMachineTemplateService(final EntityManager em)
{
this.repositoryDao = new RepositoryDAO(em);
this.infrastructureService = new InfrastructureService(em);
this.enterpriseService = new EnterpriseService(em);
this.appsLibraryRep = new AppsLibraryRep(em);
this.virtualMachineRep = new VirtualMachineRep(em);
this.categoryService = new CategoryService(em);
this.virtualDatacenterRep = new VirtualDatacenterRep(em);
}
/**
* Ignoring credentinals check
*/
@Transactional(readOnly = true)
public Repository getDatacenterRepositoryBySystem(final Integer dcId, final Integer enterpriseId)
{
Datacenter datacenter = infrastructureService.getDatacenter(dcId);
Repository repo = repositoryDao.findByDatacenter(datacenter);
if (repo == null)
{
addNotFoundErrors(APIError.VIMAGE_DATACENTER_REPOSITORY_NOT_FOUND);
flushErrors();
}
return repo;
}
@Transactional(readOnly = true)
public Repository getDatacenterRepository(final Integer dcId, final Integer enterpriseId)
{
checkEnterpriseCanUseDatacenter(enterpriseId, dcId);
return getDatacenterRepositoryBySystem(dcId, enterpriseId);
}
/**
* Get repository for allowed (limits defined) datacenters for the provided enterprise.
*/
@Transactional(readOnly = true)
public List<Repository> getDatacenterRepositories(final Integer enterpriseId)
{
List<Repository> repos = new LinkedList<Repository>();
for (DatacenterLimits dclimit : enterpriseService.findLimitsByEnterprise(enterpriseId))
{
try
{
repos.add(getDatacenterRepository(dclimit.getDatacenter().getId(), enterpriseId));
}
catch (Exception ex)
{
tracer.log(SeverityType.WARNING, ComponentType.DATACENTER,
EventType.APPLIANCE_MANAGER_CONFIGURATION, "appliancemanager.error", dclimit
.getDatacenter().getName());
}
}
return repos;
}
@Transactional(readOnly = true)
public VirtualMachineTemplate getVirtualMachineTemplate(final Integer enterpriseId,
final Integer datacenterId, final Integer virtualMachineTemplateId)
{
// When shared all enterprises can retrieve it (as long as the privileges are met)
VirtualMachineTemplate virtualMachineTemplate =
appsLibraryRep.findVirtualMachineTemplateById(virtualMachineTemplateId);
// We can't disclose whether the virtual machine template exists to user without privilege
if (virtualMachineTemplate != null && virtualMachineTemplate.isShared())
{
checkEnterpriseCanUseVMTShared(enterpriseId, datacenterId);
}
else
{
// Check that the enterprise can use the datacenter (also checks enterprise and
// datacenter exists)
checkEnterpriseCanUseDatacenter(enterpriseId, datacenterId);
if (virtualMachineTemplate == null)
{
addNotFoundErrors(APIError.NON_EXISTENT_VIRTUAL_MACHINE_TEMPLATE);
flushErrors();
}
}
return virtualMachineTemplate;
}
/**
* Gets the list of compatible(*) virtual machine templates available in the provided enterprise
* and repository.
*
* @param category null indicate all categories (no filter)
* @param connection (*) null indicate no filter compatibles, else return machine templates
* compatibles or with compatible conversions. @see {@link VirtualImageConversionDAO}
*/
@Transactional(readOnly = true)
public List<VirtualMachineTemplate> getVirtualMachineTemplates(final Integer enterpriseId,
final Integer datacenterId, final String categoryName, final String hypervisorName,
final Boolean imported)
{
Enterprise enterprise = enterpriseService.getEnterprise(enterpriseId);
Repository repository = getDatacenterRepository(datacenterId, enterpriseId);
Category category = null;
HypervisorType hypervisor = null;
if (categoryName != null)
{
category = appsLibraryRep.findCategoryByName(categoryName, null);
if (category == null)
{
category = appsLibraryRep.findCategoryByName(categoryName, enterprise);
}
}
if (hypervisorName != null)
{
try
{
hypervisor = HypervisorType.fromValue(hypervisorName);
}
catch (Exception ex)
{
// Validate the hypervisor type
addValidationErrors(APIError.INVALID_HYPERVISOR_TYPE);
flushErrors();
}
}
List<VirtualMachineTemplate> templates =
appsLibraryRep
.findVirtualMachineTemplates(enterprise, repository, category, hypervisor);
if (imported)
{
// adds the virtual machine templates from imported virtual machines. aka: they are not
// in the repository.
templates.addAll(appsLibraryRep.findImportedVirtualMachineTemplates(enterprise,
datacenterId, category, hypervisor));
}
return templates;
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public VirtualMachineTemplate updateVirtualMachineTemplate(final Integer enterpriseId,
final Integer datacenterId, final Integer virtualMachineTemplateId,
final VirtualMachineTemplateDto virtualMachineTemplate)
{
VirtualMachineTemplate old =
getVirtualMachineTemplate(enterpriseId, datacenterId, virtualMachineTemplateId);
// If shared and with instances then those instance cannot access to the template anymore
if (old.isShared() && !virtualMachineTemplate.isShared()
&& virtualMachineRep.existsVirtualMachineFromTemplate(virtualMachineTemplateId))
{
tracer.log(SeverityType.CRITICAL, ComponentType.VIRTUAL_APPLIANCE, EventType.VI_UPDATE,
"vmtemplate.modified.notshared.instance", new Object[] {virtualMachineTemplateId,
old.getName()});
addConflictErrors(APIError.VMTEMPLATE_TEMPLATE_USED_BY_VIRTUAL_MACHINES_CANNOT_BE_UNSHARED);
flushErrors();
}
if (!virtualMachineTemplate.getIconUrl().isEmpty()
&& virtualMachineTemplate.getIconUrl() != null
&& !validURI(virtualMachineTemplate.getIconUrl()))
{
addConflictErrors(APIError.VIMAGE_MALFORMED_ICON_URI);
flushErrors();
}
old.setCostCode(virtualMachineTemplate.getCostCode());
old.setCpuRequired(virtualMachineTemplate.getCpuRequired());
old.setDescription(virtualMachineTemplate.getDescription());
old.setDiskFileSize(virtualMachineTemplate.getDiskFileSize());
DiskFormatType type = DiskFormatType.fromValue(virtualMachineTemplate.getDiskFormatType());
old.setDiskFormatType(type);
old.setHdRequiredInBytes(virtualMachineTemplate.getHdRequired());
old.setName(virtualMachineTemplate.getName());
old.setPath(virtualMachineTemplate.getPath());
old.setRamRequired(virtualMachineTemplate.getRamRequired());
old.setShared(virtualMachineTemplate.isShared());
old.setChefEnabled(virtualMachineTemplate.isChefEnabled());
old.setIconUrl(virtualMachineTemplate.getIconUrl());
// retrieve the links
RESTLink categoryLink = virtualMachineTemplate.searchLink(CategoryResource.CATEGORY);
RESTLink enterpriseLink = virtualMachineTemplate.searchLink(EnterpriseResource.ENTERPRISE);
RESTLink datacenterRepositoryLink =
virtualMachineTemplate.searchLink(DatacenterRepositoryResource.DATACENTER_REPOSITORY);
RESTLink masterLink = virtualMachineTemplate.searchLink("master");
// check the links
if (enterpriseLink != null)
{
String buildPath =
buildPath(EnterprisesResource.ENTERPRISES_PATH, EnterpriseResource.ENTERPRISE_PARAM);
MultivaluedMap<String, String> map =
URIResolver.resolveFromURI(buildPath, enterpriseLink.getHref());
if (map == null || !map.containsKey(EnterpriseResource.ENTERPRISE))
{
addValidationErrors(APIError.INVALID_ENTERPRISE_LINK);
flushErrors();
}
Integer enterpriseIdFromLink =
Integer.parseInt(map.getFirst(EnterpriseResource.ENTERPRISE));
if (!enterpriseIdFromLink.equals(enterpriseId))
{
addConflictErrors(APIError.VMTEMPLATE_ENTERPRISE_CANNOT_BE_CHANGED);
flushErrors();
}
}
if (datacenterRepositoryLink != null)
{
String buildPath =
buildPath(EnterprisesResource.ENTERPRISES_PATH,
EnterpriseResource.ENTERPRISE_PARAM,
DatacenterRepositoriesResource.DATACENTER_REPOSITORIES_PATH,
DatacenterRepositoryResource.DATACENTER_REPOSITORY_PARAM);
MultivaluedMap<String, String> map =
URIResolver.resolveFromURI(buildPath, datacenterRepositoryLink.getHref());
if (map == null || !map.containsKey(DatacenterRepositoryResource.DATACENTER_REPOSITORY))
{
addValidationErrors(APIError.INVALID_DATACENTER_RESPOSITORY_LINK);
flushErrors();
}
Integer datacenterRepositoryId =
Integer.parseInt(map.getFirst(DatacenterRepositoryResource.DATACENTER_REPOSITORY));
if (!datacenterRepositoryId.equals(old.getRepository().getDatacenter().getId()))
{
addConflictErrors(APIError.VMTEMPLATE_DATACENTER_REPOSITORY_CANNOT_BE_CHANGED);
flushErrors();
}
}
if (categoryLink != null)
{
String buildPath =
buildPath(CategoriesResource.CATEGORIES_PATH, CategoryResource.CATEGORY_PARAM);
MultivaluedMap<String, String> map =
URIResolver.resolveFromURI(buildPath, categoryLink.getHref());
if (map == null || !map.containsKey(CategoryResource.CATEGORY))
{
addValidationErrors(APIError.INVALID_CATEGORY_LINK);
flushErrors();
}
Integer categoryId = Integer.parseInt(map.getFirst(CategoryResource.CATEGORY));
if (!categoryId.equals(old.getCategory().getId()))
{
Category category = appsLibraryRep.findCategoryById(categoryId);
if (category == null)
{
addConflictErrors(APIError.NON_EXISTENT_CATEGORY);
flushErrors();
}
old.setCategory(category);
}
}
// the cases when the master was null or not null but the new master template is null
// allowed
if (masterLink == null)
{
if (old.getMaster() != null)
{
if (tracer != null)
{
tracer.log(SeverityType.INFO, ComponentType.DATACENTER, EventType.VI_UPDATE,
"virtualMachineTemplate.convertedToMaster", old.getName());
}
}
old.setMaster(null);
}
// case when the new master isn't null and the old can be null or the same template or a new
// template
else
{
String buildPath =
buildPath(EnterprisesResource.ENTERPRISES_PATH,
EnterpriseResource.ENTERPRISE_PARAM,
DatacenterRepositoriesResource.DATACENTER_REPOSITORIES_PATH,
DatacenterRepositoryResource.DATACENTER_REPOSITORY_PARAM,
VirtualMachineTemplatesResource.VIRTUAL_MACHINE_TEMPLATES_PATH,
VirtualMachineTemplateResource.VIRTUAL_MACHINE_TEMPLATE_PARAM);
MultivaluedMap<String, String> map =
URIResolver.resolveFromURI(buildPath, masterLink.getHref());
if (map == null
|| !map.containsKey(VirtualMachineTemplateResource.VIRTUAL_MACHINE_TEMPLATE))
{
addValidationErrors(APIError.INVALID_VMTEMPLATE_LINK);
flushErrors();
}
Integer masterId =
Integer.parseInt(map
.getFirst(VirtualMachineTemplateResource.VIRTUAL_MACHINE_TEMPLATE));
if (old.getMaster() == null || !masterId.equals(old.getMaster().getId()))
{
addConflictErrors(APIError.VMTEMPLATE_MASTER_TEMPLATE_CANNOT_BE_CHANGED);
flushErrors();
}
// if its the same no change is necessary
}
appsLibraryRep.updateVirtualMachineTemplate(old);
return old;
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void deleteVirtualMachineTemplate(final Integer enterpriseId,
final Integer datacenterId, final Integer virtualMachineTemplateId)
{
VirtualMachineTemplate vmtemplateToDelete =
getVirtualMachineTemplate(enterpriseId, datacenterId, virtualMachineTemplateId);
Enterprise ent = enterpriseService.getEnterprise(enterpriseId);
// all the checks to delete the virtual machine template
// check if any virtual appliance is using the template
if (virtualMachineRep.hasVirtualMachineTemplate(virtualMachineTemplateId))
{
addConflictErrors(APIError.VMTEMPLATE_TEMPLATE_USED_BY_VIRTUAL_MACHINES_CANNOT_BE_DELETED);
flushErrors();
}
if (appsLibraryRep.isMaster(vmtemplateToDelete))
{
addConflictErrors(APIError.VMTEMPLATE_MASTER_TEMPLATE_CANNOT_BE_DELETED);
flushErrors();
}
if (vmtemplateToDelete.isStateful())
{
addConflictErrors(APIError.VMTEMPLATE_STATEFUL_TEMPLATE_CANNOT_BE_DELETED);
flushErrors();
}
if (vmtemplateToDelete.isShared())
{
// assert if the enterprise is the enterprise of the virtual machine template
// moreover check if the current user doesn't have the privelige to impersonate between
// enterprises
if (!vmtemplateToDelete.getEnterprise().getId().equals(ent.getId())
&& !securityService.hasPrivilege(Privileges.ENTERPRISE_ADMINISTER_ALL, userService
.getCurrentUser()))
{
addConflictErrors(APIError.VMTEMPLATE_SHARED_TEMPLATE_FROM_OTHER_ENTERPRISE);
flushErrors();
}
}
// if the virtual machine template is shared only the users from same enterprise can delete
// check if the user is for the same enterprise otherwise deny allegating permissions
String viOvf = vmtemplateToDelete.getOvfid();
if (StringUtils.isEmpty(viOvf))
{
// this is a bundle of an imported virtual machine (it havent OVF)
viOvf = codifyBundleImportedOVFid(vmtemplateToDelete.getPath());
}
am.delete(datacenterId, enterpriseId, viOvf);
// delete
appsLibraryRep.deleteVirtualMachineTemplate(vmtemplateToDelete);
if (tracer != null)
{
tracer.log(SeverityType.INFO, ComponentType.DATACENTER, EventType.VI_DELETE,
"virtualMachineTemplate.deleted", vmtemplateToDelete.getName());
}
}
private String codifyBundleImportedOVFid(final String vipath)
{
return String.format("http://bundle-imported/%s", vipath);
}
@Transactional(readOnly = true)
public List<VirtualMachineTemplate> findStatefulVirtualMachineTemplatesByDatacenter(
final Integer enterpriseId, final Integer datacenterId, final StatefulInclusion stateful)
{
return findStatefulVirtualMachineTemplatesByDatacenter(enterpriseId, datacenterId, null,
stateful);
}
@Transactional(readOnly = true)
public List<String> findIconsByEnterprise(final Integer enterpriseId)
{
return appsLibraryRep.findIconsByEnterprise(enterpriseId);
}
@Transactional(readOnly = true)
public List<VirtualMachineTemplate> findStatefulVirtualMachineTemplatesByDatacenter(
final Integer enterpriseId, final Integer datacenterId, final Integer virtualdatacenterId,
final StatefulInclusion stateful)
{
checkEnterpriseCanUseDatacenter(enterpriseId, datacenterId);
Datacenter datacenter = infrastructureService.getDatacenter(datacenterId);
if (virtualdatacenterId == null)
{
return appsLibraryRep.findStatefulVirtualMachineTemplatesByDatacenter(datacenter,
stateful);
}
else
{
VirtualDatacenter virtualdatacenter =
virtualDatacenterRep.findById(virtualdatacenterId);
return appsLibraryRep
.findStatefulVirtualMachineTemplatesByDatacenterAndVirtualDatacenter(datacenter,
virtualdatacenter, stateful);
}
}
@Transactional(readOnly = true)
public List<VirtualMachineTemplate> findStatefulVirtualMachineTemplatesByCategoryAndDatacenter(
final Integer enterpriseId, final Integer datacenterId, final String categoryName,
final StatefulInclusion stateful)
{
return findStatefulVirtualMachineTemplatesByCategoryAndDatacenter(enterpriseId,
datacenterId, null, categoryName, stateful);
}
@Transactional(readOnly = true)
public List<VirtualMachineTemplate> findStatefulVirtualMachineTemplatesByCategoryAndDatacenter(
final Integer enterpriseId, final Integer datacenterId, final Integer virtualdatacenterId,
final String categoryName, final StatefulInclusion stateful)
{
checkEnterpriseCanUseDatacenter(enterpriseId, datacenterId);
Datacenter datacenter = infrastructureService.getDatacenter(datacenterId);
Enterprise enterprise = userService.getCurrentUser().getEnterprise();
Category category =
categoryService.getCategoryByNameAndEnterprise(categoryName, enterprise);
if (virtualdatacenterId == null)
{
return appsLibraryRep.findStatefulVirtualMachineTemplatesByCategoryAndDatacenter(
category, datacenter, stateful);
}
else
{
VirtualDatacenter virtualdatacenter =
virtualDatacenterRep.findById(virtualdatacenterId);
return appsLibraryRep
.findStatefulVirtualMachineTemplatesByCategoryAndDatacenterandVirutalDatacenter(
category, datacenter, virtualdatacenter, stateful);
}
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void insertVirtualMachineTemplate(final VirtualMachineTemplate template)
{
appsLibraryRep.insertVirtualMachineTemplate(template);
}
/**
* Checks the enterprise and datacenter exists and have a limits relation (datacenter allowed by
* enterprise).
*/
private void checkEnterpriseCanUseDatacenter(final Integer enterpriseId,
final Integer datacenterId)
{
DatacenterLimits limits =
enterpriseService.findLimitsByEnterpriseAndDatacenter(enterpriseId, datacenterId);
if (limits == null)
{
addConflictErrors(APIError.ENTERPRISE_NOT_ALLOWED_DATACENTER);
flushErrors();
}
}
/**
* Checks the enterprise and datacenter exists and have a limits relation (datacenter allowed by
* enterprise). Retrieve shared virtual machine templates
*/
private void checkEnterpriseCanUseVMTShared(final Integer enterpriseId,
final Integer datacenterId)
{
DatacenterLimits limits =
enterpriseService.findLimitsByEnterpriseVMTShared(enterpriseId, datacenterId);
if (limits == null)
{
addConflictErrors(APIError.ENTERPRISE_NOT_ALLOWED_DATACENTER);
flushErrors();
}
}
private boolean validURI(final String uri)
{
try
{
new URL(uri);
return true;
}
catch (MalformedURLException e)
{
return false;
}
}
}