Merge branch '4.19' of https://github.com/apache/cloudstack into 4.20

This commit is contained in:
Pearl Dsilva 2025-05-12 12:41:34 +05:30
commit a21f912be3
22 changed files with 491 additions and 86 deletions

View File

@ -24,11 +24,13 @@ public class GetVmIpAddressCommand extends Command {
String vmName; String vmName;
String vmNetworkCidr; String vmNetworkCidr;
boolean windows = false; boolean windows = false;
String macAddress;
public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows) { public GetVmIpAddressCommand(String vmName, String vmNetworkCidr, boolean windows, String macAddress) {
this.vmName = vmName; this.vmName = vmName;
this.windows = windows; this.windows = windows;
this.vmNetworkCidr = vmNetworkCidr; this.vmNetworkCidr = vmNetworkCidr;
this.macAddress = macAddress;
} }
@Override @Override
@ -47,4 +49,8 @@ public class GetVmIpAddressCommand extends Command {
public String getVmNetworkCidr() { public String getVmNetworkCidr() {
return vmNetworkCidr; return vmNetworkCidr;
} }
public String getMacAddress() {
return macAddress;
}
} }

View File

@ -223,6 +223,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
protected final ConfigKey<Boolean> CheckTxnBeforeSending = new ConfigKey<Boolean>("Developer", Boolean.class, "check.txn.before.sending.agent.commands", "false", protected final ConfigKey<Boolean> CheckTxnBeforeSending = new ConfigKey<Boolean>("Developer", Boolean.class, "check.txn.before.sending.agent.commands", "false",
"This parameter allows developers to enable a check to see if a transaction wraps commands that are sent to the resource. This is not to be enabled on production systems.", true); "This parameter allows developers to enable a check to see if a transaction wraps commands that are sent to the resource. This is not to be enabled on production systems.", true);
public static final List<Host.Type> HOST_DOWN_ALERT_UNSUPPORTED_HOST_TYPES = Arrays.asList(
Host.Type.SecondaryStorage,
Host.Type.ConsoleProxy
);
@Override @Override
public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException { public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException {
@ -986,9 +991,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
if (determinedState == Status.Down) { if (determinedState == Status.Down) {
final String message = String.format("Host %s is down. Starting HA on the VMs", host); final String message = String.format("Host %s is down. Starting HA on the VMs", host);
logger.error(message); logger.error(message);
if (host.getType() != Host.Type.SecondaryStorage && host.getType() != Host.Type.ConsoleProxy) { if (Status.Down.equals(host.getStatus())) {
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), logger.debug(String.format("Skipping sending alert for %s as it already in %s state",
host.getPodId(), String.format("Host down, %s", host), message); host, host.getStatus()));
} else if (!HOST_DOWN_ALERT_UNSUPPORTED_HOST_TYPES.contains(host.getType())) {
_alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_HOST, host.getDataCenterId(), host.getPodId(), "Host down, " + host.getId(), message);
} }
event = Status.Event.HostDown; event = Status.Event.HostDown;
} else if (determinedState == Status.Up) { } else if (determinedState == Status.Up) {

View File

@ -28,4 +28,6 @@ public interface UsageNetworksDao extends GenericDao<UsageNetworksVO, Long> {
void remove(long networkId, Date removed); void remove(long networkId, Date removed);
List<UsageNetworksVO> getUsageRecords(Long accountId, Date startDate, Date endDate); List<UsageNetworksVO> getUsageRecords(Long accountId, Date startDate, Date endDate);
List<UsageNetworksVO> listAll(long networkId);
} }

View File

@ -19,6 +19,7 @@ package com.cloud.usage.dao;
import com.cloud.usage.UsageNetworksVO; import com.cloud.usage.UsageNetworksVO;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.TransactionLegacy;
@ -26,6 +27,7 @@ import org.springframework.stereotype.Component;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import javax.annotation.PostConstruct;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
@ -40,6 +42,14 @@ public class UsageNetworksDaoImpl extends GenericDaoBase<UsageNetworksVO, Long>
" account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " +
" OR ((created <= ?) AND (removed >= ?)))"; " OR ((created <= ?) AND (removed >= ?)))";
private SearchBuilder<UsageNetworksVO> usageNetworksSearch;
@PostConstruct
public void init() {
usageNetworksSearch = createSearchBuilder();
usageNetworksSearch.and("networkId", usageNetworksSearch.entity().getNetworkId(), SearchCriteria.Op.EQ);
usageNetworksSearch.done();
}
@Override @Override
public void update(long networkId, long newNetworkOffering, String state) { public void update(long networkId, long newNetworkOffering, String state) {
@ -131,4 +141,11 @@ public class UsageNetworksDaoImpl extends GenericDaoBase<UsageNetworksVO, Long>
return usageRecords; return usageRecords;
} }
@Override
public List<UsageNetworksVO> listAll(long networkId) {
SearchCriteria<UsageNetworksVO> sc = usageNetworksSearch.create();
sc.setParameters("networkId", networkId);
return listBy(sc);
}
} }

View File

@ -24,6 +24,10 @@ import java.util.List;
public interface UsageVpcDao extends GenericDao<UsageVpcVO, Long> { public interface UsageVpcDao extends GenericDao<UsageVpcVO, Long> {
void update(UsageVpcVO usage); void update(UsageVpcVO usage);
void remove(long vpcId, Date removed); void remove(long vpcId, Date removed);
List<UsageVpcVO> getUsageRecords(Long accountId, Date startDate, Date endDate); List<UsageVpcVO> getUsageRecords(Long accountId, Date startDate, Date endDate);
List<UsageVpcVO> listAll(long vpcId);
} }

View File

@ -19,10 +19,12 @@ package com.cloud.usage.dao;
import com.cloud.usage.UsageVpcVO; import com.cloud.usage.UsageVpcVO;
import com.cloud.utils.DateUtil; import com.cloud.utils.DateUtil;
import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.db.TransactionLegacy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.ArrayList; import java.util.ArrayList;
@ -36,6 +38,15 @@ public class UsageVpcDaoImpl extends GenericDaoBase<UsageVpcVO, Long> implements
" account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " + " account_id = ? AND ((removed IS NULL AND created <= ?) OR (created BETWEEN ? AND ?) OR (removed BETWEEN ? AND ?) " +
" OR ((created <= ?) AND (removed >= ?)))"; " OR ((created <= ?) AND (removed >= ?)))";
private SearchBuilder<UsageVpcVO> usageVpcSearch;
@PostConstruct
public void init() {
usageVpcSearch = createSearchBuilder();
usageVpcSearch.and("vpcId", usageVpcSearch.entity().getVpcId(), SearchCriteria.Op.EQ);
usageVpcSearch.done();
}
@Override @Override
public void update(UsageVpcVO usage) { public void update(UsageVpcVO usage) {
TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB); TransactionLegacy txn = TransactionLegacy.open(TransactionLegacy.USAGE_DB);
@ -124,4 +135,11 @@ public class UsageVpcDaoImpl extends GenericDaoBase<UsageVpcVO, Long> implements
return usageRecords; return usageRecords;
} }
@Override
public List<UsageVpcVO> listAll(long vpcId) {
SearchCriteria<UsageVpcVO> sc = usageVpcSearch.create();
sc.setParameters("vpcId", vpcId);
return listBy(sc);
}
} }

