blob: 33d4eaf6e777104d69ad9f15698acbeabf24acce [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.xenserver.resource.wrapper.xenbase;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.cloud.agent.resource.virtualnetwork.VRScripts;
import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource;
import org.apache.commons.lang3.StringUtils;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.OvsSetTagAndFlowAnswer;
import com.cloud.agent.api.OvsSetTagAndFlowCommand;
import com.cloud.agent.api.StartAnswer;
import com.cloud.agent.api.StartCommand;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.GPUDeviceTO;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
import com.cloud.network.Networks;
import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.IsolationType;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.cloud.storage.Storage;
import com.cloud.storage.Volume;
import com.cloud.vm.VirtualMachine;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.Host;
import com.xensource.xenapi.Types.VmPowerState;
import com.xensource.xenapi.VDI;
import com.xensource.xenapi.VM;
@ResourceWrapper(handles = StartCommand.class)
public final class CitrixStartCommandWrapper extends CommandWrapper<StartCommand, Answer, CitrixResourceBase> {
@Override
public Answer execute(final StartCommand command, final CitrixResourceBase citrixResourceBase) {
final Connection conn = citrixResourceBase.getConnection();
final VirtualMachineTO vmSpec = command.getVirtualMachine();
final String vmName = vmSpec.getName();
VmPowerState state = VmPowerState.HALTED;
VM vm = null;
// if a VDI is created, record its UUID and its type (ex. VHD) to send back to the CS MS
final Map<String, Map<String, String>> iqnToData = new HashMap<>();
try {
final Set<VM> vms = VM.getByNameLabel(conn, vmName);
if (vms != null) {
for (final VM v : vms) {
final VM.Record vRec = v.getRecord(conn);
if (vRec.powerState == VmPowerState.HALTED) {
v.destroy(conn);
} else if (vRec.powerState == VmPowerState.RUNNING) {
final String host = vRec.residentOn.getUuid(conn);
final String msg = "VM " + vmName + " is runing on host " + host;
logger.debug(msg);
return new StartAnswer(command, msg, host);
} else {
final String msg = "There is already a VM having the same name " + vmName + " vm record " + vRec.toString();
logger.warn(msg);
return new StartAnswer(command, msg);
}
}
}
logger.debug("1. The VM " + vmName + " is in Starting state.");
final Host host = Host.getByUuid(conn, citrixResourceBase.getHost().getUuid());
vm = citrixResourceBase.createVmFromTemplate(conn, vmSpec, host);
final GPUDeviceTO gpuDevice = vmSpec.getGpuDevice();
if (gpuDevice != null) {
logger.debug("Creating VGPU for of VGPU type: " + gpuDevice.getVgpuType() + " in GPU group " + gpuDevice.getGpuGroup() + " for VM " + vmName);
citrixResourceBase.createVGPU(conn, command, vm, gpuDevice);
}
if (vmSpec.getType() != VirtualMachine.Type.User) {
citrixResourceBase.createPatchVbd(conn, vmName, vm);
}
prepareDisks(vmSpec, citrixResourceBase, conn, iqnToData, vmName, vm);
for (final NicTO nic : vmSpec.getNics()) {
citrixResourceBase.createVif(conn, vmName, vm, vmSpec, nic);
}
citrixResourceBase.startVM(conn, host, vm, vmName);
if (citrixResourceBase.isOvs()) {
// TODO(Salvatore-orlando): This code should go
for (final NicTO nic : vmSpec.getNics()) {
if (nic.getBroadcastType() == Networks.BroadcastDomainType.Vswitch) {
final HashMap<String, String> args = citrixResourceBase.parseDefaultOvsRuleCommand(BroadcastDomainType.getValue(nic.getBroadcastUri()));
final OvsSetTagAndFlowCommand flowCmd = new OvsSetTagAndFlowCommand(args.get("vmName"), args.get("tag"), args.get("vlans"), args.get("seqno"),
Long.parseLong(args.get("vmId")));
final CitrixRequestWrapper citrixRequestWrapper = CitrixRequestWrapper.getInstance();
final OvsSetTagAndFlowAnswer r = (OvsSetTagAndFlowAnswer) citrixRequestWrapper.execute(flowCmd, citrixResourceBase);
if (!r.getResult()) {
logger.warn("Failed to set flow for VM " + r.getVmId());
} else {
logger.info("Success to set flow for VM " + r.getVmId());
}
}
}
}
if (citrixResourceBase.canBridgeFirewall()) {
String result = null;
if (vmSpec.getType() != VirtualMachine.Type.User) {
final NicTO[] nics = vmSpec.getNics();
boolean secGrpEnabled = false;
for (final NicTO nic : nics) {
if (nic.isSecurityGroupEnabled() || nic.getIsolationUri() != null && nic.getIsolationUri().getScheme().equalsIgnoreCase(IsolationType.Ec2.toString())) {
secGrpEnabled = true;
break;
}
}
if (secGrpEnabled) {
result = citrixResourceBase.callHostPlugin(conn, "vmops", "default_network_rules_systemvm", "vmName", vmName);
if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) {
logger.warn("Failed to program default network rules for " + vmName);
} else {
logger.info("Programmed default network rules for " + vmName);
}
}
} else {
// For user vm, program the rules for each nic if the
// isolation uri scheme is ec2
final NicTO[] nics = vmSpec.getNics();
for (final NicTO nic : nics) {
if (nic.isSecurityGroupEnabled() || nic.getIsolationUri() != null && nic.getIsolationUri().getScheme().equalsIgnoreCase(IsolationType.Ec2.toString())) {
final List<String> nicSecIps = nic.getNicSecIps();
String secIpsStr;
final StringBuilder sb = new StringBuilder();
if (nicSecIps != null) {
for (final String ip : nicSecIps) {
sb.append(ip).append(";");
}
secIpsStr = sb.toString();
} else {
secIpsStr = "0;";
}
result = citrixResourceBase.callHostPlugin(conn, "vmops", "default_network_rules", "vmName", vmName, "vmIP", nic.getIp(), "vmMAC", nic.getMac(),
"vmID", Long.toString(vmSpec.getId()), "secIps", secIpsStr);
if (result == null || result.isEmpty() || !Boolean.parseBoolean(result)) {
logger.warn("Failed to program default network rules for " + vmName + " on nic with ip:" + nic.getIp() + " mac:" + nic.getMac());
} else {
logger.info("Programmed default network rules for " + vmName + " on nic with ip:" + nic.getIp() + " mac:" + nic.getMac());
}
}
}
}
}
state = VmPowerState.RUNNING;
if (vmSpec.getType() != VirtualMachine.Type.User) {
String controlIp = null;
for (final NicTO nic : vmSpec.getNics()) {
if (nic.getType() == Networks.TrafficType.Control) {
controlIp = nic.getIp();
break;
}
}
String result2 = citrixResourceBase.connect(conn, vmName, controlIp, 1000);
if (StringUtils.isEmpty(result2)) {
logger.info(String.format("Connected to SystemVM: %s", vmName));
}
try {
citrixResourceBase.copyPatchFilesToVR(controlIp, VRScripts.CONFIG_CACHE_LOCATION);
VirtualRoutingResource vrResource = citrixResourceBase.getVirtualRoutingResource();
if (!vrResource.isSystemVMSetup(vmName, controlIp)) {
String errMsg = "Failed to patch systemVM";
logger.error(errMsg);
return new StartAnswer(command, errMsg);
}
} catch (Exception e) {
String errMsg = "Failed to scp files to system VM. Patching of systemVM failed";
logger.error(errMsg, e);
return new StartAnswer(command, String.format("%s due to: %s", errMsg, e.getMessage()));
}
}
final StartAnswer startAnswer = new StartAnswer(command);
startAnswer.setIqnToData(iqnToData);
return startAnswer;
} catch (final Exception e) {
logger.warn("Catch Exception: " + e.getClass().toString() + " due to " + e.toString(), e);
final String msg = citrixResourceBase.handleVmStartFailure(conn, vmName, vm, "", e);
final StartAnswer startAnswer = new StartAnswer(command, msg);
startAnswer.setIqnToData(iqnToData);
return startAnswer;
} finally {
if (state != VmPowerState.HALTED) {
logger.debug("2. The VM " + vmName + " is in " + state + " state.");
} else {
logger.debug("The VM is in stopped state, detected problem during startup : " + vmName);
}
}
}
private void prepareDisks(VirtualMachineTO vmSpec, CitrixResourceBase citrixResourceBase, Connection conn,
Map<String, Map<String, String>> iqnToData, String vmName, VM vm) throws Exception {
// put cdrom at the first place in the list
List<DiskTO> disks = new ArrayList<DiskTO>(vmSpec.getDisks().length);
int index = 0;
for (final DiskTO disk : vmSpec.getDisks()) {
if (Volume.Type.ISO.equals(disk.getType())) {
disks.add(0, disk);
} else {
disks.add(index, disk);
}
index++;
}
int isoCount = 0;
for (DiskTO disk : disks) {
final VDI newVdi = citrixResourceBase.prepareManagedDisk(conn, disk, vmSpec.getId(), vmSpec.getName());
if (newVdi != null) {
final Map<String, String> data = new HashMap<>();
final String path = newVdi.getUuid(conn);
data.put(StartAnswer.PATH, path);
data.put(StartAnswer.IMAGE_FORMAT, Storage.ImageFormat.VHD.toString());
iqnToData.put(disk.getDetails().get(DiskTO.IQN), data);
}
citrixResourceBase.createVbd(conn, disk, vmName, vm, vmSpec.getBootloader(), newVdi, isoCount);
if (disk.getType() == Volume.Type.ISO) {
isoCount++;
}
}
}
}