package com.cloud.vm; import java.util.ArrayList; import java.util.List; import javax.ejb.Local; import org.apache.log4j.Logger; import com.cloud.api.commands.AttachVolumeCmd; import com.cloud.api.commands.CreateTemplateCmd; import com.cloud.api.commands.DeployVMCmd; import com.cloud.api.commands.DetachVolumeCmd; import com.cloud.api.commands.UpgradeVMCmd; import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.deploy.DataCenterDeployment; import com.cloud.domain.DomainVO; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventVO; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Network; import com.cloud.network.NetworkVO; import com.cloud.network.Networks.TrafficType; import com.cloud.service.ServiceOfferingVO; import com.cloud.storage.Storage; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.Storage.TemplateType; import com.cloud.user.Account; import com.cloud.user.SSHKeyPair; import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; @Local(value={BareMetalVmManager.class, BareMetalVmService.class}) public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMetalVmManager, BareMetalVmService, Manager { private static final Logger s_logger = Logger.getLogger(BareMetalVmManagerImpl.class); @Override public boolean attachISOToVM(long vmId, long isoId, boolean attach) { s_logger.warn("attachISOToVM is not supported by Bare Metal, just fake a true"); return true; } @Override public Volume attachVolumeToVM(AttachVolumeCmd command) { s_logger.warn("attachVolumeToVM is not supported by Bare Metal, return null"); return null; } @Override public Volume detachVolumeFromVM(DetachVolumeCmd cmd) { s_logger.warn("detachVolumeFromVM is not supported by Bare Metal, return null"); return null; } @Override public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) { s_logger.warn("upgradeVirtualMachine is not supported by Bare Metal, return null"); return null; } @Override public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, ResourceAllocationException { s_logger.warn("createPrivateTemplateRecord is not supported by Bare Metal, return null"); return null; } @Override @DB public VMTemplateVO createPrivateTemplate(CreateTemplateCmd command) throws CloudRuntimeException { s_logger.warn("createPrivateTemplate is not supported by Bare Metal, return null"); return null; } @Override public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { Account caller = UserContext.current().getCaller(); String accountName = cmd.getAccountName(); Long domainId = cmd.getDomainId(); List networkList = cmd.getNetworkIds(); String group = cmd.getGroup(); Account owner = _accountDao.findActiveAccount(accountName, domainId); if (owner == null) { throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); } _accountMgr.checkAccess(caller, owner); long accountId = owner.getId(); DataCenterVO dc = _dcDao.findById(cmd.getZoneId()); if (dc == null) { throw new InvalidParameterValueException("Unable to find zone: " + cmd.getZoneId()); } if (dc.getDomainId() != null) { DomainVO domain = _domainDao.findById(dc.getDomainId()); if (domain == null) { throw new CloudRuntimeException("Unable to find the domain " + dc.getDomainId() + " for the zone: " + dc); } _accountMgr.checkAccess(caller, domain); _accountMgr.checkAccess(owner, domain); } // check if account/domain is with in resource limits to create a new vm if (_accountMgr.resourceLimitExceeded(owner, ResourceType.user_vm)) { ResourceAllocationException rae = new ResourceAllocationException("Maximum number of virtual machines for account: " + owner.getAccountName() + " has been exceeded."); rae.setResourceType("vm"); throw rae; } ServiceOfferingVO offering = _serviceOfferingDao.findById(cmd.getServiceOfferingId()); if (offering == null || offering.getRemoved() != null) { throw new InvalidParameterValueException("Unable to find service offering: " + cmd.getServiceOfferingId()); } VMTemplateVO template = _templateDao.findById(cmd.getTemplateId()); // Make sure a valid template ID was specified if (template == null || template.getRemoved() != null) { throw new InvalidParameterValueException("Unable to use template " + cmd.getTemplateId()); } if (template.getTemplateType().equals(TemplateType.SYSTEM)) { throw new InvalidParameterValueException("Unable to use system template " + cmd.getTemplateId()+" to deploy a user vm"); } if (template.getFormat() != Storage.ImageFormat.BAREMETAL) { throw new InvalidParameterValueException("Unable to use non Bare Metal template" + cmd.getTemplateId() +" to deploy a bare metal vm"); } String userData = cmd.getUserData(); byte [] decodedUserData = null; if (userData != null) { if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) { throw new InvalidParameterValueException("User data is too long"); } decodedUserData = org.apache.commons.codec.binary.Base64.decodeBase64(userData.getBytes()); if (decodedUserData.length > MAX_USER_DATA_LENGTH_BYTES){ throw new InvalidParameterValueException("User data is too long"); } if (decodedUserData.length < 1) { throw new InvalidParameterValueException("User data is too short"); } } // Find an SSH public key corresponding to the key pair name, if one is given String sshPublicKey = null; if (cmd.getSSHKeyPairName() != null && !cmd.getSSHKeyPairName().equals("")) { Account account = UserContext.current().getCaller(); SSHKeyPair pair = _sshKeyPairDao.findByName(account.getAccountId(), account.getDomainId(), cmd.getSSHKeyPairName()); if (pair == null) { throw new InvalidParameterValueException("A key pair with name '" + cmd.getSSHKeyPairName() + "' was not found."); } sshPublicKey = pair.getPublicKey(); } _accountMgr.checkAccess(caller, template); DataCenterDeployment plan = new DataCenterDeployment(dc.getId()); s_logger.debug("Allocating in the DB for bare metal vm"); if (dc.getNetworkType() != NetworkType.Basic || networkList != null) { s_logger.warn("Bare Metal only supports basical network mode now, switch to baisc network automatically"); } Network defaultNetwork = _networkMgr.getSystemNetworkByZoneAndTrafficType(dc.getId(), TrafficType.Guest); if (defaultNetwork == null) { throw new InvalidParameterValueException("Unable to find a default network to start a vm"); } networkList = new ArrayList(); networkList.add(defaultNetwork.getId()); List> networks = new ArrayList>(); short defaultNetworkNumber = 0; for (Long networkId : networkList) { NetworkVO network = _networkDao.findById(networkId); if (network == null) { throw new InvalidParameterValueException("Unable to find network by id " + networkId); } else { if (!network.isShared()) { //Check account permissions List networkMap = _networkDao.listBy(accountId, networkId); if (networkMap == null || networkMap.isEmpty()) { throw new PermissionDeniedException("Unable to create a vm using network with id " + networkId + ", permission denied"); } } if (network.isDefault()) { defaultNetworkNumber++; } networks.add(new Pair(network, null)); } } //at least one network default network has to be set if (defaultNetworkNumber == 0) { throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm"); } else if (defaultNetworkNumber >1) { throw new InvalidParameterValueException("Only 1 default network per vm is supported"); } long id = _vmDao.getNextInSequence(Long.class, "id"); String hostName = cmd.getName(); String instanceName = VirtualMachineName.getVmName(id, owner.getId(), _instance); if (hostName == null) { hostName = instanceName; } else { //verify hostName (hostname doesn't have to be unique) if (!NetUtils.verifyDomainNameLabel(hostName, true)) { throw new InvalidParameterValueException("Invalid name. Vm name can contain ASCII letters 'a' through 'z', the digits '0' through '9', " + "and the hyphen ('-'), must be between 1 and 63 characters long, and can't start or end with \"-\" and can't start with digit"); } } UserVmVO vm = new UserVmVO(id, instanceName, cmd.getDisplayName(), template.getId(), HypervisorType.BareMetal, template.getGuestOSId(), offering.getOfferHA(), domainId, owner.getId(), offering.getId(), userData, hostName); if (sshPublicKey != null) { vm.setDetail("SSH.PublicKey", sshPublicKey); } if (_itMgr.allocate(vm, template, offering, null, null, networks, null, plan, cmd.getHypervisor(), owner) == null) { return null; } _vmDao.saveDetails(vm); if (s_logger.isDebugEnabled()) { s_logger.debug("Successfully allocated DB entry for " + vm); } if (s_logger.isDebugEnabled()) { s_logger.debug("Successfully allocated DB entry for " + vm); } UserContext.current().setEventDetails("Vm Id: " + vm.getId()); UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, dc.getId(), vm.getId(), vm.getName(), offering.getId(), template.getId(), null); _usageEventDao.persist(usageEvent); _accountMgr.incrementResourceCount(accountId, ResourceType.user_vm); // Assign instance to the group try { if (group != null) { boolean addToGroup = addInstanceToGroup(Long.valueOf(id), group); if (!addToGroup) { throw new CloudRuntimeException("Unable to assign Vm to the group " + group); } } } catch (Exception ex) { throw new CloudRuntimeException("Unable to assign Vm to the group " + group); } return vm; } public UserVm startVirtualMachine(DeployVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException { return null; } }