package org.ovirt.engine.api.restapi.resource; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import javax.ws.rs.core.Response; import org.ovirt.engine.api.common.util.DetailHelper; import org.ovirt.engine.api.model.Console; import org.ovirt.engine.api.model.Disk; import org.ovirt.engine.api.model.DiskAttachment; import org.ovirt.engine.api.model.Template; import org.ovirt.engine.api.model.Templates; import org.ovirt.engine.api.model.VirtioScsi; import org.ovirt.engine.api.model.Vm; import org.ovirt.engine.api.resource.TemplateResource; import org.ovirt.engine.api.resource.TemplatesResource; import org.ovirt.engine.api.restapi.logging.Messages; import org.ovirt.engine.api.restapi.types.DiskMapper; import org.ovirt.engine.api.restapi.types.RngDeviceMapper; import org.ovirt.engine.api.restapi.util.DisplayHelper; import org.ovirt.engine.api.restapi.util.IconHelper; import org.ovirt.engine.api.restapi.util.ParametersHelper; import org.ovirt.engine.api.restapi.util.VmHelper; import org.ovirt.engine.core.common.action.AddVmTemplateParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.businessentities.Cluster; import org.ovirt.engine.core.common.businessentities.Entities; import org.ovirt.engine.core.common.businessentities.VmInit; import org.ovirt.engine.core.common.businessentities.VmRngDevice; import org.ovirt.engine.core.common.businessentities.VmStatic; import org.ovirt.engine.core.common.businessentities.VmTemplate; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType; import org.ovirt.engine.core.common.interfaces.SearchType; import org.ovirt.engine.core.common.queries.GetVmByVmNameForDataCenterParameters; import org.ovirt.engine.core.common.queries.GetVmTemplateParameters; import org.ovirt.engine.core.common.queries.IdQueryParameters; import org.ovirt.engine.core.common.queries.IdsQueryParameters; import org.ovirt.engine.core.common.queries.NameQueryParameters; import org.ovirt.engine.core.common.queries.VdcQueryParametersBase; import org.ovirt.engine.core.common.queries.VdcQueryReturnValue; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.compat.Guid; public class BackendTemplatesResource extends AbstractBackendCollectionResource<Template, VmTemplate> implements TemplatesResource { public static final String CLONE_PERMISSIONS = "clone_permissions"; public static final String SEAL = "seal"; public BackendTemplatesResource() { super(Template.class, VmTemplate.class); } @Override public Templates list() { if (isFiltered()) { return mapCollection(getBackendCollection(VdcQueryType.GetAllVmTemplates, new VdcQueryParametersBase(), SearchType.VmTemplate)); } else { return mapCollection(getBackendCollection(SearchType.VmTemplate)); } } @Override public TemplateResource getTemplateResource(String id) { return inject(new BackendTemplateResource(id)); } @Override public Response add(Template template) { validateParameters(template, "name", "vm.id|name"); validateIconParameters(template); Guid clusterId = null; Cluster cluster = null; if (namedCluster(template)) { clusterId = getClusterId(template); cluster = lookupCluster(clusterId); } if (template.getVersion() != null) { validateParameters(template.getVersion(), "baseTemplate"); } VmStatic originalVm = getVm(cluster, template); VmStatic staticVm = getMapper(Template.class, VmStatic.class).map(template, originalVm); if (namedCluster(template)) { staticVm.setClusterId(clusterId); } // REVISIT: powershell has a IsVmTemlateWithSameNameExist safety check AddVmTemplateParameters params = new AddVmTemplateParameters(staticVm, template.getName(), template.getDescription()); if (template.getVersion() != null) { params.setBaseTemplateId(Guid.createGuidFromString(template.getVersion().getBaseTemplate().getId())); params.setTemplateVersionName(template.getVersion().getVersionName()); } params.setConsoleEnabled(template.getConsole() != null && template.getConsole().isSetEnabled() ? template.getConsole().isEnabled() : !getConsoleDevicesForEntity(originalVm.getId()).isEmpty()); params.setVirtioScsiEnabled(template.isSetVirtioScsi() && template.getVirtioScsi().isSetEnabled() ? template.getVirtioScsi().isEnabled() : null); if(template.isSetSoundcardEnabled()) { params.setSoundDeviceEnabled(template.isSoundcardEnabled()); } else { params.setSoundDeviceEnabled(!VmHelper.getSoundDevicesForEntity(this, originalVm.getId()).isEmpty()); } if (template.isSetRngDevice()) { params.setUpdateRngDevice(true); params.setRngDevice(RngDeviceMapper.map(template.getRngDevice(), null)); } DisplayHelper.setGraphicsToParams(template.getDisplay(), params); boolean isDomainSet = false; if (template.isSetStorageDomain() && template.getStorageDomain().isSetId()) { params.setDestinationStorageDomainId(asGuid(template.getStorageDomain().getId())); isDomainSet = true; } params.setDiskInfoDestinationMap( getDestinationTemplateDiskMap( template.getVm(), originalVm.getId(), params.getDestinationStorageDomainId(), isDomainSet ) ); setupOptionalParameters(params); IconHelper.setIconToParams(template, params); Response response = performCreate( VdcActionType.AddVmTemplate, params, new QueryIdResolver<Guid>(VdcQueryType.GetVmTemplate, GetVmTemplateParameters.class) ); Template result = (Template) response.getEntity(); if (result != null) { DisplayHelper.adjustDisplayData(this, result); } return response; } private void validateIconParameters(Template incoming) { if (!IconHelper.validateIconParameters(incoming)) { throw new BaseBackendResource.WebFaultException(null, localize(Messages.INVALID_ICON_PARAMETERS), Response.Status.BAD_REQUEST); } } void setupOptionalParameters(AddVmTemplateParameters params) { boolean clonePermissions = ParametersHelper.getBooleanParameter(httpHeaders, uriInfo, CLONE_PERMISSIONS, true, false); boolean seal = ParametersHelper.getBooleanParameter(httpHeaders, uriInfo, SEAL, true, false); if (clonePermissions) { params.setCopyVmPermissions(clonePermissions); } if (seal) { params.setSealTemplate(seal); } } private Cluster lookupCluster(Guid id) { return getEntity(Cluster.class, VdcQueryType.GetClusterById, new IdQueryParameters(id), "GetClusterById"); } protected HashMap<Guid, DiskImage> getDestinationTemplateDiskMap(Vm vm, Guid vmId, Guid storageDomainId, boolean isTemplateGeneralStorageDomainSet) { HashMap<Guid, DiskImage> destinationTemplateDiskMap = null; if (vm.isSetDiskAttachments() && vm.getDiskAttachments().isSetDiskAttachments()) { destinationTemplateDiskMap = new HashMap<>(); Map<Guid, org.ovirt.engine.core.common.businessentities.storage.Disk> vmSourceDisks = queryVmDisksMap(vmId); for (DiskAttachment diskAttachment : vm.getDiskAttachments().getDiskAttachments()) { Disk disk = diskAttachment.getDisk(); if (disk == null || !disk.isSetId()) { continue; } Guid currDiskID = asGuid(disk.getId()); org.ovirt.engine.core.common.businessentities.storage.Disk sourceDisk = vmSourceDisks.get(currDiskID); // VM template can only have disk images if (sourceDisk == null || !isDiskImage(sourceDisk)) { continue; } DiskImage destinationDisk = (DiskImage) DiskMapper.map(disk, sourceDisk); if (isTemplateGeneralStorageDomainSet) { destinationDisk.setStorageIds(new ArrayList<>(Arrays.asList(storageDomainId))); } // Since domain can be changed, do not set profile and quota for this disk. destinationDisk.setDiskProfileId(null); destinationDisk.setQuotaId(null); destinationTemplateDiskMap.put(destinationDisk.getId(), destinationDisk); } } return destinationTemplateDiskMap; } private boolean isDiskImage(org.ovirt.engine.core.common.businessentities.storage.Disk disk) { return disk.getDiskStorageType() == DiskStorageType.IMAGE; } private Map<Guid, org.ovirt.engine.core.common.businessentities.storage.Disk> queryVmDisksMap(Guid vmId) { List<org.ovirt.engine.core.common.businessentities.storage.Disk> vmDisks = getBackendCollection( org.ovirt.engine.core.common.businessentities.storage.Disk.class, VdcQueryType.GetAllDisksByVmId, new IdQueryParameters(vmId) ); return Entities.businessEntitiesById(vmDisks); } protected Templates mapCollection(List<VmTemplate> entities) { Set<String> details = DetailHelper.getDetails(httpHeaders, uriInfo); boolean includeData = details.contains(DetailHelper.MAIN); boolean includeSize = details.contains("size"); if (includeData) { // Fill VmInit for entities - the search query no join the VmInit to Templates IdsQueryParameters params = new IdsQueryParameters(); List<Guid> ids = entities.stream().map(VmTemplate::getId).collect(Collectors.toList()); params.setId(ids); VdcQueryReturnValue queryReturnValue = runQuery(VdcQueryType.GetVmsInit, params); if (queryReturnValue.getSucceeded() && queryReturnValue.getReturnValue() != null) { List<VmInit> vmInits = queryReturnValue.getReturnValue(); Map<Guid, VmInit> initMap = Entities.businessEntitiesById(vmInits); for (VmTemplate template : entities) { template.setVmInit(initMap.get(template.getId())); } } } Templates collection = new Templates(); if (includeData) { for (VmTemplate entity : entities) { Template template = map(entity); collection.getTemplates().add(addLinks(populate(template, entity))); DisplayHelper.adjustDisplayData(this, template); } } if (includeSize) { collection.setSize((long) entities.size()); } return collection; } protected VmStatic getVm(Cluster cluster, Template template) { org.ovirt.engine.core.common.businessentities.VM vm; if (template.getVm().isSetId()) { vm = getEntity(org.ovirt.engine.core.common.businessentities.VM.class, VdcQueryType.GetVmByVmId, new IdQueryParameters(asGuid(template.getVm().getId())), template.getVm().getId()); } else { Guid dataCenterId = null; if (cluster != null && cluster.getStoragePoolId() != null) { dataCenterId = cluster.getStoragePoolId(); } GetVmByVmNameForDataCenterParameters params = new GetVmByVmNameForDataCenterParameters(dataCenterId, template.getVm().getName()); params.setFiltered(isFiltered()); vm = getEntity(org.ovirt.engine.core.common.businessentities.VM.class, VdcQueryType.GetVmByVmNameForDataCenter, params, template.getVm().getName()); } return vm.getStaticData(); } protected boolean namedCluster(Template template) { return template.isSetCluster() && template.getCluster().isSetName() && !template.getCluster().isSetId(); } protected Guid getClusterId(Template template) { return getEntity(Cluster.class, VdcQueryType.GetClusterByName, new NameQueryParameters(template.getCluster().getName()), "Cluster: name=" + template.getCluster().getName()).getId(); } @Override protected Template doPopulate(Template model, VmTemplate entity) { if (!model.isSetConsole()) { model.setConsole(new Console()); } model.getConsole().setEnabled(!getConsoleDevicesForEntity(entity.getId()).isEmpty()); if (!model.isSetVirtioScsi()) { model.setVirtioScsi(new VirtioScsi()); } model.getVirtioScsi().setEnabled(!VmHelper.getVirtioScsiControllersForEntity(this, entity.getId()).isEmpty()); model.setSoundcardEnabled(!VmHelper.getSoundDevicesForEntity(this, entity.getId()).isEmpty()); List<VmRngDevice> rngDevices = getRngDevices(entity.getId()); if (rngDevices != null && !rngDevices.isEmpty()) { model.setRngDevice(RngDeviceMapper.map(rngDevices.get(0), null)); } MemoryPolicyHelper.setupMemoryBalloon(model, this); return model; } private List<VmRngDevice> getRngDevices(Guid id) { return getEntity(List.class, VdcQueryType.GetRngDevice, new IdQueryParameters(id), "GetRngDevice", true); } private List<String> getConsoleDevicesForEntity(Guid id) { return getEntity(List.class, VdcQueryType.GetConsoleDevices, new IdQueryParameters(id), "GetConsoleDevices", true); } }