| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with 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. |
| package com.cloud.network.element; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.inject.Inject; |
| |
| import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; |
| import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; |
| import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; |
| import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; |
| import org.apache.cloudstack.storage.configdrive.ConfigDrive; |
| import org.apache.cloudstack.storage.configdrive.ConfigDriveBuilder; |
| import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; |
| import org.apache.cloudstack.storage.to.TemplateObjectTO; |
| import org.apache.commons.collections.CollectionUtils; |
| import org.apache.commons.collections.MapUtils; |
| |
| import com.cloud.agent.AgentManager; |
| import com.cloud.agent.api.HandleConfigDriveIsoAnswer; |
| import com.cloud.agent.api.HandleConfigDriveIsoCommand; |
| import com.cloud.agent.api.to.DiskTO; |
| import com.cloud.configuration.ConfigurationManager; |
| import com.cloud.dc.dao.ClusterDao; |
| import com.cloud.dc.dao.DataCenterDao; |
| import com.cloud.dc.dao.HostPodDao; |
| import com.cloud.deploy.DeployDestination; |
| import com.cloud.exception.ConcurrentOperationException; |
| import com.cloud.exception.InsufficientCapacityException; |
| import com.cloud.exception.ResourceUnavailableException; |
| import com.cloud.exception.UnsupportedServiceException; |
| import com.cloud.host.HostVO; |
| import com.cloud.host.dao.HostDao; |
| import com.cloud.host.Status; |
| import com.cloud.hypervisor.HypervisorGuruManager; |
| import com.cloud.network.Network; |
| import com.cloud.network.Network.Capability; |
| import com.cloud.network.Network.Provider; |
| import com.cloud.network.Network.Service; |
| import com.cloud.network.NetworkMigrationResponder; |
| import com.cloud.network.NetworkModel; |
| import com.cloud.network.Networks.TrafficType; |
| import com.cloud.network.PhysicalNetworkServiceProvider; |
| import com.cloud.offering.NetworkOffering; |
| import com.cloud.service.dao.ServiceOfferingDao; |
| import com.cloud.storage.DataStoreRole; |
| import com.cloud.storage.Storage; |
| import com.cloud.storage.StoragePool; |
| import com.cloud.storage.Volume; |
| import com.cloud.storage.VolumeVO; |
| import com.cloud.storage.dao.GuestOSCategoryDao; |
| import com.cloud.storage.dao.GuestOSDao; |
| import com.cloud.storage.dao.VolumeDao; |
| import com.cloud.utils.Pair; |
| import com.cloud.utils.StringUtils; |
| import com.cloud.utils.component.AdapterBase; |
| import com.cloud.utils.crypt.DBEncryptionUtil; |
| import com.cloud.utils.db.EntityManager; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.utils.fsm.StateListener; |
| import com.cloud.utils.fsm.StateMachine2; |
| import com.cloud.vm.Nic; |
| import com.cloud.vm.NicProfile; |
| import com.cloud.vm.ReservationContext; |
| import com.cloud.vm.UserVmDetailVO; |
| import com.cloud.vm.UserVmVO; |
| import com.cloud.vm.VirtualMachine; |
| import com.cloud.vm.VirtualMachineManager; |
| import com.cloud.vm.VirtualMachineProfile; |
| import com.cloud.vm.VmDetailConstants; |
| import com.cloud.vm.dao.UserVmDao; |
| import com.cloud.vm.dao.UserVmDetailsDao; |
| |
| public class ConfigDriveNetworkElement extends AdapterBase implements NetworkElement, UserDataServiceProvider, |
| StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualMachine>, NetworkMigrationResponder { |
| |
| private static final Map<Service, Map<Capability, String>> capabilities = setCapabilities(); |
| |
| @Inject |
| NetworkModel _networkMgr; |
| @Inject |
| UserVmDao _userVmDao; |
| @Inject |
| UserVmDetailsDao _userVmDetailsDao; |
| @Inject |
| ConfigurationManager _configMgr; |
| @Inject |
| DataCenterDao _dcDao; |
| @Inject |
| ServiceOfferingDao _serviceOfferingDao; |
| @Inject |
| NetworkModel _networkModel; |
| @Inject |
| GuestOSCategoryDao _guestOSCategoryDao; |
| @Inject |
| GuestOSDao _guestOSDao; |
| @Inject |
| VolumeDao _volumeDao; |
| @Inject |
| HostDao _hostDao; |
| @Inject |
| HostPodDao _podDao; |
| @Inject |
| ClusterDao _clusterDao; |
| @Inject |
| AgentManager agentManager; |
| @Inject |
| DataStoreManager _dataStoreMgr; |
| @Inject |
| EndPointSelector _ep; |
| @Inject |
| EntityManager _entityMgr; |
| @Inject |
| ServiceOfferingDao _offeringDao; |
| @Inject |
| ImageStoreDao imgstore; |
| @Inject |
| private HypervisorGuruManager _hvGuruMgr; |
| |
| private final static Integer CONFIGDRIVEDISKSEQ = 4; |
| |
| private boolean canHandle(TrafficType trafficType) { |
| return trafficType.equals(TrafficType.Guest); |
| } |
| |
| @Override |
| public boolean start() { |
| VirtualMachine.State.getStateMachine().registerListener(this); |
| return super.start(); |
| } |
| |
| @Override |
| public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ResourceUnavailableException, ConcurrentOperationException, |
| InsufficientCapacityException { |
| return canHandle(offering.getTrafficType()); |
| } |
| |
| @Override |
| public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vmProfile, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, |
| InsufficientCapacityException, ResourceUnavailableException { |
| return true; |
| } |
| |
| @Override |
| public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) { |
| if (!nic.isDefaultNic()) { |
| return true; |
| } |
| |
| try { |
| return deleteConfigDriveIso(vm.getVirtualMachine()); |
| } catch (ResourceUnavailableException e) { |
| logger.error("Failed to delete config drive due to: ", e); |
| return false; |
| } |
| } |
| |
| @Override |
| public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { |
| return true; // assume that the agent will remove userdata etc |
| } |
| |
| @Override |
| public boolean destroy(Network config, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { |
| return true; // assume that the agent will remove userdata etc |
| } |
| |
| @Override |
| public Provider getProvider() { |
| return Provider.ConfigDrive; |
| } |
| |
| @Override |
| public Map<Service, Map<Capability, String>> getCapabilities() { |
| return capabilities; |
| } |
| |
| private static Map<Service, Map<Capability, String>> setCapabilities() { |
| Map<Service, Map<Capability, String>> capabilities = new HashMap<>(); |
| capabilities.put(Service.UserData, null); |
| return capabilities; |
| } |
| |
| @Override |
| public boolean isReady(PhysicalNetworkServiceProvider provider) { |
| return true; |
| } |
| |
| @Override |
| public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { |
| return true; |
| } |
| |
| @Override |
| public boolean canEnableIndividualServices() { |
| return false; |
| } |
| |
| private String getSshKey(VirtualMachineProfile profile) { |
| final UserVmDetailVO vmDetailSshKey = _userVmDetailsDao.findDetail(profile.getId(), VmDetailConstants.SSH_PUBLIC_KEY); |
| return (vmDetailSshKey!=null ? vmDetailSshKey.getValue() : null); |
| } |
| |
| @Override |
| public boolean addPasswordAndUserdata(Network network, NicProfile nic, VirtualMachineProfile profile, DeployDestination dest, ReservationContext context) |
| throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { |
| return (canHandle(network.getTrafficType()) |
| && configureConfigDriveData(profile, nic, dest)) |
| && createConfigDriveIso(profile, dest, null); |
| } |
| |
| @Override |
| public boolean savePassword(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException { |
| // savePassword is called by resetPasswordForVirtualMachine API which requires VM to be shutdown |
| // Upper layers should save password in db, we do not need to update/create config drive iso at this point |
| // Config drive will be created with updated password when VM starts in future |
| if (vm != null && vm.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) { |
| throw new CloudRuntimeException("VM should to stopped to reset password"); |
| } |
| |
| final boolean canHandle = canHandle(network.getTrafficType()); |
| |
| if (canHandle) { |
| storePasswordInVmDetails(vm); |
| } |
| |
| return canHandle; |
| } |
| |
| @Override |
| public boolean saveSSHKey(final Network network, final NicProfile nic, final VirtualMachineProfile vm, final String sshPublicKey) throws ResourceUnavailableException { |
| // saveSSHKey is called by resetSSHKeyForVirtualMachine API which requires VM to be shutdown |
| // Upper layers should save ssh public key in db, we do not need to update/create config drive iso at this point |
| // Config drive will be created with updated password when VM starts in future |
| if (vm != null && vm.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) { |
| throw new CloudRuntimeException("VM should to stopped to reset password"); |
| } |
| |
| final boolean canHandle = canHandle(network.getTrafficType()); |
| |
| return canHandle; |
| } |
| |
| @Override |
| public boolean saveHypervisorHostname(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { |
| if (vm.getVirtualMachine().getType() == VirtualMachine.Type.User) { |
| try { |
| recreateConfigDriveIso(nic, network, vm, dest); |
| } catch (ResourceUnavailableException e) { |
| logger.error("Failed to add config disk drive due to: ", e); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean saveUserData(final Network network, final NicProfile nic, final VirtualMachineProfile vm) throws ResourceUnavailableException { |
| // saveUserData is called by updateVirtualMachine API which requires VM to be shutdown |
| // Upper layers should save userdata in db, we do not need to update/create config drive iso at this point |
| // Config drive will be created with updated password when VM starts in future |
| if (vm != null && vm.getVirtualMachine().getState().equals(VirtualMachine.State.Running)) { |
| throw new CloudRuntimeException("VM should to stopped to reset password"); |
| } |
| return canHandle(network.getTrafficType()); |
| } |
| |
| /** |
| * Store password in vm details so it can be picked up during VM start. |
| */ |
| private void storePasswordInVmDetails(VirtualMachineProfile vm) { |
| final String password = (String) vm.getParameter(VirtualMachineProfile.Param.VmPassword); |
| final String password_encrypted = DBEncryptionUtil.encrypt(password); |
| final UserVmVO userVmVO = _userVmDao.findById(vm.getId()); |
| |
| _userVmDetailsDao.addDetail(vm.getId(), VmDetailConstants.PASSWORD, password_encrypted, false); |
| |
| userVmVO.setUpdateParameters(true); |
| _userVmDao.update(userVmVO.getId(), userVmVO); |
| } |
| |
| @Override |
| public boolean verifyServicesCombination(Set<Service> services) { |
| return true; |
| } |
| |
| @Override |
| public boolean preStateTransitionEvent(VirtualMachine.State oldState, VirtualMachine.Event event, VirtualMachine.State newState, VirtualMachine vo, boolean status, Object opaque) { |
| return true; |
| } |
| |
| @Override |
| public boolean postStateTransitionEvent(StateMachine2.Transition<VirtualMachine.State, VirtualMachine.Event> transition, VirtualMachine vm, boolean status, Object opaque) { |
| if (transition.getToState().equals(VirtualMachine.State.Expunging) && transition.getEvent().equals(VirtualMachine.Event.ExpungeOperation)) { |
| Nic nic = _networkModel.getDefaultNic(vm.getId()); |
| if (nic == null) { |
| return true; |
| } |
| try { |
| final Network network = _networkMgr.getNetwork(nic.getNetworkId()); |
| final UserDataServiceProvider userDataUpdateProvider = _networkModel.getUserDataUpdateProvider(network); |
| final Provider provider = userDataUpdateProvider.getProvider(); |
| if (provider.equals(Provider.ConfigDrive)) { |
| try { |
| return deleteConfigDriveIso(vm); |
| } catch (ResourceUnavailableException e) { |
| logger.error("Failed to delete config drive due to: ", e); |
| return false; |
| } |
| } |
| } catch (UnsupportedServiceException usse) {} |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean prepareMigration(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) { |
| if (_networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.ConfigDrive)) { |
| logger.trace(String.format("[prepareMigration] for vm: %s", vm.getInstanceName())); |
| try { |
| if (isConfigDriveIsoOnHostCache(vm.getId())) { |
| vm.setConfigDriveLocation(Location.HOST); |
| configureConfigDriveData(vm, nic, dest); |
| |
| // Create the config drive on dest host cache |
| createConfigDriveIsoOnHostCache(vm, dest.getHost().getId()); |
| } else { |
| vm.setConfigDriveLocation(getConfigDriveLocation(vm.getId())); |
| addPasswordAndUserdata(network, nic, vm, dest, context); |
| } |
| } catch (InsufficientCapacityException | ResourceUnavailableException e) { |
| logger.error("Failed to add config disk drive due to: ", e); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Override |
| public void rollbackMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) { |
| try { |
| if (isConfigDriveIsoOnHostCache(vm.getId())) { |
| vm.setConfigDriveLocation(Location.HOST); |
| // Delete the config drive on dest host cache |
| deleteConfigDriveIsoOnHostCache(vm.getVirtualMachine(), vm.getHostId()); |
| } |
| } catch (ConcurrentOperationException | ResourceUnavailableException e) { |
| logger.error("rollbackMigration failed.", e); |
| } |
| } |
| |
| @Override |
| public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) { |
| try { |
| if (isConfigDriveIsoOnHostCache(vm.getId())) { |
| vm.setConfigDriveLocation(Location.HOST); |
| // Delete the config drive on src host cache |
| deleteConfigDriveIsoOnHostCache(vm.getVirtualMachine(), vm.getHostId()); |
| } |
| } catch (ConcurrentOperationException | ResourceUnavailableException e) { |
| logger.error("commitMigration failed.", e); |
| } |
| } |
| |
| private void recreateConfigDriveIso(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest) throws ResourceUnavailableException { |
| if (nic.isDefaultNic() && _networkModel.getUserDataUpdateProvider(network).getProvider().equals(Provider.ConfigDrive)) { |
| DiskTO diskToUse = null; |
| for (DiskTO disk : vm.getDisks()) { |
| if (disk.getType() == Volume.Type.ISO && disk.getPath() != null && disk.getPath().contains("configdrive")) { |
| diskToUse = disk; |
| break; |
| } |
| } |
| final UserVmVO userVm = _userVmDao.findById(vm.getId()); |
| |
| if (userVm != null) { |
| final boolean isWindows = isWindows(userVm.getGuestOSId()); |
| List<String[]> vmData = _networkModel.generateVmData(userVm.getUserData(), userVm.getUserDataDetails(), _serviceOfferingDao.findById(userVm.getServiceOfferingId()).getName(), userVm.getDataCenterId(), userVm.getInstanceName(), vm.getHostName(), vm.getId(), |
| vm.getUuid(), nic.getMacAddress(), userVm.getDetail("SSH.PublicKey"), (String) vm.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows, VirtualMachineManager.getHypervisorHostname(dest.getHost() != null ? dest.getHost().getName() : "")); |
| vm.setVmData(vmData); |
| vm.setConfigDriveLabel(VirtualMachineManager.VmConfigDriveLabel.value()); |
| createConfigDriveIso(vm, dest, diskToUse); |
| } |
| } |
| } |
| |
| private boolean isWindows(long guestOSId) { |
| return _guestOSCategoryDao.findById(_guestOSDao.findById(guestOSId).getCategoryId()).getName().equalsIgnoreCase("Windows"); |
| } |
| |
| private DataStore findDataStore(VirtualMachineProfile profile, DeployDestination dest) { |
| DataStore dataStore = null; |
| if (VirtualMachineManager.VmConfigDriveOnPrimaryPool.valueIn(dest.getDataCenter().getId()) || |
| VirtualMachineManager.VmConfigDriveForceHostCacheUse.valueIn(dest.getDataCenter().getId())) { |
| if(MapUtils.isNotEmpty(dest.getStorageForDisks())) { |
| dataStore = getPlannedDataStore(dest, dataStore); |
| } |
| if (dataStore == null) { |
| dataStore = pickExistingRootVolumeFromDataStore(profile, dataStore); |
| } |
| } else { |
| dataStore = _dataStoreMgr.getImageStoreWithFreeCapacity(dest.getDataCenter().getId()); |
| } |
| return dataStore; |
| } |
| |
| private DataStore getPlannedDataStore(DeployDestination dest, DataStore dataStore) { |
| for (final Volume volume : dest.getStorageForDisks().keySet()) { |
| if (volume.getVolumeType() == Volume.Type.ROOT) { |
| final StoragePool primaryPool = dest.getStorageForDisks().get(volume); |
| dataStore = _dataStoreMgr.getDataStore(primaryPool.getId(), DataStoreRole.Primary); |
| break; |
| } |
| } |
| return dataStore; |
| } |
| |
| private DataStore pickExistingRootVolumeFromDataStore(VirtualMachineProfile profile, DataStore dataStore) { |
| final List<VolumeVO> volumes = _volumeDao.findByInstanceAndType(profile.getVirtualMachine().getId(), Volume.Type.ROOT); |
| if (CollectionUtils.isNotEmpty(volumes)) { |
| dataStore = pickDataStoreFromVolumes(volumes); |
| } |
| return dataStore; |
| } |
| |
| private DataStore pickDataStoreFromVolumes(List<VolumeVO> volumes) { |
| DataStore dataStore = null; |
| for (Volume vol : volumes) { |
| if (doesVolumeStateCheckout(vol)) { |
| dataStore = _dataStoreMgr.getDataStore(vol.getPoolId(), DataStoreRole.Primary); |
| if (dataStore != null) { |
| return dataStore; |
| } |
| } |
| } |
| return dataStore; |
| } |
| |
| private boolean doesVolumeStateCheckout(Volume vol) { |
| switch (vol.getState()) { |
| case Allocated: |
| case Creating: |
| case Ready: |
| case Snapshotting: |
| case RevertSnapshotting: |
| case Resizing: |
| case Copying: |
| case Attaching: |
| return true; |
| case Migrating: |
| case Expunging: |
| case Expunged: |
| case Destroy: |
| case Destroying: |
| case UploadOp: |
| case Uploaded: |
| case NotUploaded: |
| case UploadInProgress: |
| case UploadError: |
| case UploadAbandoned: |
| return false; |
| default: |
| throw new IllegalArgumentException("volume has a state that does not compute: " +vol.getState()); |
| } |
| } |
| |
| private Long findAgentIdForImageStore(final DataStore dataStore) throws ResourceUnavailableException { |
| EndPoint endpoint = _ep.select(dataStore); |
| if (endpoint == null) { |
| throw new ResourceUnavailableException("Config drive creation failed, secondary store not available", |
| dataStore.getClass(), dataStore.getId()); |
| } |
| return endpoint.getId(); |
| } |
| |
| private Long findAgentId(VirtualMachineProfile profile, DeployDestination dest, DataStore dataStore) throws ResourceUnavailableException { |
| Long agentId; |
| if (dest.getHost() == null) { |
| agentId = (profile.getVirtualMachine().getHostId() == null ? profile.getVirtualMachine().getLastHostId() : profile.getVirtualMachine().getHostId()); |
| } else { |
| agentId = dest.getHost().getId(); |
| } |
| if (!VirtualMachineManager.VmConfigDriveOnPrimaryPool.valueIn(dest.getDataCenter().getId()) && |
| !VirtualMachineManager.VmConfigDriveForceHostCacheUse.valueIn(dest.getDataCenter().getId()) && dataStore != null) { |
| agentId = findAgentIdForImageStore(dataStore); |
| } |
| return agentId; |
| } |
| |
| private Location getConfigDriveLocation(long vmId) { |
| final UserVmDetailVO vmDetailConfigDriveLocation = _userVmDetailsDao.findDetail(vmId, VmDetailConstants.CONFIG_DRIVE_LOCATION); |
| if (vmDetailConfigDriveLocation != null) { |
| if (Location.HOST.toString().equalsIgnoreCase(vmDetailConfigDriveLocation.getValue())) { |
| return Location.HOST; |
| } else if (Location.PRIMARY.toString().equalsIgnoreCase(vmDetailConfigDriveLocation.getValue())) { |
| return Location.PRIMARY; |
| } else { |
| return Location.SECONDARY; |
| } |
| } |
| return Location.SECONDARY; |
| } |
| |
| private boolean isConfigDriveIsoOnHostCache(long vmId) { |
| final UserVmDetailVO vmDetailConfigDriveLocation = _userVmDetailsDao.findDetail(vmId, VmDetailConstants.CONFIG_DRIVE_LOCATION); |
| if (vmDetailConfigDriveLocation != null && Location.HOST.toString().equalsIgnoreCase(vmDetailConfigDriveLocation.getValue())) { |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean createConfigDriveIsoOnHostCache(VirtualMachineProfile profile, Long hostId) throws ResourceUnavailableException { |
| if (hostId == null) { |
| throw new ResourceUnavailableException("Config drive iso creation failed, dest host not available", |
| ConfigDriveNetworkElement.class, 0L); |
| } |
| |
| logger.debug("Creating config drive ISO for vm: " + profile.getInstanceName() + " on host: " + hostId); |
| |
| Map<String, String> customUserdataParamMap = getVMCustomUserdataParamMap(profile.getId()); |
| |
| final String isoFileName = ConfigDrive.configIsoFileName(profile.getInstanceName()); |
| final String isoPath = ConfigDrive.createConfigDrivePath(profile.getInstanceName()); |
| final String isoData = ConfigDriveBuilder.buildConfigDrive(profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), customUserdataParamMap); |
| final HandleConfigDriveIsoCommand configDriveIsoCommand = new HandleConfigDriveIsoCommand(isoPath, isoData, null, false, true, true); |
| |
| final HandleConfigDriveIsoAnswer answer = (HandleConfigDriveIsoAnswer) agentManager.easySend(hostId, configDriveIsoCommand); |
| if (answer == null) { |
| throw new CloudRuntimeException("Unable to get an answer to handle config drive creation for vm: " + profile.getInstanceName() + " on host: " + hostId); |
| } |
| |
| if (!answer.getResult()) { |
| throw new ResourceUnavailableException(String.format("Config drive iso creation failed, details: %s", |
| answer.getDetails()), ConfigDriveNetworkElement.class, 0L); |
| } |
| |
| profile.setConfigDriveLocation(answer.getConfigDriveLocation()); |
| _userVmDetailsDao.addDetail(profile.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION, answer.getConfigDriveLocation().toString(), false); |
| addConfigDriveDisk(profile, null); |
| return true; |
| } |
| |
| private boolean deleteConfigDriveIsoOnHostCache(final VirtualMachine vm, final Long hostId) throws ResourceUnavailableException { |
| if (hostId == null) { |
| throw new ResourceUnavailableException("Config drive iso deletion failed, host not available", |
| ConfigDriveNetworkElement.class, 0L); |
| } |
| |
| logger.debug("Deleting config drive ISO for vm: " + vm.getInstanceName() + " on host: " + hostId); |
| final String isoPath = ConfigDrive.createConfigDrivePath(vm.getInstanceName()); |
| final HandleConfigDriveIsoCommand configDriveIsoCommand = new HandleConfigDriveIsoCommand(isoPath, null, null, false, true, false); |
| HostVO hostVO = _hostDao.findById(hostId); |
| if (hostVO == null) { |
| logger.warn(String.format("Host %s appears to be unavailable, skipping deletion of config-drive ISO on host cache", hostId)); |
| return false; |
| } |
| if (!Arrays.asList(Status.Up, Status.Connecting).contains(hostVO.getStatus())) { |
| logger.warn(String.format("Host status %s is not Up or Connecting, skipping deletion of config-drive ISO on host cache", hostId)); |
| return false; |
| } |
| |
| final HandleConfigDriveIsoAnswer answer = (HandleConfigDriveIsoAnswer) agentManager.easySend(hostId, configDriveIsoCommand); |
| if (answer == null) { |
| throw new CloudRuntimeException("Unable to get an answer to handle config drive deletion for vm: " + vm.getInstanceName() + " on host: " + hostId); |
| } |
| |
| if (!answer.getResult()) { |
| logger.error("Failed to remove config drive for instance: " + vm.getInstanceName()); |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean createConfigDriveIso(VirtualMachineProfile profile, DeployDestination dest, DiskTO disk) throws ResourceUnavailableException { |
| DataStore dataStore = getDatastoreForConfigDriveIso(disk, profile, dest); |
| |
| final Long agentId = findAgentId(profile, dest, dataStore); |
| if (agentId == null || dataStore == null) { |
| throw new ResourceUnavailableException("Config drive iso creation failed, agent or datastore not available", |
| ConfigDriveNetworkElement.class, 0L); |
| } |
| |
| logger.debug("Creating config drive ISO for vm: " + profile.getInstanceName()); |
| |
| Map<String, String> customUserdataParamMap = getVMCustomUserdataParamMap(profile.getId()); |
| |
| final String isoFileName = ConfigDrive.configIsoFileName(profile.getInstanceName()); |
| final String isoPath = ConfigDrive.createConfigDrivePath(profile.getInstanceName()); |
| final String isoData = ConfigDriveBuilder.buildConfigDrive(profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), customUserdataParamMap); |
| boolean useHostCacheOnUnsupportedPool = VirtualMachineManager.VmConfigDriveUseHostCacheOnUnsupportedPool.valueIn(dest.getDataCenter().getId()); |
| boolean preferHostCache = VirtualMachineManager.VmConfigDriveForceHostCacheUse.valueIn(dest.getDataCenter().getId()); |
| final HandleConfigDriveIsoCommand configDriveIsoCommand = new HandleConfigDriveIsoCommand(isoPath, isoData, dataStore.getTO(), useHostCacheOnUnsupportedPool, preferHostCache, true); |
| |
| final HandleConfigDriveIsoAnswer answer = (HandleConfigDriveIsoAnswer) agentManager.easySend(agentId, configDriveIsoCommand); |
| if (!answer.getResult()) { |
| throw new ResourceUnavailableException(String.format("Config drive iso creation failed, details: %s", |
| answer.getDetails()), ConfigDriveNetworkElement.class, 0L); |
| } |
| profile.setConfigDriveLocation(answer.getConfigDriveLocation()); |
| _userVmDetailsDao.addDetail(profile.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION, answer.getConfigDriveLocation().toString(), false); |
| addConfigDriveDisk(profile, dataStore); |
| return true; |
| } |
| |
| private Map<String, String> getVMCustomUserdataParamMap(long vmId) { |
| UserVmVO userVm = _userVmDao.findById(vmId); |
| String userDataDetails = userVm.getUserDataDetails(); |
| Map<String,String> customUserdataParamMap = new HashMap<>(); |
| if(userDataDetails != null && !userDataDetails.isEmpty()) { |
| userDataDetails = userDataDetails.substring(1, userDataDetails.length()-1); |
| String[] keyValuePairs = userDataDetails.split(","); |
| for(String pair : keyValuePairs) |
| { |
| final Pair<String, String> keyValue = StringUtils.getKeyValuePairWithSeparator(pair, "="); |
| customUserdataParamMap.put(keyValue.first(), keyValue.second()); |
| } |
| } |
| |
| return customUserdataParamMap; |
| } |
| |
| private DataStore getDatastoreForConfigDriveIso(DiskTO disk, VirtualMachineProfile profile, DeployDestination dest) { |
| DataStore dataStore = null; |
| if (disk != null) { |
| String dId = disk.getData().getDataStore().getUuid(); |
| if (VirtualMachineManager.VmConfigDriveOnPrimaryPool.value()) { |
| dataStore = _dataStoreMgr.getDataStore(dId, DataStoreRole.Primary); |
| } else { |
| List<DataStore> dataStores = _dataStoreMgr.listImageStores(); |
| String url = disk.getData().getDataStore().getUrl(); |
| for(DataStore ds : dataStores) { |
| if (url.equals(ds.getUri()) && DataStoreRole.Image.equals(ds.getRole())) { |
| dataStore = ds; |
| break; |
| } |
| } |
| } |
| } else { |
| dataStore = findDataStore(profile, dest); |
| } |
| return dataStore; |
| } |
| |
| private boolean deleteConfigDriveIso(final VirtualMachine vm) throws ResourceUnavailableException { |
| Long hostId = (vm.getHostId() != null) ? vm.getHostId() : vm.getLastHostId(); |
| Location location = getConfigDriveLocation(vm.getId()); |
| if (hostId == null) { |
| logger.info(String.format("The VM was never booted; no config-drive ISO created for VM %s", vm.getName())); |
| return true; |
| } |
| if (location == Location.HOST) { |
| return deleteConfigDriveIsoOnHostCache(vm, hostId); |
| } |
| |
| Long agentId = null; |
| DataStore dataStore = null; |
| |
| if (location == Location.SECONDARY) { |
| dataStore = _dataStoreMgr.getImageStoreWithFreeCapacity(vm.getDataCenterId()); |
| if (dataStore != null) { |
| agentId = findAgentIdForImageStore(dataStore); |
| } |
| } else if (location == Location.PRIMARY) { |
| List<VolumeVO> volumes = _volumeDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT); |
| if (volumes != null && volumes.size() > 0) { |
| dataStore = _dataStoreMgr.getDataStore(volumes.get(0).getPoolId(), DataStoreRole.Primary); |
| } |
| agentId = hostId; |
| } |
| |
| if (agentId == null || dataStore == null) { |
| throw new ResourceUnavailableException("Config drive iso deletion failed, agent or datastore not available", |
| ConfigDriveNetworkElement.class, 0L); |
| } |
| |
| logger.debug("Deleting config drive ISO for vm: " + vm.getInstanceName()); |
| |
| final String isoPath = ConfigDrive.createConfigDrivePath(vm.getInstanceName()); |
| final HandleConfigDriveIsoCommand configDriveIsoCommand = new HandleConfigDriveIsoCommand(isoPath, null, dataStore.getTO(), false, false, false); |
| |
| final HandleConfigDriveIsoAnswer answer = (HandleConfigDriveIsoAnswer) agentManager.easySend(agentId, configDriveIsoCommand); |
| if (!answer.getResult()) { |
| logger.error("Failed to remove config drive for instance: " + vm.getInstanceName()); |
| return false; |
| } |
| return true; |
| } |
| |
| private void addConfigDriveDisk(final VirtualMachineProfile profile, final DataStore dataStore) throws ResourceUnavailableException { |
| boolean isoAvailable = false; |
| final String isoPath = ConfigDrive.createConfigDrivePath(profile.getInstanceName()); |
| for (DiskTO dataTo : profile.getDisks()) { |
| if (dataTo.getPath().equals(isoPath)) { |
| isoAvailable = true; |
| break; |
| } |
| } |
| if (!isoAvailable) { |
| TemplateObjectTO dataTO = new TemplateObjectTO(); |
| if (dataStore == null && !isConfigDriveIsoOnHostCache(profile.getId())) { |
| throw new ResourceUnavailableException("Config drive disk add failed, datastore not available", |
| ConfigDriveNetworkElement.class, 0L); |
| } else if (dataStore != null) { |
| dataTO.setDataStore(dataStore.getTO()); |
| } |
| |
| dataTO.setUuid(profile.getUuid()); |
| dataTO.setPath(isoPath); |
| dataTO.setFormat(Storage.ImageFormat.ISO); |
| |
| profile.addDisk(new DiskTO(dataTO, CONFIGDRIVEDISKSEQ.longValue(), isoPath, Volume.Type.ISO)); |
| } else { |
| logger.warn("Config drive iso already is in VM profile."); |
| } |
| } |
| |
| private boolean configureConfigDriveData(final VirtualMachineProfile profile, final NicProfile nic, final DeployDestination dest) { |
| final UserVmVO vm = _userVmDao.findById(profile.getId()); |
| if (vm.getType() != VirtualMachine.Type.User) { |
| return false; |
| } |
| final Nic defaultNic = _networkModel.getDefaultNic(vm.getId()); |
| if (defaultNic != null) { |
| final String sshPublicKey = getSshKey(profile); |
| final String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()).getDisplayText(); |
| boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); |
| String hostname = _hostDao.findById(vm.getHostId()).getName(); |
| String destHostname = null; |
| if (dest.getHost() == null ) { |
| destHostname = VirtualMachineManager.getHypervisorHostname(hostname); |
| } else { |
| destHostname = VirtualMachineManager.getHypervisorHostname(dest.getHost().getName()); |
| } |
| final List<String[]> vmData = _networkModel.generateVmData(vm.getUserData(), vm.getUserDataDetails(), serviceOffering, vm.getDataCenterId(), vm.getInstanceName(), vm.getHostName(), vm.getId(), |
| vm.getUuid(), nic.getIPv4Address(), sshPublicKey, (String) profile.getParameter(VirtualMachineProfile.Param.VmPassword), isWindows, destHostname); |
| profile.setVmData(vmData); |
| profile.setConfigDriveLabel(VirtualMachineManager.VmConfigDriveLabel.value()); |
| } |
| return true; |
| } |
| |
| } |