blob: e897ca5e5edc45d71979ad3229ac85028c38f5b1 [file] [log] [blame]
// 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.hypervisor.ovm3.resources;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.UUID;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.storage.command.AttachCommand;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.CreateObjectCommand;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import org.apache.log4j.Logger;
import com.cloud.agent.IAgentControl;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.AttachIsoCommand;
import com.cloud.agent.api.CheckHealthCommand;
import com.cloud.agent.api.CheckNetworkCommand;
import com.cloud.agent.api.CheckOnHostCommand;
import com.cloud.agent.api.CheckVirtualMachineCommand;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.FenceCommand;
import com.cloud.agent.api.GetHostStatsCommand;
import com.cloud.agent.api.GetStorageStatsCommand;
import com.cloud.agent.api.GetVmStatsCommand;
import com.cloud.agent.api.GetVncPortCommand;
import com.cloud.agent.api.MaintainCommand;
import com.cloud.agent.api.MigrateCommand;
import com.cloud.agent.api.ModifyStoragePoolCommand;
import com.cloud.agent.api.NetworkRulesSystemVmCommand;
import com.cloud.agent.api.NetworkUsageCommand;
import com.cloud.agent.api.PingCommand;
import com.cloud.agent.api.PingRoutingCommand;
import com.cloud.agent.api.PingTestCommand;
import com.cloud.agent.api.PlugNicCommand;
import com.cloud.agent.api.PrepareForMigrationCommand;
import com.cloud.agent.api.ReadyCommand;
import com.cloud.agent.api.RebootAnswer;
import com.cloud.agent.api.RebootCommand;
import com.cloud.agent.api.StartAnswer;
import com.cloud.agent.api.StartCommand;
import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.StartupRoutingCommand;
import com.cloud.agent.api.StartupStorageCommand;
import com.cloud.agent.api.StopAnswer;
import com.cloud.agent.api.StopCommand;
import com.cloud.agent.api.UnPlugNicCommand;
import com.cloud.agent.api.check.CheckSshCommand;
import com.cloud.agent.api.routing.NetworkElementCommand;
import com.cloud.agent.api.storage.CopyVolumeCommand;
import com.cloud.agent.api.storage.CreateCommand;
import com.cloud.agent.api.storage.DestroyCommand;
import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource;
import com.cloud.host.Host.Type;
import com.cloud.hypervisor.ovm3.objects.CloudstackPlugin;
import com.cloud.hypervisor.ovm3.objects.Common;
import com.cloud.hypervisor.ovm3.objects.Connection;
import com.cloud.hypervisor.ovm3.objects.Ovm3ResourceException;
import com.cloud.hypervisor.ovm3.objects.OvmObject;
import com.cloud.hypervisor.ovm3.objects.Xen;
import com.cloud.hypervisor.ovm3.resources.helpers.Ovm3Configuration;
import com.cloud.hypervisor.ovm3.resources.helpers.Ovm3HypervisorNetwork;
import com.cloud.hypervisor.ovm3.resources.helpers.Ovm3HypervisorSupport;
import com.cloud.hypervisor.ovm3.resources.helpers.Ovm3StoragePool;
import com.cloud.hypervisor.ovm3.resources.helpers.Ovm3VirtualRoutingSupport;
import com.cloud.hypervisor.ovm3.resources.helpers.Ovm3VmGuestTypes;
import com.cloud.hypervisor.ovm3.resources.helpers.Ovm3VmSupport;
import com.cloud.network.Networks.TrafficType;
import com.cloud.resource.ServerResourceBase;
import com.cloud.resource.hypervisor.HypervisorResource;
import com.cloud.storage.resource.StorageSubsystemCommandHandler;
import com.cloud.storage.resource.StorageSubsystemCommandHandlerBase;
import com.cloud.template.VirtualMachineTemplate.BootloaderType;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State;
public class Ovm3HypervisorResource extends ServerResourceBase implements HypervisorResource {
private static final Logger LOGGER = Logger.getLogger(Ovm3HypervisorResource.class);
@Inject
private VirtualRoutingResource vrResource;
private StorageSubsystemCommandHandler storageHandler;
private Connection c;
private Ovm3StoragePool storagepool;
private Ovm3StorageProcessor storageprocessor;
private Ovm3HypervisorSupport hypervisorsupport;
private Ovm3VmSupport vmsupport;
private Ovm3HypervisorNetwork hypervisornetwork;
private Ovm3VirtualRoutingResource virtualroutingresource;
private Ovm3VirtualRoutingSupport virtualroutingsupport;
private Ovm3Configuration configuration;
private Ovm3VmGuestTypes guesttypes;
private final OvmObject ovmObject = new OvmObject();
@Override
public Type getType() {
return Type.Routing;
}
/*
* configure is called before this, does setup of the connection and
* gets the params.
*
* @see com.cloud.resource.ServerResource#initialize()
*/
@Override
public StartupCommand[] initialize() {
LOGGER.debug("Ovm3 resource intializing");
try {
StartupRoutingCommand srCmd = new StartupRoutingCommand();
StartupStorageCommand ssCmd = new StartupStorageCommand();
/* here stuff gets completed, but where should state live ? */
hypervisorsupport.fillHostInfo(srCmd);
hypervisorsupport.vmStateMapClear();
LOGGER.debug("Ovm3 pool " + ssCmd + " " + srCmd);
return new StartupCommand[] {srCmd, ssCmd};
} catch (Exception e) {
LOGGER.debug("Ovm3 resource initializes failed", e);
return new StartupCommand[] {};
}
}
@Override
public PingCommand getCurrentStatus(long id) {
try {
/* feels useless somehow */
Common test = new Common(c);
String ping = "put";
String pong = test.echo(ping);
if (pong.contains(ping)) {
hypervisorsupport.syncState();
CloudstackPlugin cSp = new CloudstackPlugin(c);
if (!cSp.dom0CheckStorageHealthCheck(configuration.getAgentScriptsDir(), configuration.getAgentCheckStorageScript(), configuration.getCsHostGuid(),
configuration.getAgentStorageCheckTimeout(), configuration.getAgentStorageCheckInterval()) && !cSp.dom0CheckStorageHealthCheck()) {
LOGGER.error("Storage health check not running on " + configuration.getAgentHostname());
} else if (cSp.dom0CheckStorageHealthCheck()) {
LOGGER.error("Storage health check started on " + configuration.getAgentHostname());
} else {
LOGGER.debug("Storage health check running on " + configuration.getAgentHostname());
}
return new PingRoutingCommand(getType(), id, hypervisorsupport.hostVmStateReport());
} else {
LOGGER.debug("Agent did not respond correctly: " + ping + " but got " + pong);
}
} catch (Ovm3ResourceException | NullPointerException e) {
LOGGER.debug("Check agent status failed", e);
return null;
}
return null;
}
@Override
public Answer executeRequest(Command cmd) {
Class<? extends Command> clazz = cmd.getClass();
LOGGER.debug("executeRequest called: " + cmd.getClass());
if (cmd instanceof NetworkElementCommand) {
return vrResource.executeRequest((NetworkElementCommand)cmd);
} else if (clazz == NetworkRulesSystemVmCommand.class) {
return virtualroutingsupport.execute((NetworkRulesSystemVmCommand)cmd);
} else if (clazz == CheckSshCommand.class) {
return virtualroutingsupport.execute((CheckSshCommand)cmd);
} else if (clazz == NetworkUsageCommand.class) {
return virtualroutingsupport.execute((NetworkUsageCommand)cmd);
/* double check order! */
} else if (clazz == CopyCommand.class) {
return storageprocessor.execute((CopyCommand)cmd);
} else if (cmd instanceof StorageSubSystemCommand) {
return storageHandler.handleStorageCommands((StorageSubSystemCommand)cmd);
} else if (clazz == CreateCommand.class) {
return storageprocessor.execute((CreateCommand)cmd);
} else if (clazz == CreateObjectCommand.class) {
return storageprocessor.execute((CreateObjectCommand)cmd);
} else if (clazz == AttachIsoCommand.class) {
return storageprocessor.attachIso((AttachCommand)cmd);
} else if (clazz == CreatePrivateTemplateFromVolumeCommand.class) {
return storageprocessor.execute((CreatePrivateTemplateFromVolumeCommand)cmd);
} else if (clazz == DestroyCommand.class) {
return storageprocessor.execute((DestroyCommand)cmd);
} else if (clazz == CopyVolumeCommand.class) {
return storageprocessor.execute((CopyVolumeCommand)cmd);
} else if (clazz == CreateStoragePoolCommand.class) {
return storagepool.execute((CreateStoragePoolCommand)cmd);
} else if (clazz == ModifyStoragePoolCommand.class) {
return storagepool.execute((ModifyStoragePoolCommand)cmd);
} else if (clazz == PrimaryStorageDownloadCommand.class) {
return storagepool.execute((PrimaryStorageDownloadCommand)cmd);
} else if (clazz == DeleteStoragePoolCommand.class) {
return storagepool.execute((DeleteStoragePoolCommand)cmd);
} else if (clazz == GetStorageStatsCommand.class) {
return storagepool.execute((GetStorageStatsCommand)cmd);
} else if (clazz == GetHostStatsCommand.class) {
return hypervisorsupport.execute((GetHostStatsCommand)cmd);
} else if (clazz == CheckVirtualMachineCommand.class) {
return hypervisorsupport.execute((CheckVirtualMachineCommand)cmd);
} else if (clazz == MaintainCommand.class) {
return hypervisorsupport.execute((MaintainCommand)cmd);
} else if (clazz == CheckHealthCommand.class) {
return hypervisorsupport.execute((CheckHealthCommand)cmd);
} else if (clazz == ReadyCommand.class) {
return hypervisorsupport.execute((ReadyCommand)cmd);
} else if (clazz == FenceCommand.class) {
return hypervisorsupport.execute((FenceCommand)cmd);
} else if (clazz == CheckOnHostCommand.class) {
return hypervisorsupport.execute((CheckOnHostCommand)cmd);
} else if (clazz == PingTestCommand.class) {
return hypervisornetwork.execute((PingTestCommand)cmd);
} else if (clazz == CheckNetworkCommand.class) {
return hypervisornetwork.execute((CheckNetworkCommand)cmd);
} else if (clazz == GetVmStatsCommand.class) {
return vmsupport.execute((GetVmStatsCommand)cmd);
} else if (clazz == PrepareForMigrationCommand.class) {
return vmsupport.execute((PrepareForMigrationCommand)cmd);
} else if (clazz == MigrateCommand.class) {
return vmsupport.execute((MigrateCommand)cmd);
} else if (clazz == GetVncPortCommand.class) {
return vmsupport.execute((GetVncPortCommand)cmd);
} else if (clazz == PlugNicCommand.class) {
return vmsupport.execute((PlugNicCommand)cmd);
} else if (clazz == UnPlugNicCommand.class) {
return vmsupport.execute((UnPlugNicCommand)cmd);
} else if (clazz == StartCommand.class) {
return execute((StartCommand)cmd);
} else if (clazz == StopCommand.class) {
return execute((StopCommand)cmd);
} else if (clazz == RebootCommand.class) {
return execute((RebootCommand)cmd);
}
LOGGER.debug("Can't find class for executeRequest " + cmd.getClass() + ", is your direct call missing?");
return Answer.createUnsupportedCommandAnswer(cmd);
}
@Override
public void disconnected() {
LOGGER.debug("disconnected seems unused everywhere else");
}
@Override
public IAgentControl getAgentControl() {
LOGGER.debug("we don't use IAgentControl");
return null;
}
@Override
public void setAgentControl(IAgentControl agentControl) {
LOGGER.debug("No use in setting IAgentControl");
}
@Override
public String getName() {
return configuration.getAgentName();
}
@Override
public void setName(String name) {
configuration.setAgentName(name);
}
@Override
public void setConfigParams(Map<String, Object> params) {
configuration.setRawParams(params);
}
@Override
public Map<String, Object> getConfigParams() {
return configuration.getRawParams();
}
@Override
public int getRunLevel() {
return 0;
}
@Override
public void setRunLevel(int level) {
LOGGER.debug("runlevel seems unused in other hypervisors");
}
/**
* Base configuration of the plugins components.
*/
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
LOGGER.debug("configure " + name + " with params: " + params);
/* check if we're primary or not and if we can connect */
try {
configuration = new Ovm3Configuration(params);
if (!configuration.getIsTest()) {
c = new Connection(configuration.getAgentIp(), configuration.getAgentOvsAgentPort(), configuration.getAgentOvsAgentUser(),
configuration.getAgentOvsAgentPassword());
c.setHostName(configuration.getAgentHostname());
}
hypervisorsupport = new Ovm3HypervisorSupport(c, configuration);
if (!configuration.getIsTest()) {
hypervisorsupport.setupServer(configuration.getAgentSshKeyFileName());
}
hypervisorsupport.primaryCheck();
} catch (Exception e) {
throw new CloudRuntimeException("Base checks failed for " + configuration.getAgentHostname(), e);
}
hypervisornetwork = new Ovm3HypervisorNetwork(c, configuration);
hypervisornetwork.configureNetworking();
virtualroutingresource = new Ovm3VirtualRoutingResource(c);
storagepool = new Ovm3StoragePool(c, configuration);
storagepool.prepareForPool();
storageprocessor = new Ovm3StorageProcessor(c, configuration, storagepool);
vmsupport = new Ovm3VmSupport(c, configuration, hypervisorsupport, storageprocessor, storagepool, hypervisornetwork);
vrResource = new VirtualRoutingResource(virtualroutingresource);
if (!vrResource.configure(name, params)) {
throw new ConfigurationException("Unable to configure VirtualRoutingResource");
}
guesttypes = new Ovm3VmGuestTypes();
storageHandler = new StorageSubsystemCommandHandlerBase(storageprocessor);
virtualroutingsupport = new Ovm3VirtualRoutingSupport(c, configuration, virtualroutingresource);
setConfigParams(params);
return true;
}
public void setConnection(Connection con) {
LOGGER.debug("override connection: " + con.getIp());
c = con;
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
@Override
public synchronized StartAnswer execute(StartCommand cmd) {
VirtualMachineTO vmSpec = cmd.getVirtualMachine();
String vmName = vmSpec.getName();
State state = State.Stopped;
Xen xen = new Xen(c);
try {
hypervisorsupport.setVmStateStarting(vmName);
Xen.Vm vm = xen.getVmConfig();
/* max and min ? */
vm.setVmCpus(vmSpec.getCpus());
/* in mb not in bytes */
vm.setVmMemory(vmSpec.getMinRam() / 1024 / 1024);
vm.setVmUuid(UUID.nameUUIDFromBytes(vmSpec.getName().getBytes(Charset.defaultCharset())).toString());
vm.setVmName(vmName);
String domType = guesttypes.getOvm3GuestType(vmSpec.getOs());
if (domType == null || domType.isEmpty()) {
domType = "default";
LOGGER.debug("VM Virt type missing setting to: " + domType);
} else {
LOGGER.debug("VM Virt type set to " + domType + " for " + vmSpec.getOs());
}
vm.setVmDomainType(domType);
if (vmSpec.getBootloader() == BootloaderType.CD) {
LOGGER.warn("CD booting is not supported");
}
/*
* officially CD boot is only supported on HVM, although there is a
* simple way around it..
*/
vmsupport.createVbds(vm, vmSpec);
if (vmSpec.getType() != VirtualMachine.Type.User) {
// double check control network if we run a non user VM
hypervisornetwork.configureNetworking();
vm.setVmExtra(vmSpec.getBootArgs().replace(" ", "%"));
String svmPath = configuration.getAgentOvmRepoPath() + "/" + ovmObject.deDash(vm.getPrimaryPoolUuid()) + "/ISOs";
String svmIso = svmPath + "/" + storagepool.getSystemVMPatchIsoFile().getName();
vm.addIso(svmIso);
}
/* OVS/Network stuff should go here! */
vmsupport.createVifs(vm, vmSpec);
vm.setupVifs();
vm.setVnc("0.0.0.0", vmSpec.getVncPassword());
xen.createVm(ovmObject.deDash(vm.getPrimaryPoolUuid()), vm.getVmUuid());
xen.startVm(ovmObject.deDash(vm.getPrimaryPoolUuid()), vm.getVmUuid());
state = State.Running;
if (vmSpec.getType() != VirtualMachine.Type.User) {
String controlIp = null;
for (NicTO nic : vmSpec.getNics()) {
if (nic.getType() == TrafficType.Control) {
controlIp = nic.getIp();
}
}
/* fix is in cloudstack.py for xend restart timer */
for (int count = 0; count < 60; count++) {
CloudstackPlugin cSp = new CloudstackPlugin(c);
/* skip a beat to make sure we didn't miss start */
if (hypervisorsupport.getVmState(vmName) == null && count > 1) {
String msg = "VM " + vmName + " went missing on " + configuration.getAgentHostname() + ", returning stopped";
LOGGER.debug(msg);
state = State.Stopped;
return new StartAnswer(cmd, msg);
}
/* creative fix? */
try {
Boolean res = cSp.domrCheckSsh(controlIp);
LOGGER.debug("connected to " + controlIp + " on attempt " + count + " result: " + res);
if (res) {
break;
}
} catch (Exception x) {
LOGGER.trace("unable to connect to " + controlIp + " on attempt " + count + " " + x.getMessage(), x);
}
Thread.sleep(5000);
}
}
/*
* Can't remember if HA worked if we were only a pool ?
*/
if (configuration.getAgentInOvm3Pool() && configuration.getAgentInOvm3Cluster()) {
xen.configureVmHa(ovmObject.deDash(vm.getPrimaryPoolUuid()), vm.getVmUuid(), true);
}
/* should be starting no ? */
state = State.Running;
return new StartAnswer(cmd);
} catch (Exception e) {
LOGGER.debug("Start vm " + vmName + " failed", e);
state = State.Stopped;
return new StartAnswer(cmd, e.getMessage());
} finally {
hypervisorsupport.setVmState(vmName, state);
}
}
/**
* Removes the vm and its configuration from the hypervisor.
*/
@Override
public StopAnswer execute(StopCommand cmd) {
String vmName = cmd.getVmName();
State state = State.Error;
hypervisorsupport.setVmState(vmName, State.Stopping);
try {
Xen vms = new Xen(c);
Xen.Vm vm = null;
vm = vms.getRunningVmConfig(vmName);
if (vm == null) {
state = State.Stopping;
LOGGER.debug("Unable to get details of vm: " + vmName + ", treating it as Stopping");
return new StopAnswer(cmd, "success", true);
}
String repoId = ovmObject.deDash(vm.getVmRootDiskPoolId());
String vmId = vm.getVmUuid();
/* can we do without the poolId ? */
vms.stopVm(repoId, vmId);
int tries = 30;
while (vms.getRunningVmConfig(vmName) != null && tries > 0) {
String msg = "Waiting for " + vmName + " to stop";
LOGGER.debug(msg);
tries--;
Thread.sleep(10 * 1000);
}
vms.deleteVm(repoId, vmId);
vmsupport.cleanup(vm);
if (vms.getRunningVmConfig(vmName) != null) {
String msg = "Stop " + vmName + " failed ";
LOGGER.debug(msg);
return new StopAnswer(cmd, msg, false);
}
state = State.Stopped;
return new StopAnswer(cmd, "success", true);
} catch (Exception e) {
LOGGER.debug("Stop " + vmName + " failed ", e);
return new StopAnswer(cmd, e.getMessage(), false);
} finally {
if (state != null) {
hypervisorsupport.setVmState(vmName, state);
} else {
hypervisorsupport.revmoveVmState(vmName);
}
}
}
@Override
public RebootAnswer execute(RebootCommand cmd) {
String vmName = cmd.getVmName();
hypervisorsupport.setVmStateStarting(vmName);
try {
Xen xen = new Xen(c);
Xen.Vm vm = xen.getRunningVmConfig(vmName);
if (vm == null) {
return new RebootAnswer(cmd, vmName + " not present", false);
}
xen.rebootVm(ovmObject.deDash(vm.getVmRootDiskPoolId()), vm.getVmUuid());
vm = xen.getRunningVmConfig(vmName);
Integer vncPort = vm.getVncPort();
return new RebootAnswer(cmd, null, vncPort);
} catch (Exception e) {
LOGGER.debug("Reboot " + vmName + " failed", e);
return new RebootAnswer(cmd, e.getMessage(), false);
} finally {
hypervisorsupport.setVmState(vmName, State.Running);
}
}
@Override
protected String getDefaultScriptsDir() {
return null;
}
}