/** * 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; import java.util.Collection; import java.util.List; import java.util.UUID; import javax.persistence.EntityManager; 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 org.springframework.util.StringUtils; import com.abiquo.api.exceptions.APIError; import com.abiquo.api.exceptions.APIException; import com.abiquo.api.services.cloud.VirtualMachineService; import com.abiquo.api.services.stub.NodecollectorServiceStub; import com.abiquo.api.services.stub.VsmServiceStub; import com.abiquo.api.tracer.TracerLogger; import com.abiquo.model.enumerator.RemoteServiceType; import com.abiquo.model.enumerator.MachineState; import com.abiquo.scheduler.workload.VirtualimageAllocationService; import com.abiquo.server.core.cloud.Hypervisor; import com.abiquo.server.core.cloud.VirtualDatacenterRep; import com.abiquo.server.core.cloud.VirtualMachine; import com.abiquo.server.core.cloud.VirtualMachineState; import com.abiquo.server.core.infrastructure.Datacenter; import com.abiquo.server.core.infrastructure.Datastore; import com.abiquo.server.core.infrastructure.DatastoreDto; import com.abiquo.server.core.infrastructure.InfrastructureRep; import com.abiquo.server.core.infrastructure.Machine; import com.abiquo.server.core.infrastructure.MachineDto; import com.abiquo.server.core.infrastructure.Rack; import com.abiquo.server.core.infrastructure.RemoteService; import com.abiquo.server.core.infrastructure.UcsRack; import com.abiquo.tracer.ComponentType; import com.abiquo.tracer.EventType; import com.abiquo.tracer.SeverityType; @Service @Transactional(readOnly = false) public class MachineService extends DefaultApiService { protected static final Logger logger = LoggerFactory.getLogger(MachineService.class); @Autowired protected InfrastructureRep repo; @Autowired protected DatastoreService dataService; @Autowired private VsmServiceStub vsm; @Autowired protected RemoteServiceService remoteServiceService; @Autowired protected VirtualMachineService virtualMachineService; @Autowired protected VirtualDatacenterRep virtualDatacenterRep; @Autowired protected VirtualMachineAllocatorService allocationService; @Autowired protected NodecollectorServiceStub nodecollectorServiceStub; public MachineService() { } public MachineService(final EntityManager em) { repo = new InfrastructureRep(em); dataService = new DatastoreService(em); vsm = new VsmServiceStub(); virtualMachineService = new VirtualMachineService(em); virtualDatacenterRep = new VirtualDatacenterRep(em); remoteServiceService = new RemoteServiceService(em); nodecollectorServiceStub = new NodecollectorServiceStub(); allocationService = new VirtualMachineAllocatorService(em); tracer = new TracerLogger(); } public List<Machine> getMachinesByRack(final Integer rackId) { return getMachinesByRack(rackId, null); } public List<Machine> getMachinesByRack(final Integer rackId, final String filter) { Rack rack = repo.findRackById(rackId); List<Machine> machines = repo.findRackMachines(rack, filter); // If it is an UCS rack, put the property 'belongsToManagedRack' as true. // If they belong to a managed rack, a new {@link RESTLink} will be created // in the Dto informing the managed machines special functionality. UcsRack ucsRack = repo.findUcsRackById(rackId); if (ucsRack != null) { for (Machine machine : machines) { machine.setBelongsToManagedRack(Boolean.TRUE); } } return machines; } public Machine getMachine(final Integer id) { if (id == 0) { addValidationErrors(APIError.INVALID_ID); flushErrors(); } Machine machine = repo.findMachineById(id); if (machine == null) { addNotFoundErrors(APIError.NON_EXISTENT_MACHINE); flushErrors(); } return machine; } public Machine getMachine(final Integer datacenterId, final Integer rackId, final Integer machineId) { Machine machine = repo.findMachineByIds(datacenterId, rackId, machineId); if (machine == null) { addNotFoundErrors(APIError.NON_EXISTENT_MACHINE); flushErrors(); } return machine; } @Transactional(propagation = Propagation.REQUIRED) public Machine addMachine(final MachineDto machineDto, final Integer rackId) { Rack rack = repo.findRackById(rackId); Datacenter datacenter = rack.getDatacenter(); // Part 1: Insert the machine into database Machine machine = datacenter.createMachine(machineDto.getName(), machineDto.getDescription(), machineDto.getVirtualRamInMb(), machineDto.getVirtualRamUsedInMb(), machineDto.getVirtualCpuCores(), machineDto.getVirtualCpusUsed(), machineDto.getState(), machineDto.getVirtualSwitch()); machine.setRack(rack); isValidMachine(machine); // Monitoring machine RemoteService vsmRS = remoteServiceService.getRemoteService(datacenter.getId(), RemoteServiceType.VIRTUAL_SYSTEM_MONITOR); Hypervisor hypervisor = machine.createHypervisor(machineDto.getType(), machineDto.getIp(), machineDto.getIpService(), machineDto.getPort(), machineDto.getUser(), machineDto.getPassword()); vsm.monitor(vsmRS, hypervisor); repo.insertMachine(machine); // Part 2: Insert the hypervisor into database. if (repo.existAnyHypervisorWithIpInDatacenter(machineDto.getIp(), datacenter.getId())) { addConflictErrors(APIError.HYPERVISOR_EXIST_IP); } if (repo.existAnyHypervisorWithIpServiceInDatacenter(machineDto.getIpService(), datacenter.getId())) { addConflictErrors(APIError.HYPERVISOR_EXIST_SERVICE_IP); } flushErrors(); if (!hypervisor.isValid()) { addValidationErrors(hypervisor.getValidationErrors()); } flushErrors(); // Part 3. Call the Datastores resource to create them also // Add the Remote Services in database in case are informed in the request if (machineDto.getDatastores() != null) { for (DatastoreDto dataDto : machineDto.getDatastores().getCollection()) { // FIXME: All Datastores need to have an UUID in DB if (dataDto.getDatastoreUUID() == null) { dataDto.setDatastoreUUID(UUID.randomUUID().toString()); } dataService.addDatastore(dataDto, machine.getId()); } } repo.insertHypervisor(hypervisor); return machine; } @Transactional(propagation = Propagation.REQUIRED) public Machine modifyMachine(final Integer machineId, final MachineDto machineDto) throws Exception { Machine old = getMachine(machineId); if (old.getBelongsToManagedRack()) { if (!old.getName().equalsIgnoreCase(machineDto.getName())) { addValidationErrors(APIError.MANAGED_MACHINE_CANNOT_CHANGE_NAME); flushErrors(); } } if (old.getBelongsToManagedRack()) { if (!old.getName().equalsIgnoreCase(machineDto.getName())) { addValidationErrors(APIError.MANAGED_MACHINE_CANNOT_CHANGE_NAME); flushErrors(); } } old.setName(machineDto.getName()); old.setDescription(machineDto.getDescription()); old.setIpmiIP(machineDto.getIpmiIP()); old.setIpmiPort(machineDto.getIpmiPort()); old.setIpmiUser(machineDto.getIpmiUser()); old.setIpmiPassword(machineDto.getIpmiPassword()); // [ABICLOUDPREMIUM-2996] CPU and RAM must never be edited. Always must reflect the // real ones, obtained from NodeCollector in Machine creation. // Used CPU and RAM should not be edited, since the Allocator is the responsible of // maintaining them. // Only allow to modify the state if it is going to disabled or re-enabled if (machineDto.getState() == MachineState.HALTED || old.getState() == MachineState.HALTED) { old.setState(machineDto.getState()); } isValidMachine(old); // [ABICLOUDPREMIUM-1516] If ip service changes, must change the vrdp ip of the // virtual machines deployed in that hypervisor if (StringUtils.hasText(machineDto.getIpService()) && !machineDto.getIpService().equals(old.getHypervisor().getIpService())) { old.getHypervisor().setIpService(machineDto.getIpService()); updateVirtualMachines(old.getHypervisor(), machineDto.getIpService()); } repo.updateMachine(old); tracer.log(SeverityType.INFO, ComponentType.MACHINE, EventType.MACHINE_MODIFY, "machine.modified", old.getName(), old.getHypervisor() == null ? "No Hypervisor" : old .getHypervisor().getIp(), old.getHypervisor() == null ? "No Hypervisor" : old .getHypervisor().getType(), old.getState()); if (old.getInitiatorIQN() == null) { tracer.log(SeverityType.WARNING, ComponentType.MACHINE, EventType.MACHINE_MODIFY, "machine.withoutiqn", old.getName(), old.getHypervisor().getIp()); } return old; } private void updateVirtualMachines(final Hypervisor hypervisor, final String ipService) { Collection<VirtualMachine> vms = virtualMachineService.findByHypervisor(hypervisor); if (vms != null && !vms.isEmpty()) { for (VirtualMachine vm : vms) { if (StringUtils.hasText(vm.getVdrpIP())) { vm.setVdrpIP(ipService); } } } } @Transactional(propagation = Propagation.REQUIRED) public void removeMachine(final Integer id) { removeMachine(id, true); } @Transactional(propagation = Propagation.REQUIRED) public void removeMachine(final Integer id, final boolean force) { Machine machine = repo.findMachineById(id); Hypervisor hypervisor = machine.getHypervisor(); RemoteService service = remoteServiceService.getVSMRemoteService(machine.getDatacenter()); // Delete not maneged vms is needed before update virtual appliances List<VirtualMachine> vmachines = repo.getNotManagedVirtualMachines(hypervisor); for (VirtualMachine vm : vmachines) { vsm.unsubscribe(service, vm); virtualDatacenterRep.deleteVirtualMachine(vm); } // Update virtual machines and remove imported virtual machines Collection<VirtualMachine> virtualMachines = virtualMachineService.findByHypervisor(hypervisor); if (virtualMachines != null && !virtualMachines.isEmpty()) { for (VirtualMachine vm : virtualMachines) { if (vm.isManaged()) { // even we are going to delete the physical machine we need // to deallocate the networking values. // And for shared datastores it will be usefull as well. allocationService.deallocateVirtualMachine(vm); if (vm.getState() != VirtualMachineState.NOT_ALLOCATED) { if (!force) { addConflictErrors(APIError.RACK_CANNOT_REMOVE_VMS); flushErrors(); } } vm.setState(VirtualMachineState.NOT_ALLOCATED); vm.setDatastore(null); vm.setHypervisor(null); virtualMachineService.updateVirtualMachine(vm); } else if (vm.isImported()) { try { vsm.unsubscribe(service, vm); } catch (APIException e) { logger .error( "Trying to unsubscribe virtual machine {} when it is already unsubscribed.", vm.getName()); } virtualDatacenterRep.deleteVirtualMachine(vm); } } } deleteMachineLoadRulesFromMachine(machine); if (machine.getDatastores() != null && !machine.getDatastores().isEmpty()) { for (Datastore d : machine.getDatastores()) { repo.deleteDatastore(d); } } repo.deleteMachine(machine); // Update VSM state if (virtualMachines != null) { for (VirtualMachine vm : virtualMachines) { // import yet unsubscribed from vsm if (!vm.isImported()) { try { vsm.unsubscribe(service, vm); } catch (APIException e) { logger .error( "Trying to unsubscribe virtual machine {} when it is already unsubscribed.", vm.getName()); } } } } try { vsm.shutdownMonitor(service, hypervisor); } catch (APIException e) { logger.error("Trying to stop monitor of machine {} when it is not monitored.", hypervisor.getIp()); } tracer.log(SeverityType.INFO, ComponentType.MACHINE, EventType.MACHINE_DELETE, "machine.deleted", machine.getName(), machine.getHypervisor().getIp(), machine .getHypervisor().getType(), machine.getState()); } protected void deleteMachineLoadRulesFromMachine(final Machine machine) { // PREMIUM } public boolean isAssignedTo(final Integer datacenterId, final Integer rackId, final Integer machineId) { Machine machine = getMachine(machineId); if (machine == null) { return false; } return machine.getDatacenter().getId().equals(datacenterId) && machine.getRack().getId().equals(rackId); } private void isValidMachine(final Machine machine) { if (!machine.isValid()) { addValidationErrors(machine.getValidationErrors()); } flushErrors(); } // Needed in unit testing public VsmServiceStub getVsm() { return vsm; } public void setVsm(final VsmServiceStub vsm) { this.vsm = vsm; } }