View File

@ -66,10 +66,13 @@ public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper<Ge
String sanitizedVmName = sanitizeBashCommandArgument(vmName); String sanitizedVmName = sanitizeBashCommandArgument(vmName);
String networkCidr = command.getVmNetworkCidr(); String networkCidr = command.getVmNetworkCidr();
String macAddress = command.getMacAddress();
ip = ipFromDomIf(sanitizedVmName, networkCidr); init();
if (ip == null) { ip = ipFromDomIf(sanitizedVmName, networkCidr, macAddress);
if (ip == null && networkCidr != null) {
if(!command.isWindows()) { if(!command.isWindows()) {
ip = ipFromDhcpLeaseFile(sanitizedVmName, networkCidr); ip = ipFromDhcpLeaseFile(sanitizedVmName, networkCidr);
} else { } else {
@ -87,25 +90,17 @@ public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper<Ge
return new Answer(command, result, ip); return new Answer(command, result, ip);
} }
private String ipFromDomIf(String sanitizedVmName, String networkCidr) { private String ipFromDomIf(String sanitizedVmName, String networkCidr, String macAddress) {
String ip = null; String ip = null;
List<String[]> commands = new ArrayList<>(); List<String[]> commands = new ArrayList<>();
commands.add(new String[]{virsh_path, "domifaddr", sanitizedVmName, "--source", "agent"}); commands.add(new String[]{virsh_path, "domifaddr", sanitizedVmName, "--source", "agent"});
Pair<Integer,String> response = executePipedCommands(commands, 0); Pair<Integer,String> response = executePipedCommands(commands, 0);
if (response != null) { if (response != null) {
String output = response.second(); String output = response.second();
String[] lines = output.split("\n"); Pair<String, String> ipAddresses = getIpAddresses(output, macAddress);
for (String line : lines) { String ipv4 = ipAddresses.first();
if (line.contains("ipv4")) { if (networkCidr == null || NetUtils.isIpWithInCidrRange(ipv4, networkCidr)) {
String[] parts = line.split(" "); ip = ipv4;
String[] ipParts = parts[parts.length-1].split("/");
if (ipParts.length > 1) {
if (NetUtils.isIpWithInCidrRange(ipParts[0], networkCidr)) {
ip = ipParts[0];
break;
}
}
}
} }
} else { } else {
logger.error("ipFromDomIf: Command execution failed for VM: " + sanitizedVmName); logger.error("ipFromDomIf: Command execution failed for VM: " + sanitizedVmName);
@ -113,6 +108,38 @@ public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper<Ge
return ip; return ip;
} }
private Pair<String, String> getIpAddresses(String output, String macAddress) {
String ipv4 = null;
String ipv6 = null;
boolean found = false;
String[] lines = output.split("\n");
for (String line : lines) {
String[] parts = line.replaceAll(" +", " ").trim().split(" ");
if (parts.length < 4) {
continue;
}
String device = parts[0];
String mac = parts[1];
if (found) {
if (!device.equals("-") || !mac.equals("-")) {
break;
}
} else if (!mac.equals(macAddress)) {
continue;
}
found = true;
String ipFamily = parts[2];
String ipPart = parts[3].split("/")[0];
if (ipFamily.equals("ipv4")) {
ipv4 = ipPart;
} else if (ipFamily.equals("ipv6")) {
ipv6 = ipPart;
}
}
s_logger.debug(String.format("Found ipv4: %s and ipv6: %s with mac address %s", ipv4, ipv6, macAddress));
return new Pair<>(ipv4, ipv6);
}
private String ipFromDhcpLeaseFile(String sanitizedVmName, String networkCidr) { private String ipFromDhcpLeaseFile(String sanitizedVmName, String networkCidr) {
String ip = null; String ip = null;
List<String[]> commands = new ArrayList<>(); List<String[]> commands = new ArrayList<>();

View File

@ -66,6 +66,7 @@ public class LibvirtGetVmIpAddressCommandWrapperTest {
when(getVmIpAddressCommand.getVmName()).thenReturn("validVmName"); when(getVmIpAddressCommand.getVmName()).thenReturn("validVmName");
when(getVmIpAddressCommand.getVmNetworkCidr()).thenReturn("192.168.0.0/24"); when(getVmIpAddressCommand.getVmNetworkCidr()).thenReturn("192.168.0.0/24");
when(getVmIpAddressCommand.getMacAddress()).thenReturn("02:0c:02:f9:00:80");
when(getVmIpAddressCommand.isWindows()).thenReturn(false); when(getVmIpAddressCommand.isWindows()).thenReturn(false);
when(Script.executePipedCommands(anyList(), anyLong())).thenReturn(new Pair<>(0, VIRSH_DOMIF_OUTPUT)); when(Script.executePipedCommands(anyList(), anyLong())).thenReturn(new Pair<>(0, VIRSH_DOMIF_OUTPUT));
@ -88,6 +89,7 @@ public class LibvirtGetVmIpAddressCommandWrapperTest {
when(getVmIpAddressCommand.getVmName()).thenReturn("invalidVmName!"); when(getVmIpAddressCommand.getVmName()).thenReturn("invalidVmName!");
when(getVmIpAddressCommand.getVmNetworkCidr()).thenReturn("192.168.0.0/24"); when(getVmIpAddressCommand.getVmNetworkCidr()).thenReturn("192.168.0.0/24");
when(getVmIpAddressCommand.getMacAddress()).thenReturn("02:0c:02:f9:00:80");
when(getVmIpAddressCommand.isWindows()).thenReturn(false); when(getVmIpAddressCommand.isWindows()).thenReturn(false);
when(Script.executePipedCommands(anyList(), anyLong())).thenReturn(new Pair<>(0, VIRSH_DOMIF_OUTPUT)); when(Script.executePipedCommands(anyList(), anyLong())).thenReturn(new Pair<>(0, VIRSH_DOMIF_OUTPUT));
@ -114,6 +116,7 @@ public class LibvirtGetVmIpAddressCommandWrapperTest {
when(getVmIpAddressCommand.getVmName()).thenReturn("validVmName"); when(getVmIpAddressCommand.getVmName()).thenReturn("validVmName");
when(getVmIpAddressCommand.getVmNetworkCidr()).thenReturn("192.168.0.0/24"); when(getVmIpAddressCommand.getVmNetworkCidr()).thenReturn("192.168.0.0/24");
when(getVmIpAddressCommand.getMacAddress()).thenReturn("02:0c:02:f9:00:80");
when(getVmIpAddressCommand.isWindows()).thenReturn(true); when(getVmIpAddressCommand.isWindows()).thenReturn(true);
when(Script.executePipedCommands(anyList(), anyLong())).thenReturn(new Pair<>(0, "192.168.0.10")); when(Script.executePipedCommands(anyList(), anyLong())).thenReturn(new Pair<>(0, "192.168.0.10"));

View File

@ -34,6 +34,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.cloud.agent.api.CleanupVMCommand;
import com.cloud.agent.api.Command; import com.cloud.agent.api.Command;
import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataStoreTO;
@ -234,4 +235,12 @@ public class XenServerGuru extends HypervisorGuruBase implements HypervisorGuru,
public ConfigKey<?>[] getConfigKeys() { public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {MaxNumberOfVCPUSPerVM}; return new ConfigKey<?>[] {MaxNumberOfVCPUSPerVM};
} }
@Override
public List<Command> finalizeExpunge(VirtualMachine vm) {
List<Command> commands = new ArrayList<>();
final CleanupVMCommand cleanupVMCommand = new CleanupVMCommand(vm.getInstanceName(), true);
commands.add(cleanupVMCommand);
return commands;
}
} }

View File

@ -0,0 +1,82 @@
// 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.Iterator;
import java.util.Set;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CleanupVMCommand;
import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
import com.cloud.resource.CommandWrapper;
import com.cloud.resource.ResourceWrapper;
import com.xensource.xenapi.Connection;
import com.xensource.xenapi.Types;
import com.xensource.xenapi.VM;
@ResourceWrapper(handles = CleanupVMCommand.class)
public class CitrixCleanupVMCommandWrapper extends CommandWrapper<CleanupVMCommand, Answer, CitrixResourceBase> {
private static final Logger s_logger = Logger.getLogger(CitrixCleanupVMCommandWrapper.class);
@Override
public Answer execute(final CleanupVMCommand command, final CitrixResourceBase citrixResourceBase) {
if (citrixResourceBase.isDestroyHaltedVms()) {
s_logger.debug(String.format("Cleanup VM is not needed for host with version %s",
citrixResourceBase.getHost().getProductVersion()));
return new Answer(command);
}
final String vmName = command.getVmName();
try {
final Connection conn = citrixResourceBase.getConnection();
final Set<VM> vms = VM.getByNameLabel(conn, vmName);
if (vms.isEmpty()) {
return new Answer(command, true, "VM does not exist");
}
// destroy vm which is in HALTED state on this host
final Iterator<VM> iter = vms.iterator();
while (iter.hasNext()) {
final VM vm = iter.next();
final VM.Record vmr = vm.getRecord(conn);
if (!Types.VmPowerState.HALTED.equals(vmr.powerState)) {
final String msg = String.format("VM %s is not in %s state", vmName, Types.VmPowerState.HALTED);
s_logger.error(msg);
return new Answer(command, false, msg);
}
if (citrixResourceBase.isRefNull(vmr.residentOn)) {
continue;
}
if (vmr.residentOn.getUuid(conn).equals(citrixResourceBase.getHost().getUuid())) {
continue;
}
iter.remove();
}
for (final VM vm : vms) {
citrixResourceBase.destroyVm(vm, conn, true);
}
} catch (final Exception e) {
final String msg = String.format("Clean up VM %s fail due to %s", vmName, e);
s_logger.error(msg, e);
return new Answer(command, false, e.getMessage());
}
return new Answer(command);
}
}

View File

@ -1934,7 +1934,7 @@ public class CitrixRequestWrapperTest {
vmIpsMap.put("Test", "127.0.0.1"); vmIpsMap.put("Test", "127.0.0.1");
rec.networks = vmIpsMap; rec.networks = vmIpsMap;
final GetVmIpAddressCommand getVmIpAddrCmd = new GetVmIpAddressCommand("Test", "127.0.0.1/24", false); final GetVmIpAddressCommand getVmIpAddrCmd = new GetVmIpAddressCommand("Test", "127.0.0.1/24", false, null);
final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance(); final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance();
assertNotNull(wrapper); assertNotNull(wrapper);

View File

@ -24,6 +24,7 @@ import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -45,6 +46,7 @@ import org.apache.cloudstack.managed.context.ManagedContext;
import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.managed.context.ManagedContextRunnable;
import org.apache.cloudstack.management.ManagementServerHost; import org.apache.cloudstack.management.ManagementServerHost;
import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.ThreadContext;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.agent.AgentManager; import com.cloud.agent.AgentManager;
import com.cloud.alert.AlertManager; import com.cloud.alert.AlertManager;
@ -73,7 +75,6 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.VpcVirtualNetworkApplianceService; import com.cloud.network.VpcVirtualNetworkApplianceService;
import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceManager;
import com.cloud.server.ManagementServer; import com.cloud.server.ManagementServer;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManager;
@ -236,6 +237,18 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
long _timeBetweenCleanups; long _timeBetweenCleanups;
String _haTag = null; String _haTag = null;
private boolean vmHasPendingHAJob(final List<HaWorkVO> pendingHaWorks, final VMInstanceVO vm) {
Optional<HaWorkVO> item = pendingHaWorks.stream()
.filter(h -> h.getInstanceId() == vm.getId())
.reduce((first, second) -> second);
if (item.isPresent() && (item.get().getTimesTried() < _maxRetries ||
!item.get().canScheduleNew(_timeBetweenFailures))) {
logger.debug(String.format("Skipping HA on %s as there is already a running HA job for it", vm));
return true;
}
return false;
}
protected HighAvailabilityManagerImpl() { protected HighAvailabilityManagerImpl() {
} }
@ -295,28 +308,37 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
logger.warn("Scheduling restart for VMs on host {}", host); logger.warn("Scheduling restart for VMs on host {}", host);
final List<VMInstanceVO> vms = _instanceDao.listByHostId(host.getId()); final List<VMInstanceVO> vms = _instanceDao.listByHostId(host.getId());
final List<HaWorkVO> pendingHaWorks = _haDao.listPendingHAWorkForHost(host.getId());
final DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId()); final DataCenterVO dcVO = _dcDao.findById(host.getDataCenterId());
// send an email alert that the host is down // send an email alert that the host is down
StringBuilder sb = null; StringBuilder sb = null;
List<VMInstanceVO> reorderedVMList = new ArrayList<VMInstanceVO>(); List<VMInstanceVO> reorderedVMList = new ArrayList<VMInstanceVO>();
if ((vms != null) && !vms.isEmpty()) { int skippedHAVms = 0;
if (CollectionUtils.isNotEmpty(vms)) {
sb = new StringBuilder(); sb = new StringBuilder();
sb.append(" Starting HA on the following VMs:"); sb.append(" Starting HA on the following VMs:");
// collect list of vm names for the alert email // collect list of vm names for the alert email
for (int i = 0; i < vms.size(); i++) { for (VMInstanceVO vm : vms) {
VMInstanceVO vm = vms.get(i); if (vmHasPendingHAJob(pendingHaWorks, vm)) {
skippedHAVms++;
continue;
}
if (vm.getType() == VirtualMachine.Type.User) { if (vm.getType() == VirtualMachine.Type.User) {
reorderedVMList.add(vm); reorderedVMList.add(vm);
} else { } else {
reorderedVMList.add(0, vm); reorderedVMList.add(0, vm);
} }
if (vm.isHaEnabled()) { if (vm.isHaEnabled()) {
sb.append(" " + vm.getHostName()); sb.append(" ").append(vm.getHostName());
} }
} }
} }
if (reorderedVMList.isEmpty() && skippedHAVms > 0 && skippedHAVms == vms.size()) {
logger.debug(String.format(
"Skipping sending alert for %s as it is suspected to be a duplicate of a recent alert", host));
return;
}
// send an email alert that the host is down, include VMs // send an email alert that the host is down, include VMs
HostPodVO podVO = _podDao.findById(host.getPodId()); HostPodVO podVO = _podDao.findById(host.getPodId());
String hostDesc = "name: " + host.getName() + " (id:" + host.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName(); String hostDesc = "name: " + host.getName() + " (id:" + host.getId() + "), availability zone: " + dcVO.getName() + ", pod: " + podVO.getName();
@ -324,7 +346,6 @@ public class HighAvailabilityManagerImpl extends ManagerBase implements Configur
"Host [" + hostDesc + "] is down." + ((sb != null) ? sb.toString() : "")); "Host [" + hostDesc + "] is down." + ((sb != null) ? sb.toString() : ""));
for (VMInstanceVO vm : reorderedVMList) { for (VMInstanceVO vm : reorderedVMList) {
ServiceOfferingVO vmOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId());
if (_itMgr.isRootVolumeOnLocalStorage(vm.getId())) { if (_itMgr.isRootVolumeOnLocalStorage(vm.getId())) {
if (logger.isDebugEnabled()){ if (logger.isDebugEnabled()){
logger.debug("Skipping HA on vm " + vm + ", because it uses local storage. Its fate is tied to the host."); logger.debug("Skipping HA on vm " + vm + ", because it uses local storage. Its fate is tied to the host.");

View File

@ -85,6 +85,9 @@ public interface HighAvailabilityDao extends GenericDao<HaWorkVO, Long> {
List<HaWorkVO> listPendingHaWorkForVm(long vmId); List<HaWorkVO> listPendingHaWorkForVm(long vmId);
List<HaWorkVO> listPendingMigrationsForVm(long vmId); List<HaWorkVO> listPendingMigrationsForVm(long vmId);
List<HaWorkVO> listPendingHAWorkForHost(long hostId);
int expungeByVmList(List<Long> vmIds, Long batchSize); int expungeByVmList(List<Long> vmIds, Long batchSize);
void markPendingWorksAsInvestigating(); void markPendingWorksAsInvestigating();
void markServerPendingWorksAsInvestigating(long managementServerId); void markServerPendingWorksAsInvestigating(long managementServerId);

View File

@ -260,6 +260,19 @@ public class HighAvailabilityDaoImpl extends GenericDaoBase<HaWorkVO, Long> impl
return update(vo, sc); return update(vo, sc);
} }
@Override
public List<HaWorkVO> listPendingHAWorkForHost(long hostId) {
SearchBuilder<HaWorkVO> sb = createSearchBuilder();
sb.and("hostId", sb.entity().getHostId(), Op.EQ);
sb.and("type", sb.entity().getWorkType(), Op.EQ);
sb.and("step", sb.entity().getStep(), Op.NIN);
SearchCriteria<HaWorkVO> sc = sb.create();
sc.setParameters("hostId", hostId);
sc.setParameters("type", WorkType.HA);
sc.setParameters("step", Step.Done, Step.Cancelled, Step.Error);
return listBy(sc);
}
@Override @Override
public int expungeByVmList(List<Long> vmIds, Long batchSize) { public int expungeByVmList(List<Long> vmIds, Long batchSize) {
if (CollectionUtils.isEmpty(vmIds)) { if (CollectionUtils.isEmpty(vmIds)) {

View File

@ -749,8 +749,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
boolean isWindows; boolean isWindows;
Long hostId; Long hostId;
String networkCidr; String networkCidr;
String macAddress;
public VmIpAddrFetchThread(long vmId, String vmUuid, long nicId, String instanceName, boolean windows, Long hostId, String networkCidr) { public VmIpAddrFetchThread(long vmId, long nicId, String instanceName, boolean windows, Long hostId, String networkCidr, String macAddress) {
this.vmId = vmId; this.vmId = vmId;
this.vmUuid = vmUuid; this.vmUuid = vmUuid;
this.nicId = nicId; this.nicId = nicId;
@ -758,11 +759,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
this.isWindows = windows; this.isWindows = windows;
this.hostId = hostId; this.hostId = hostId;
this.networkCidr = networkCidr; this.networkCidr = networkCidr;
this.macAddress = macAddress;
} }
@Override @Override
protected void runInContext() { protected void runInContext() {
GetVmIpAddressCommand cmd = new GetVmIpAddressCommand(vmName, networkCidr, isWindows); GetVmIpAddressCommand cmd = new GetVmIpAddressCommand(vmName, networkCidr, isWindows, macAddress);
boolean decrementCount = true; boolean decrementCount = true;
NicVO nic = _nicDao.findById(nicId); NicVO nic = _nicDao.findById(nicId);
@ -2435,9 +2437,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
private void loadVmDetailsInMapForExternalDhcpIp() { private void loadVmDetailsInMapForExternalDhcpIp() {
List<NetworkVO> networks = _networkDao.listByGuestType(Network.GuestType.Shared); List<NetworkVO> networks = _networkDao.listByGuestType(Network.GuestType.Shared);
networks.addAll(_networkDao.listByGuestType(Network.GuestType.L2));
for (NetworkVO network: networks) { for (NetworkVO network: networks) {
if(_networkModel.isSharedNetworkWithoutServices(network.getId())) { if (GuestType.L2.equals(network.getGuestType()) || _networkModel.isSharedNetworkWithoutServices(network.getId())) {
List<NicVO> nics = _nicDao.listByNetworkId(network.getId()); List<NicVO> nics = _nicDao.listByNetworkId(network.getId());
for (NicVO nic : nics) { for (NicVO nic : nics) {
@ -2685,9 +2688,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(userVm); VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(userVm);
VirtualMachine vm = vmProfile.getVirtualMachine(); VirtualMachine vm = vmProfile.getVirtualMachine();
boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
_vmIpFetchThreadExecutor.execute(new VmIpAddrFetchThread(vmId, nicId, vmInstance.getInstanceName(),
_vmIpFetchThreadExecutor.execute(new VmIpAddrFetchThread(vmId, vmInstance.getUuid(), nicId, vmInstance.getInstanceName(), isWindows, vm.getHostId(), network.getCidr(), nicVo.getMacAddress()));
isWindows, vm.getHostId(), network.getCidr()));
} }
} catch (Exception e) { } catch (Exception e) {
@ -3352,8 +3354,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
final List<NicVO> nics = _nicDao.listByVmId(vmId); final List<NicVO> nics = _nicDao.listByVmId(vmId);
for (NicVO nic : nics) { for (NicVO nic : nics) {
Network network = _networkModel.getNetwork(nic.getNetworkId()); Network network = _networkModel.getNetwork(nic.getNetworkId());
if (_networkModel.isSharedNetworkWithoutServices(network.getId())) { if (GuestType.L2.equals(network.getGuestType()) || _networkModel.isSharedNetworkWithoutServices(network.getId())) {
logger.debug("Adding vm {} nic {} into vmIdCountMap as part of vm reboot for vm ip fetch ", userVm, nic); logger.debug("Adding vm " +vmId +" nic id "+ nic.getId() +" into vmIdCountMap as part of vm " +
"reboot for vm ip fetch ");
vmIdCountMap.put(nic.getId(), new VmAndCountDetails(nic.getInstanceId(), VmIpFetchTrialMax.value())); vmIdCountMap.put(nic.getId(), new VmAndCountDetails(nic.getInstanceId(), VmIpFetchTrialMax.value()));
} }
} }
@ -5366,7 +5369,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
final List<NicVO> nics = _nicDao.listByVmId(vm.getId()); final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
for (NicVO nic : nics) { for (NicVO nic : nics) {
Network network = _networkModel.getNetwork(nic.getNetworkId()); Network network = _networkModel.getNetwork(nic.getNetworkId());
if (_networkModel.isSharedNetworkWithoutServices(network.getId())) { if (GuestType.L2.equals(network.getGuestType()) || _networkModel.isSharedNetworkWithoutServices(network.getId())) {
vmIdCountMap.put(nic.getId(), new VmAndCountDetails(nic.getInstanceId(), VmIpFetchTrialMax.value())); vmIdCountMap.put(nic.getId(), new VmAndCountDetails(nic.getInstanceId(), VmIpFetchTrialMax.value()));
} }
} }

View File

@ -65,7 +65,6 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.VpcVirtualNetworkApplianceService; import com.cloud.network.VpcVirtualNetworkApplianceService;
import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceManager;
import com.cloud.server.ManagementServer; import com.cloud.server.ManagementServer;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.storage.StorageManager; import com.cloud.storage.StorageManager;
import com.cloud.storage.dao.GuestOSCategoryDao; import com.cloud.storage.dao.GuestOSCategoryDao;
@ -237,7 +236,6 @@ public class HighAvailabilityManagerImplTest {
Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(DataCenterVO.class)); Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(DataCenterVO.class));
Mockito.when(_haDao.findPreviousHA(Mockito.anyLong())).thenReturn(Arrays.asList(Mockito.mock(HaWorkVO.class))); Mockito.when(_haDao.findPreviousHA(Mockito.anyLong())).thenReturn(Arrays.asList(Mockito.mock(HaWorkVO.class)));
Mockito.when(_haDao.persist((HaWorkVO)Mockito.any())).thenReturn(Mockito.mock(HaWorkVO.class)); Mockito.when(_haDao.persist((HaWorkVO)Mockito.any())).thenReturn(Mockito.mock(HaWorkVO.class));
Mockito.when(_serviceOfferingDao.findById(vm1.getServiceOfferingId())).thenReturn(Mockito.mock(ServiceOfferingVO.class));
ConfigKey<Boolean> haEnabled = Mockito.mock(ConfigKey.class); ConfigKey<Boolean> haEnabled = Mockito.mock(ConfigKey.class);
highAvailabilityManager.VmHaEnabled = haEnabled; highAvailabilityManager.VmHaEnabled = haEnabled;

View File

@ -99,9 +99,12 @@ class TestCreateAffinityGroup(cloudstackTestCase):
cls.api_client = cls.testClient.getApiClient() cls.api_client = cls.testClient.getApiClient()
cls.services = Services().services cls.services = Services().services
cls._cleanup = []
#Get Zone, Domain and templates #Get Zone, Domain and templates
cls.rootdomain = get_domain(cls.api_client) cls.rootdomain = get_domain(cls.api_client)
cls.domain = Domain.create(cls.api_client, cls.services["domain"]) cls.domain = Domain.create(cls.api_client, cls.services["domain"])
cls._cleanup.append(cls.domain)
cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests()) cls.zone = get_zone(cls.api_client, cls.testClient.getZoneForTests())
cls.template = get_template( cls.template = get_template(
@ -177,21 +180,11 @@ class TestCreateAffinityGroup(cloudstackTestCase):
self.cleanup = [] self.cleanup = []
def tearDown(self): def tearDown(self):
try: super(TestCreateAffinityGroup, self).tearDownClass()
# #Clean up, terminate the created instance, volumes and snapshots
cleanup_resources(self.apiclient, self.cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
return
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
try: super(TestCreateAffinityGroup, cls).tearDownClass()
#Clean up, terminate the created templates
cls.domain.delete(cls.api_client, cleanup=True)
cleanup_resources(cls.api_client, cls._cleanup)
except Exception as e:
raise Exception("Warning: Exception during cleanup : %s" % e)
def create_aff_grp(self, api_client=None, aff_grp=None, aff_grp_name=None, projectid=None): def create_aff_grp(self, api_client=None, aff_grp=None, aff_grp_name=None, projectid=None):

View File

@ -522,7 +522,7 @@ export default {
this.chartLabels.push(currentLabel) this.chartLabels.push(currentLabel)
if (this.resourceIsVirtualMachine) { if (this.resourceIsVirtualMachine) {
cpuLine.data.push({ timestamp: currentLabel, stat: element.cpuused.split('%')[0] }) cpuLine.data.push({ timestamp: currentLabel, stat: element.cpuused.replace(',', '.').split('%')[0] })
element.memoryusedkbs = element.memorykbs - element.memoryintfreekbs element.memoryusedkbs = element.memorykbs - element.memoryintfreekbs
memFreeLinePercent.data.push({ timestamp: currentLabel, stat: this.calculateMemoryPercentage(false, element.memorykbs, element.memoryintfreekbs) }) memFreeLinePercent.data.push({ timestamp: currentLabel, stat: this.calculateMemoryPercentage(false, element.memorykbs, element.memoryintfreekbs) })

View File

@ -2150,37 +2150,88 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
} }
private void handleNetworkEvent(UsageEventVO event) { private void handleNetworkEvent(UsageEventVO event) {
Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId()); String eventType = event.getType();
long domainId = account.getDomainId(); if (EventTypes.EVENT_NETWORK_DELETE.equals(eventType)) {
if (EventTypes.EVENT_NETWORK_DELETE.equals(event.getType())) { removeNetworkHelperEntry(event);
usageNetworksDao.remove(event.getResourceId(), event.getCreateDate()); } else if (EventTypes.EVENT_NETWORK_CREATE.equals(eventType)) {
} else if (EventTypes.EVENT_NETWORK_CREATE.equals(event.getType())) { createNetworkHelperEntry(event);
logger.debug("Marking existing helper entries for network [{}] as removed.", event.getResourceId()); } else if (EventTypes.EVENT_NETWORK_UPDATE.equals(eventType)) {
usageNetworksDao.remove(event.getResourceId(), event.getCreateDate()); updateNetworkHelperEntry(event);
logger.debug("Creating a helper entry for network [{}].", event.getResourceId());
UsageNetworksVO usageNetworksVO = new UsageNetworksVO(event.getResourceId(), event.getOfferingId(), event.getZoneId(), event.getAccountId(), domainId, Network.State.Allocated.name(), event.getCreateDate(), null);
usageNetworksDao.persist(usageNetworksVO);
} else if (EventTypes.EVENT_NETWORK_UPDATE.equals(event.getType())) {
usageNetworksDao.update(event.getResourceId(), event.getOfferingId(), event.getResourceType());
} else { } else {
logger.error("Unknown event type [{}] in Networks event parser. Skipping it.", event.getType()); logger.error(String.format("Unknown event type [%s] in Networks event parser. Skipping it.", eventType));
} }
} }
private void handleVpcEvent(UsageEventVO event) { private void removeNetworkHelperEntry(UsageEventVO event) {
long networkId = event.getResourceId();
logger.debug(String.format("Removing helper entries of network [%s].", networkId));
usageNetworksDao.remove(networkId, event.getCreateDate());
}
private void createNetworkHelperEntry(UsageEventVO event) {
long networkId = event.getResourceId();
Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId()); Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId());
long domainId = account.getDomainId(); long domainId = account.getDomainId();
if (EventTypes.EVENT_VPC_DELETE.equals(event.getType())) {
usageVpcDao.remove(event.getResourceId(), event.getCreateDate()); List<UsageNetworksVO> entries = usageNetworksDao.listAll(networkId);
} else if (EventTypes.EVENT_VPC_CREATE.equals(event.getType())) { if (!entries.isEmpty()) {
logger.debug("Marking existing helper entries for VPC [{}] as removed.", event.getResourceId()); logger.warn(String.format("Received a NETWORK.CREATE event for a network [%s] that already has helper entries; " +
usageVpcDao.remove(event.getResourceId(), event.getCreateDate()); "therefore, we will not create a new one.", networkId));
logger.debug("Creating a helper entry for VPC [{}].", event.getResourceId()); return;
UsageVpcVO usageVPCVO = new UsageVpcVO(event.getResourceId(), event.getZoneId(), event.getAccountId(), domainId, Vpc.State.Enabled.name(), event.getCreateDate(), null);
usageVpcDao.persist(usageVPCVO);
} else {
logger.error("Unknown event type [{}] in VPC event parser. Skipping it.", event.getType());
} }
logger.debug(String.format("Creating a helper entry for network [%s].", networkId));
UsageNetworksVO usageNetworksVO = new UsageNetworksVO(networkId, event.getOfferingId(), event.getZoneId(),
event.getAccountId(), domainId, Network.State.Allocated.name(), event.getCreateDate(), null);
usageNetworksDao.persist(usageNetworksVO);
}
private void updateNetworkHelperEntry(UsageEventVO event) {
long networkId = event.getResourceId();
Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId());
long domainId = account.getDomainId();
logger.debug(String.format("Marking previous helper entries of network [%s] as removed.", networkId));
usageNetworksDao.remove(networkId, event.getCreateDate());
logger.debug(String.format("Creating an updated helper entry for network [%s].", networkId));
UsageNetworksVO usageNetworksVO = new UsageNetworksVO(networkId, event.getOfferingId(), event.getZoneId(),
event.getAccountId(), domainId, event.getResourceType(), event.getCreateDate(), null);
usageNetworksDao.persist(usageNetworksVO);
}
private void handleVpcEvent(UsageEventVO event) {
String eventType = event.getType();
if (EventTypes.EVENT_VPC_DELETE.equals(eventType)) {
removeVpcHelperEntry(event);
} else if (EventTypes.EVENT_VPC_CREATE.equals(eventType)) {
createVpcHelperEntry(event);
} else {
logger.error(String.format("Unknown event type [%s] in VPC event parser. Skipping it.", eventType));
}
}
private void removeVpcHelperEntry(UsageEventVO event) {
long vpcId = event.getResourceId();
logger.debug(String.format("Removing helper entries of VPC [%s].", vpcId));
usageVpcDao.remove(vpcId, event.getCreateDate());
}
private void createVpcHelperEntry(UsageEventVO event) {
long vpcId = event.getResourceId();
Account account = _accountDao.findByIdIncludingRemoved(event.getAccountId());
long domainId = account.getDomainId();
List<UsageVpcVO> entries = usageVpcDao.listAll(vpcId);
if (!entries.isEmpty()) {
logger.warn(String.format("Active helper entries already exist for VPC [%s]; therefore, we will not create a new one.",
vpcId));
return;
}
logger.debug(String.format("Creating a helper entry for VPC [%s].", vpcId));
UsageVpcVO usageVPCVO = new UsageVpcVO(vpcId, event.getZoneId(), event.getAccountId(), domainId, Vpc.State.Enabled.name(), event.getCreateDate(), null);
usageVpcDao.persist(usageVPCVO);
} }
private class Heartbeat extends ManagedContextRunnable { private class Heartbeat extends ManagedContextRunnable {

View File

@ -28,6 +28,8 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -90,7 +92,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/** /**
* Converts a List of tags to a comma separated list * Converts a List of tags to a comma separated list
* @param tags * @param tagsList
* @return String containing a comma separated list of tags * @return String containing a comma separated list of tags
*/ */
@ -304,4 +306,92 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
return mapResult; return mapResult;
} }
/**
* Converts the comma separated numbers to ranges for any consecutive numbers in the input with numbers (and ranges)
* Eg: "198,200-203,299,300,301,303,304,305,306,307,308,311,197" to "197-198,200-203,299-301,303-308,311"
* @param inputNumbersAndRanges
* @return String containing a converted ranges for any consecutive numbers
*/
public static String numbersToRange(String inputNumbersAndRanges) {
Set<Integer> numberSet = new TreeSet<>();
for (String inputNumber : inputNumbersAndRanges.split(",")) {
inputNumber = inputNumber.trim();
if (inputNumber.contains("-")) {
String[] range = inputNumber.split("-");
if (range.length == 2 && range[0] != null && range[1] != null) {
int start = NumbersUtil.parseInt(range[0], 0);
int end = NumbersUtil.parseInt(range[1], 0);
for (int i = start; i <= end; i++) {
numberSet.add(i);
}
}
} else {
numberSet.add(NumbersUtil.parseInt(inputNumber, 0));
}
}
StringBuilder result = new StringBuilder();
if (!numberSet.isEmpty()) {
List<Integer> numbers = new ArrayList<>(numberSet);
int startNumber = numbers.get(0);
int endNumber = startNumber;
for (int i = 1; i < numbers.size(); i++) {
if (numbers.get(i) == endNumber + 1) {
endNumber = numbers.get(i);
} else {
appendRange(result, startNumber, endNumber);
startNumber = endNumber = numbers.get(i);
}
}
appendRange(result, startNumber, endNumber);
}
return result.toString();
}
private static void appendRange(StringBuilder sb, int startNumber, int endNumber) {
if (sb.length() > 0) {
sb.append(",");
}
if (startNumber == endNumber) {
sb.append(startNumber);
} else {
sb.append(startNumber).append("-").append(endNumber);
}
}
/**
* Converts the comma separated numbers and ranges to numbers
* Eg: "197-198,200-203,299-301,303-308,311" to "197,198,200,201,202,203,299,300,301,303,304,305,306,307,308,311"
* @param inputNumbersAndRanges
* @return String containing a converted numbers
*/
public static String rangeToNumbers(String inputNumbersAndRanges) {
Set<Integer> numberSet = new TreeSet<>();
for (String inputNumber : inputNumbersAndRanges.split(",")) {
inputNumber = inputNumber.trim();
if (inputNumber.contains("-")) {
String[] range = inputNumber.split("-");
int startNumber = Integer.parseInt(range[0]);
int endNumber = Integer.parseInt(range[1]);
for (int i = startNumber; i <= endNumber; i++) {
numberSet.add(i);
}
} else {
numberSet.add(Integer.parseInt(inputNumber));
}
}
StringBuilder result = new StringBuilder();
for (int number : numberSet) {
if (result.length() > 0) {
result.append(",");
}
result.append(number);
}
return result.toString();
}
} }

View File

@ -276,16 +276,18 @@ public class HypervisorHostHelper {
} }
} }
public static String composeCloudNetworkName(String prefix, String vlanId, String svlanId, Integer networkRateMbps, String vSwitchName) { public static String composeCloudNetworkName(String prefix, String vlanId, String svlanId, Integer networkRateMbps, String vSwitchName, VirtualSwitchType vSwitchType) {
StringBuffer sb = new StringBuffer(prefix); StringBuffer sb = new StringBuffer(prefix);
if (vlanId == null || UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId)) { if (vlanId == null || UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId)) {
sb.append(".untagged"); sb.append(".untagged");
} else { } else {
if (vSwitchType != VirtualSwitchType.StandardVirtualSwitch && StringUtils.containsAny(vlanId, ",-")) {
vlanId = com.cloud.utils.StringUtils.numbersToRange(vlanId);
}
sb.append(".").append(vlanId); sb.append(".").append(vlanId);
if (svlanId != null) { if (svlanId != null) {
sb.append(".").append("s" + svlanId); sb.append(".").append("s" + svlanId);
} }
} }
if (networkRateMbps != null && networkRateMbps.intValue() > 0) if (networkRateMbps != null && networkRateMbps.intValue() > 0)
@ -295,7 +297,12 @@ public class HypervisorHostHelper {
sb.append(".").append(VersioningContants.PORTGROUP_NAMING_VERSION); sb.append(".").append(VersioningContants.PORTGROUP_NAMING_VERSION);
sb.append("-").append(vSwitchName); sb.append("-").append(vSwitchName);
return sb.toString(); String networkName = sb.toString();
if (networkName.length() > 80) {
// the maximum limit for a vSwitch name is 80 chars, applies to both standard and distributed virtual switches.
s_logger.warn(String.format("The network name: %s for the vSwitch %s of type %s, exceeds 80 chars", networkName, vSwitchName, vSwitchType));
}
return networkName;
} }
public static Map<String, String> getValidatedVsmCredentials(VmwareContext context) throws Exception { public static Map<String, String> getValidatedVsmCredentials(VmwareContext context) throws Exception {
@ -598,7 +605,7 @@ public class HypervisorHostHelper {
if (vlanId != null) { if (vlanId != null) {
vlanId = vlanId.replace("vlan://", ""); vlanId = vlanId.replace("vlan://", "");
} }
networkName = composeCloudNetworkName(namePrefix, vlanId, secondaryvlanId, networkRateMbps, physicalNetwork); networkName = composeCloudNetworkName(namePrefix, vlanId, secondaryvlanId, networkRateMbps, physicalNetwork, vSwitchType);
if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId) && !StringUtils.containsAny(vlanId, ",-")) { if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId) && !StringUtils.containsAny(vlanId, ",-")) {
createGCTag = true; createGCTag = true;
@ -1173,8 +1180,9 @@ public class HypervisorHostHelper {
if (vlanId == null && vlanRange != null && !vlanRange.isEmpty()) { if (vlanId == null && vlanRange != null && !vlanRange.isEmpty()) {
LOGGER.debug("Creating dvSwitch port vlan-trunk spec with range: " + vlanRange); LOGGER.debug("Creating dvSwitch port vlan-trunk spec with range: " + vlanRange);
VmwareDistributedVirtualSwitchTrunkVlanSpec trunkVlanSpec = new VmwareDistributedVirtualSwitchTrunkVlanSpec(); VmwareDistributedVirtualSwitchTrunkVlanSpec trunkVlanSpec = new VmwareDistributedVirtualSwitchTrunkVlanSpec();
for (final String vlanRangePart : vlanRange.split(",")) { String vlanRangeUpdated = com.cloud.utils.StringUtils.numbersToRange(vlanRange);
if (vlanRangePart == null || vlanRange.isEmpty()) { for (final String vlanRangePart : vlanRangeUpdated.split(",")) {
if (vlanRangePart == null || vlanRangePart.isEmpty()) {
continue; continue;
} }
final NumericRange numericRange = new NumericRange(); final NumericRange numericRange = new NumericRange();
@ -1327,7 +1335,7 @@ public class HypervisorHostHelper {
// No doubt about this, depending on vid=null to avoid lots of code below // No doubt about this, depending on vid=null to avoid lots of code below
vid = null; vid = null;
} else { } else {
networkName = composeCloudNetworkName(namePrefix, vlanId, null, networkRateMbps, vSwitchName); networkName = composeCloudNetworkName(namePrefix, vlanId, null, networkRateMbps, vSwitchName, VirtualSwitchType.StandardVirtualSwitch);
if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId)) { if (vlanId != null && !UNTAGGED_VLAN_NAME.equalsIgnoreCase(vlanId)) {
createGCTag = true; createGCTag = true;

View File

@ -581,7 +581,7 @@ public class HypervisorHostHelperTest {
networkRateMbps = 200; networkRateMbps = 200;
prefix = "cloud.public"; prefix = "cloud.public";
vSwitchName = "vSwitch0"; vSwitchName = "vSwitch0";
String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, svlanId, networkRateMbps, vSwitchName); String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, svlanId, networkRateMbps, vSwitchName, VirtualSwitchType.StandardVirtualSwitch);
assertEquals("cloud.public.100.200.1-vSwitch0", cloudNetworkName); assertEquals("cloud.public.100.200.1-vSwitch0", cloudNetworkName);
} }
@ -591,7 +591,7 @@ public class HypervisorHostHelperTest {
networkRateMbps = null; networkRateMbps = null;
prefix = "cloud.storage"; prefix = "cloud.storage";
vSwitchName = "vSwitch1"; vSwitchName = "vSwitch1";
String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, svlanId, networkRateMbps, vSwitchName); String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, svlanId, networkRateMbps, vSwitchName, VirtualSwitchType.StandardVirtualSwitch);
assertEquals("cloud.storage.untagged.0.1-vSwitch1", cloudNetworkName); assertEquals("cloud.storage.untagged.0.1-vSwitch1", cloudNetworkName);
} }
@ -602,10 +602,60 @@ public class HypervisorHostHelperTest {
networkRateMbps = 512; networkRateMbps = 512;
prefix = "cloud.guest"; prefix = "cloud.guest";
vSwitchName = "vSwitch2"; vSwitchName = "vSwitch2";
String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, svlanId, networkRateMbps, vSwitchName); String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, svlanId, networkRateMbps, vSwitchName, VirtualSwitchType.StandardVirtualSwitch);
assertEquals("cloud.guest.400.s123.512.1-vSwitch2", cloudNetworkName); assertEquals("cloud.guest.400.s123.512.1-vSwitch2", cloudNetworkName);
} }
@Test
public void testComposeCloudNetworkNameVlanRangeGuestTrafficDvSwitch() {
vlanId = "400-500";
networkRateMbps = 512;
prefix = "cloud.guest";
vSwitchName = "dvSwitch0";
String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, null, networkRateMbps, vSwitchName, VirtualSwitchType.VMwareDistributedVirtualSwitch);
assertEquals("cloud.guest.400-500.512.1-dvSwitch0", cloudNetworkName);
}
@Test
public void testComposeCloudNetworkNameVlanNumbersGuestTrafficDvSwitch() {
vlanId = "3001,3002,3003,3004,3005,3006,3007,3008,3009,3010,3011,3012,3013,3014,3015,3016,3017,3018,3019,3020";
networkRateMbps = 512;
prefix = "cloud.guest";
vSwitchName = "dvSwitch0";
String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, null, networkRateMbps, vSwitchName, VirtualSwitchType.VMwareDistributedVirtualSwitch);
assertEquals("cloud.guest.3001-3020.512.1-dvSwitch0", cloudNetworkName);
}
@Test
public void testComposeCloudNetworkNameVlanNumbersAndRangeGuestTrafficDvSwitch() {
vlanId = "3001,3004-3006,3007,3008,3009,3010,3011,3012,3013,3014,3015,3016,3017,3018,3020";
networkRateMbps = 512;
prefix = "cloud.guest";
vSwitchName = "dvSwitch0";
String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, null, networkRateMbps, vSwitchName, VirtualSwitchType.VMwareDistributedVirtualSwitch);
assertEquals("cloud.guest.3001,3004-3018,3020.512.1-dvSwitch0", cloudNetworkName);
}
@Test
public void testComposeCloudNetworkNameUnorderedVlanNumbersAndRangeGuestTrafficDvSwitch() {
vlanId = "3018,3020,3011,3012,3004-3006,3007,3001,3008,3009,3010,3013,3014,3015,3016,3017";
networkRateMbps = 512;
prefix = "cloud.guest";
vSwitchName = "dvSwitch0";
String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, null, networkRateMbps, vSwitchName, VirtualSwitchType.VMwareDistributedVirtualSwitch);
assertEquals("cloud.guest.3001,3004-3018,3020.512.1-dvSwitch0", cloudNetworkName);
}
@Test
public void testComposeCloudNetworkNameOverlappingVlanNumbersAndRangeGuestTrafficDvSwitch() {
vlanId = "3018,3020,3011,3012,3004-3006,3007,3001,3008,3009,3010,3013,3014,3015,3016,3017,3005-3008";
networkRateMbps = 512;
prefix = "cloud.guest";
vSwitchName = "dvSwitch0";
String cloudNetworkName = HypervisorHostHelper.composeCloudNetworkName(prefix, vlanId, null, networkRateMbps, vSwitchName, VirtualSwitchType.VMwareDistributedVirtualSwitch);
assertEquals("cloud.guest.3001,3004-3018,3020.512.1-dvSwitch0", cloudNetworkName);
}
@Test @Test
public void testOvfDomRewriter() { public void testOvfDomRewriter() {
final String ovfString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + final String ovfString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +