// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by the License. // You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.baremetal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Executors; import javax.ejb.Local; import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; import com.cloud.agent.api.StopAnswer; import com.cloud.agent.api.baremetal.IpmISetBootDevCommand; import com.cloud.agent.api.baremetal.IpmiBootorResetCommand; import com.cloud.agent.manager.Commands; 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.StartVMCmd; import com.cloud.api.commands.UpgradeVMCmd; import com.cloud.baremetal.PxeServerManager.PxeServerType; import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeployDestination; 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.host.Host; import com.cloud.host.HostVO; 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.org.Grouping; import com.cloud.resource.ResourceManager; import com.cloud.service.ServiceOfferingVO; import com.cloud.storage.Storage; import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.template.TemplateAdapter; import com.cloud.template.TemplateAdapter.TemplateAdapterType; import com.cloud.template.TemplateProfile; import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.SSHKeyPair; import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.component.Adapters; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.component.Inject; import com.cloud.utils.component.Manager; import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.StateListener; import com.cloud.utils.net.NetUtils; import com.cloud.vm.BareMetalVmService; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; import com.cloud.vm.ReservationContext; import com.cloud.vm.UserVmManagerImpl; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Event; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineName; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile.Param; @Local(value={BareMetalVmManager.class, BareMetalVmService.class}) public class BareMetalVmManagerImpl extends UserVmManagerImpl implements BareMetalVmManager, BareMetalVmService, Manager, StateListener<State, VirtualMachine.Event, VirtualMachine> { private static final Logger s_logger = Logger.getLogger(BareMetalVmManagerImpl.class); private ConfigurationDao _configDao; @Inject PxeServerManager _pxeMgr; @Inject ResourceManager _resourceMgr; @Inject (adapter=TemplateAdapter.class) protected Adapters<TemplateAdapter> _adapters; @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, Account templateOwner) throws ResourceAllocationException { /*Baremetal creates record after host rebooting for imaging, in createPrivateTemplate*/ return null; } @Override @DB public VMTemplateVO createPrivateTemplate(CreateTemplateCmd cmd) throws CloudRuntimeException { Long vmId = cmd.getVmId(); if (vmId == null) { throw new InvalidParameterValueException("VM ID is null"); } UserVmVO vm = _vmDao.findById(vmId); if (vm == null) { throw new InvalidParameterValueException("Cannot find VM for ID " + vmId); } Long hostId = (vm.getHostId() == null ? vm.getLastHostId() : vm.getHostId()); HostVO host = _hostDao.findById(hostId); if (host == null) { throw new InvalidParameterValueException("Cannot find host with id " + hostId); } List<HostVO> pxes = _resourceMgr.listAllUpAndEnabledHosts(Host.Type.PxeServer, null, host.getPodId(), host.getDataCenterId()); if (pxes.size() == 0) { throw new CloudRuntimeException("Please add PXE server in Pod before taking image"); } if (pxes.size() > 1) { CloudRuntimeException ex = new CloudRuntimeException("Multiple PXE servers found in Pod " + host.getPodId() + " in Zone with specified id"); ex.addProxyObject("data_center", host.getDataCenterId(), "zoneId"); throw ex; } HostVO pxe = pxes.get(0); /* * prepare() will check if current account has right for creating * template */ TemplateAdapter adapter = _adapters.get(TemplateAdapterType.BareMetal.getName()); Long userId = UserContext.current().getCallerUserId(); userId = (userId == null ? User.UID_SYSTEM : userId); AccountVO account = _accountDao.findById(vm.getAccountId()); try { TemplateProfile tmplProfile; tmplProfile = adapter.prepare(false, userId, cmd.getTemplateName(), cmd.getDisplayText(), cmd.getBits(), false, false, cmd.getUrl(), cmd.isPublic(), cmd.isFeatured(), false, "BareMetal", cmd.getOsTypeId(), pxe.getDataCenterId(), HypervisorType.BareMetal, account.getAccountName(), account.getDomainId(), "0", true, cmd.getDetails()); if (!_pxeMgr.prepareCreateTemplate(_pxeMgr.getPxeServerType(pxe), pxe.getId(), vm, cmd.getUrl())) { throw new Exception("Prepare PXE boot file for host " + hostId + " failed"); } IpmISetBootDevCommand setBootDev = new IpmISetBootDevCommand(IpmISetBootDevCommand.BootDev.pxe); Answer ans = _agentMgr.send(hostId, setBootDev); if (!ans.getResult()) { throw new Exception("Set host " + hostId + " to PXE boot failed"); } IpmiBootorResetCommand boot = new IpmiBootorResetCommand(); ans = _agentMgr.send(hostId, boot); if (!ans.getResult()) { throw new Exception("Boot/Reboot host " + hostId + " failed"); } VMTemplateVO tmpl = adapter.create(tmplProfile); s_logger.debug("Create baremetal template for host " + hostId + " successfully, template id:" + tmpl.getId()); return tmpl; } catch (Exception e) { s_logger.debug("Create baremetal tempalte for host " + hostId + " failed", e); throw new CloudRuntimeException(e.getMessage()); } } @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<Long> 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, null, true, owner); long accountId = owner.getId(); DataCenterVO dc = _dcDao.findById(cmd.getZoneId()); if (dc == null) { throw new InvalidParameterValueException("Unable to find zone: " + cmd.getZoneId()); } if(Grouping.AllocationState.Disabled == dc.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())){ throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: "+ 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); } _configMgr.checkZoneAccess(caller, dc); _configMgr.checkZoneAccess(owner, dc); } // check if account/domain is with in resource limits to create a new vm _resourceLimitMgr.checkResourceLimit(owner, ResourceType.user_vm); 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, null, true, 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.getExclusiveGuestNetwork(dc.getId()); if (defaultNetwork == null) { throw new InvalidParameterValueException("Unable to find a default network to start a vm"); } networkList = new ArrayList<Long>(); networkList.add(defaultNetwork.getId()); List<Pair<NetworkVO, NicProfile>> networks = new ArrayList<Pair<NetworkVO, NicProfile>>(); 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.getGuestType() != Network.GuestType.Shared) { //Check account permissions List<NetworkVO> 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"); } } networks.add(new Pair<NetworkVO, NicProfile>(network, null)); } } 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(), false, 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; } 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, cmd.getZoneId(), vm.getId(), vm.getHostName(), offering.getId(), template.getId(), HypervisorType.BareMetal.toString()); _usageEventDao.persist(usageEvent); _resourceLimitMgr.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 { UserVmVO vm = _vmDao.findById(cmd.getInstanceId()); List<HostVO> servers = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByType(Host.Type.PxeServer, vm.getDataCenterIdToDeployIn()); if (servers.size() == 0) { throw new CloudRuntimeException("Cannot find PXE server, please make sure there is one PXE server per zone"); } HostVO pxeServer = servers.get(0); VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); if (template == null || template.getFormat() != Storage.ImageFormat.BAREMETAL) { throw new InvalidParameterValueException("Invalid template with id = " + vm.getTemplateId()); } Map<VirtualMachineProfile.Param, Object> params = new HashMap<VirtualMachineProfile.Param, Object>(); params.put(Param.PxeSeverType, _pxeMgr.getPxeServerType(pxeServer)); return startVirtualMachine(cmd, params); } public UserVm startVirtualMachine(StartVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException, ConcurrentOperationException { UserVmVO vm = _vmDao.findById(cmd.getInstanceId()); VMTemplateVO template = _templateDao.findById(vm.getTemplateId()); if (template == null || template.getFormat() != Storage.ImageFormat.BAREMETAL) { throw new InvalidParameterValueException("Invalid template with id = " + vm.getTemplateId()); } Map<VirtualMachineProfile.Param, Object> params = null; if (vm.isUpdateParameters()) { List<HostVO> servers = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByType(Host.Type.PxeServer, vm.getDataCenterIdToDeployIn()); if (servers.size() == 0) { throw new CloudRuntimeException("Cannot find PXE server, please make sure there is one PXE server per zone"); } HostVO pxeServer = servers.get(0); params = new HashMap<VirtualMachineProfile.Param, Object>(); params.put(Param.PxeSeverType, _pxeMgr.getPxeServerType(pxeServer)); } Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmDetailsPair = super.startVirtualMachine(vm.getId(), cmd.getHostId(), params); return vmDetailsPair.first(); } @Override public boolean configure(String name, Map<String, Object> params) throws ConfigurationException { _name = name; ComponentLocator locator = ComponentLocator.getCurrentLocator(); _configDao = locator.getDao(ConfigurationDao.class); if (_configDao == null) { throw new ConfigurationException("Unable to get the configuration dao."); } Map<String, String> configs = _configDao.getConfiguration("AgentManager", params); _instance = configs.get("instance.name"); if (_instance == null) { _instance = "DEFAULT"; } String workers = configs.get("expunge.workers"); int wrks = NumbersUtil.parseInt(workers, 10); String time = configs.get("expunge.interval"); _expungeInterval = NumbersUtil.parseInt(time, 86400); time = configs.get("expunge.delay"); _expungeDelay = NumbersUtil.parseInt(time, _expungeInterval); _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("UserVm-Scavenger")); _itMgr.registerGuru(Type.UserBareMetal, this); VirtualMachine.State.getStateMachine().registerListener(this); s_logger.info("User VM Manager is configured."); return true; } @Override public boolean finalizeVirtualMachineProfile(VirtualMachineProfile<UserVmVO> profile, DeployDestination dest, ReservationContext context) { UserVmVO vm = profile.getVirtualMachine(); Account owner = _accountDao.findById(vm.getAccountId()); if (owner == null || owner.getState() == Account.State.disabled) { throw new PermissionDeniedException("The owner of " + vm + " either does not exist or is disabled: " + vm.getAccountId()); } PxeServerType pxeType = (PxeServerType) profile.getParameter(Param.PxeSeverType); if (pxeType == null) { s_logger.debug("This is a normal IPMI start, skip prepartion of PXE server"); return true; } s_logger.debug("This is a PXE start, prepare PXE server first"); List<HostVO> servers = _resourceMgr.listAllUpAndEnabledHosts(Host.Type.PxeServer, null, dest.getPod().getId(), dest.getDataCenter().getId()); if (servers.size() == 0) { throw new CloudRuntimeException("Cannot find PXE server, please make sure there is one PXE server per zone"); } if (servers.size() > 1) { throw new CloudRuntimeException("Find more than one PXE server, please make sure there is only one PXE server per zone in pod " + dest.getPod().getId() + " zone " + dest.getDataCenter().getId()); } HostVO pxeServer = servers.get(0); if (!_pxeMgr.prepare(pxeType, profile, dest, context, pxeServer.getId())) { throw new CloudRuntimeException("Pepare PXE server failed"); } profile.addBootArgs("PxeBoot"); return true; } @Override public boolean finalizeDeployment(Commands cmds, VirtualMachineProfile<UserVmVO> profile, DeployDestination dest, ReservationContext context) { UserVmVO userVm = profile.getVirtualMachine(); List<NicVO> nics = _nicDao.listByVmId(userVm.getId()); for (NicVO nic : nics) { NetworkVO network = _networkDao.findById(nic.getNetworkId()); if (network.getTrafficType() == TrafficType.Guest) { userVm.setPrivateIpAddress(nic.getIp4Address()); userVm.setPrivateMacAddress(nic.getMacAddress()); } } _vmDao.update(userVm.getId(), userVm); return true; } @Override public void finalizeStop(VirtualMachineProfile<UserVmVO> profile, StopAnswer answer) { super.finalizeStop(profile, answer); } @Override public UserVm destroyVm(long vmId) throws ResourceUnavailableException, ConcurrentOperationException { return super.destroyVm(vmId); } @Override public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) { return true; } @Override public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, boolean status, Object opaque) { if (newState != State.Starting && newState != State.Error && newState != State.Expunging) { return true; } if (vo.getHypervisorType() != HypervisorType.BareMetal) { return true; } HostVO host = _hostDao.findById(vo.getHostId()); if (host == null) { s_logger.debug("Skip oldState " + oldState + " to " + "newState " + newState + " transimtion"); return true; } _hostDao.loadDetails(host); if (newState == State.Starting) { host.setDetail("vmName", vo.getInstanceName()); s_logger.debug("Add vmName " + host.getDetail("vmName") + " to host " + host.getId() + " details"); } else { if (host.getDetail("vmName") != null && host.getDetail("vmName").equalsIgnoreCase(vo.getInstanceName())) { s_logger.debug("Remove vmName " + host.getDetail("vmName") + " from host " + host.getId() + " details"); host.getDetails().remove("vmName"); } } _hostDao.saveDetails(host); return true; } }