This commit is contained in:
Pearl Dsilva 2025-05-12 13:12:09 +05:30
commit 1e5d133033
44 changed files with 866 additions and 151 deletions

View File

@ -236,7 +236,7 @@ jobs:
- name: Install Python dependencies - name: Install Python dependencies
run: | run: |
python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycrypto mock flask netaddr pylint pycodestyle six astroid pynose python3 -m pip install --user --upgrade urllib3 lxml paramiko nose texttable ipmisim pyopenssl pycryptodome mock flask netaddr pylint pycodestyle six astroid pynose
- name: Install jacoco dependencies - name: Install jacoco dependencies
run: | run: |

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

@ -247,6 +247,11 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
protected final ConfigKey<Boolean> CheckTxnBeforeSending = new ConfigKey<>("Developer", Boolean.class, "check.txn.before.sending.agent.commands", "false", protected final ConfigKey<Boolean> CheckTxnBeforeSending = new ConfigKey<>("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 {
@ -1093,9 +1098,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

@ -595,7 +595,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks) == null) { if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks) == null) {
offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks, offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks,
"Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null, "Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null,
defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false); defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null, null, false);
} }
//#6 - default vpc offering with no LB service //#6 - default vpc offering with no LB service

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

@ -75,3 +75,6 @@ CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'va
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW'); CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW'); CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
-- Re-apply VPC: update default network offering for vpc tier to conserve_mode=1 (#8309)
UPDATE `cloud`.`network_offerings` SET conserve_mode=1 WHERE name='DefaultIsolatedNetworkOfferingForVpcNetworks';

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;
}
}
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

@ -150,16 +150,16 @@ public class LibvirtRestoreBackupCommandWrapper extends CommandWrapper<RestoreBa
String mountDirectory = String.format("%s.%s",BACKUP_TEMP_FILE_PREFIX , randomChars); String mountDirectory = String.format("%s.%s",BACKUP_TEMP_FILE_PREFIX , randomChars);
try { try {
mountDirectory = Files.createTempDirectory(mountDirectory).toString(); mountDirectory = Files.createTempDirectory(mountDirectory).toString();
String mountOpts = null;
if (Objects.nonNull(mountOptions)) {
mountOpts = mountOptions;
if ("cifs".equals(backupRepoType)) {
mountOpts += ",nobrl";
}
}
String mount = String.format(MOUNT_COMMAND, backupRepoType, backupRepoAddress, mountDirectory); String mount = String.format(MOUNT_COMMAND, backupRepoType, backupRepoAddress, mountDirectory);
if (Objects.nonNull(mountOpts)) { if ("cifs".equals(backupRepoType)) {
mount += " -o " + mountOpts; if (Objects.isNull(mountOptions) || mountOptions.trim().isEmpty()) {
mountOptions = "nobrl";
} else {
mountOptions += ",nobrl";
}
}
if (Objects.nonNull(mountOptions) && !mountOptions.trim().isEmpty()) {
mount += " -o " + mountOptions;
} }
Script.runSimpleBashScript(mount); Script.runSimpleBashScript(mount);
} catch (Exception e) { } catch (Exception e) {

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,78 @@
// 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 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> {
@Override
public Answer execute(final CleanupVMCommand command, final CitrixResourceBase citrixResourceBase) {
if (citrixResourceBase.isDestroyHaltedVms()) {
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);
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);
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

@ -412,13 +412,14 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
if (err == null && needResize) { if (err == null && needResize) {
err = notifyQemuForTheNewSize(data, err, vol, payload); err = notifyQemuForTheNewSize(data, err, vol, payload);
} }
if (err != null) { if (err != null) {
// try restoring volume to its initial size // try restoring volume to its initial size
SpApiResponse response = StorPoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn); SpApiResponse response = StorPoolUtil.volumeUpdate(name, oldSize, true, oldMaxIops, conn);
if (response.getError() != null) { if (response.getError() != null) {
logger.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, response.getError())); logger.debug(String.format("Could not resize StorPool volume %s back to its original size. Error: %s", name, response.getError()));
} }
} else {
updateVolumeWithTheNewSize(vol, payload);
} }
} catch (Exception e) { } catch (Exception e) {
logger.debug("sending resize command failed", e); logger.debug("sending resize command failed", e);
@ -427,6 +428,17 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return err; return err;
} }
private void updateVolumeWithTheNewSize(VolumeObject vol, ResizeVolumePayload payload) {
vol.setSize(payload.newSize);
vol.update();
if (payload.newMaxIops != null) {
VolumeVO volume = volumeDao.findById(vol.getId());
volume.setMaxIops(payload.newMaxIops);
volumeDao.update(volume.getId(), volume);
}
updateStoragePool(vol.getPoolId(), payload.newSize - vol.getSize());
}
private String notifyQemuForTheNewSize(DataObject data, String err, VolumeObject vol, ResizeVolumePayload payload) private String notifyQemuForTheNewSize(DataObject data, String err, VolumeObject vol, ResizeVolumePayload payload)
throws StorageUnavailableException { throws StorageUnavailableException {
StoragePool pool = (StoragePool)data.getDataStore(); StoragePool pool = (StoragePool)data.getDataStore();
@ -455,11 +467,24 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
} }
SpApiResponse resp = new SpApiResponse(); SpApiResponse resp = new SpApiResponse();
if (tier != null || template != null) { if (tier != null || template != null) {
resp = updateVolumeByStorPoolQoS(payload, conn, name, tier, template);
} else {
resp = updateVolumeByOffering(vol, payload, conn, name);
}
if (resp.getError() != null) {
err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError());
}
return err;
}
private static SpApiResponse updateVolumeByStorPoolQoS(ResizeVolumePayload payload, SpConnectionDesc conn, String name, String tier, String template) {
Map<String, String> tags = StorPoolHelper.addStorPoolTags(null, null, null, null, tier); Map<String, String> tags = StorPoolHelper.addStorPoolTags(null, null, null, null, tier);
StorPoolVolumeDef spVolume = new StorPoolVolumeDef(name, payload.newSize, tags, null, null, template, null, null, StorPoolVolumeDef spVolume = new StorPoolVolumeDef(name, payload.newSize, tags, null, null, template, null, null,
payload.shrinkOk); payload.shrinkOk);
resp = StorPoolUtil.volumeUpdate(spVolume, conn); return StorPoolUtil.volumeUpdate(spVolume, conn);
} else { }
private static SpApiResponse updateVolumeByOffering(VolumeObject vol, ResizeVolumePayload payload, SpConnectionDesc conn, String name) {
long maxIops = payload.newMaxIops == null ? Long.valueOf(0) : payload.newMaxIops; long maxIops = payload.newMaxIops == null ? Long.valueOf(0) : payload.newMaxIops;
StorPoolVolumeDef spVolume = new StorPoolVolumeDef(name, payload.newSize, null, null, maxIops, null, null, null, StorPoolVolumeDef spVolume = new StorPoolVolumeDef(name, payload.newSize, null, null, maxIops, null, null, null,
@ -467,23 +492,7 @@ public class StorPoolPrimaryDataStoreDriver implements PrimaryDataStoreDriver {
StorPoolUtil.spLog( StorPoolUtil.spLog(
"StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s, maxIops=%s", "StorpoolPrimaryDataStoreDriverImpl.resize: name=%s, uuid=%s, oldSize=%d, newSize=%s, shrinkOk=%s, maxIops=%s",
name, vol.getUuid(), vol.getSize(), payload.newSize, payload.shrinkOk, maxIops); name, vol.getUuid(), vol.getSize(), payload.newSize, payload.shrinkOk, maxIops);
return StorPoolUtil.volumeUpdate(spVolume, conn);
resp = StorPoolUtil.volumeUpdate(spVolume, conn);
}
if (resp.getError() != null) {
err = String.format("Could not resize StorPool volume %s. Error: %s", name, resp.getError());
} else {
vol.setSize(payload.newSize);
vol.update();
if (payload.newMaxIops != null) {
VolumeVO volume = volumeDao.findById(vol.getId());
volume.setMaxIops(payload.newMaxIops);
volumeDao.update(volume.getId(), volume);
}
updateStoragePool(vol.getPoolId(), payload.newSize - vol.getSize());
}
return err;
} }
@Override @Override

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

@ -47,19 +47,14 @@ class TestGuestOS(cloudstackTestCase):
cls.hypervisor = cls.get_hypervisor_type() cls.hypervisor = cls.get_hypervisor_type()
@classmethod
def setUp(self): def setUp(self):
self.apiclient = self.testClient.getApiClient() self.apiclient = self.testClient.getApiClient()
#build cleanup list #build cleanup list
self.cleanup = [] self.cleanup = []
@classmethod
def tearDown(self): def tearDown(self):
try: super(TestGuestOS, self).tearDown()
cleanup_resources(self.apiclient, self.cleanup)
except Exception as e:
self.debug("Warning! Exception in tearDown: %s" % e)
@classmethod @classmethod
def get_hypervisor_type(cls): def get_hypervisor_type(cls):
@ -95,6 +90,7 @@ class TestGuestOS(cloudstackTestCase):
osdisplayname="testCentOS", osdisplayname="testCentOS",
oscategoryid=os_category.id oscategoryid=os_category.id
) )
self.cleanup.append(self.guestos1)
list_guestos = GuestOS.list(self.apiclient, id=self.guestos1.id, listall=True) list_guestos = GuestOS.list(self.apiclient, id=self.guestos1.id, listall=True)
self.assertNotEqual( self.assertNotEqual(
len(list_guestos), len(list_guestos),
@ -112,6 +108,7 @@ class TestGuestOS(cloudstackTestCase):
self.apiclient, self.apiclient,
id=self.guestos1.id id=self.guestos1.id
) )
self.cleanup.remove(self.guestos1)
@attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False) @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
def test_CRUD_operations_guest_OS_mapping(self): def test_CRUD_operations_guest_OS_mapping(self):
@ -127,6 +124,7 @@ class TestGuestOS(cloudstackTestCase):
osdisplayname="testCentOS", osdisplayname="testCentOS",
oscategoryid=os_category.id oscategoryid=os_category.id
) )
self.cleanup.append(self.guestos1)
if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]: if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]:
raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware") raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware")
@ -138,6 +136,7 @@ class TestGuestOS(cloudstackTestCase):
hypervisorversion=self.hypervisor.hypervisorversion, hypervisorversion=self.hypervisor.hypervisorversion,
osnameforhypervisor="testOSMappingName" osnameforhypervisor="testOSMappingName"
) )
self.cleanup.append(self.guestosmapping1)
list_guestos_mapping = GuestOsMapping.list(self.apiclient, id=self.guestosmapping1.id, listall=True) list_guestos_mapping = GuestOsMapping.list(self.apiclient, id=self.guestosmapping1.id, listall=True)
self.assertNotEqual( self.assertNotEqual(
@ -156,11 +155,13 @@ class TestGuestOS(cloudstackTestCase):
self.apiclient, self.apiclient,
id=self.guestosmapping1.id id=self.guestosmapping1.id
) )
self.cleanup.remove(self.guestosmapping1)
GuestOS.remove( GuestOS.remove(
self.apiclient, self.apiclient,
id=self.guestos1.id id=self.guestos1.id
) )
self.cleanup.remove(self.guestos1)
@attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False) @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
def test_guest_OS_mapping_check_with_hypervisor(self): def test_guest_OS_mapping_check_with_hypervisor(self):
@ -176,6 +177,7 @@ class TestGuestOS(cloudstackTestCase):
osdisplayname="testOSname1", osdisplayname="testOSname1",
oscategoryid=os_category.id oscategoryid=os_category.id
) )
self.cleanup.append(self.guestos1)
if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]: if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]:
raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware") raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware")
@ -193,6 +195,7 @@ class TestGuestOS(cloudstackTestCase):
osnameforhypervisor=testosname, osnameforhypervisor=testosname,
osmappingcheckenabled=True osmappingcheckenabled=True
) )
self.cleanup.append(self.guestosmapping1)
list_guestos_mapping = GuestOsMapping.list(self.apiclient, id=self.guestosmapping1.id, listall=True) list_guestos_mapping = GuestOsMapping.list(self.apiclient, id=self.guestosmapping1.id, listall=True)
self.assertNotEqual( self.assertNotEqual(
@ -211,11 +214,13 @@ class TestGuestOS(cloudstackTestCase):
self.apiclient, self.apiclient,
id=self.guestosmapping1.id id=self.guestosmapping1.id
) )
self.cleanup.remove(self.guestosmapping1)
GuestOS.remove( GuestOS.remove(
self.apiclient, self.apiclient,
id=self.guestos1.id id=self.guestos1.id
) )
self.cleanup.remove(self.guestos1)
@attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False) @attr(tags=['advanced', 'simulator', 'basic', 'sg'], required_hardware=False)
def test_guest_OS_mapping_check_with_hypervisor_failure(self): def test_guest_OS_mapping_check_with_hypervisor_failure(self):
@ -231,6 +236,7 @@ class TestGuestOS(cloudstackTestCase):
osdisplayname="testOSname2", osdisplayname="testOSname2",
oscategoryid=os_category.id oscategoryid=os_category.id
) )
self.cleanup.append(self.guestos1)
if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]: if self.hypervisor.hypervisor.lower() not in ["xenserver", "vmware"]:
raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware") raise unittest.SkipTest("OS name check with hypervisor is supported only on XenServer and VMware")
@ -246,10 +252,12 @@ class TestGuestOS(cloudstackTestCase):
osnameforhypervisor=testosname, osnameforhypervisor=testosname,
osmappingcheckenabled=True osmappingcheckenabled=True
) )
self.cleanup.append(self.guestosmapping1)
GuestOsMapping.remove( GuestOsMapping.remove(
self.apiclient, self.apiclient,
id=self.guestosmapping1.id id=self.guestosmapping1.id
) )
self.cleanup.remove(self.guestosmapping1)
self.fail("Since os mapping name is wrong, this API should fail") self.fail("Since os mapping name is wrong, this API should fail")
except CloudstackAPIException as e: except CloudstackAPIException as e:
self.debug("Addition guest OS mapping failed as expected %s " % e) self.debug("Addition guest OS mapping failed as expected %s " % e)
@ -257,4 +265,5 @@ class TestGuestOS(cloudstackTestCase):
self.apiclient, self.apiclient,
id=self.guestos1.id id=self.guestos1.id
) )
self.cleanup.remove(self.guestos1)
return return

View File

@ -457,7 +457,7 @@ class TestHostHA(cloudstackTestCase):
retry_interval = 1 + (pingInterval * pingTimeout / 10) retry_interval = 1 + (pingInterval * pingTimeout / 10)
res, _ = wait_until(retry_interval, 20, removeFakeMgmtServer, self.getFakeMsRunId()) res, _ = wait_until(retry_interval, 100, removeFakeMgmtServer, self.getFakeMsRunId())
if not res: if not res:
self.fail("Management server failed to turn down or remove fake mgmt server") self.fail("Management server failed to turn down or remove fake mgmt server")

View File

@ -544,7 +544,7 @@ class TestOutOfBandManagement(cloudstackTestCase):
retry_interval = 1 + (pingInterval * pingTimeout / 10) retry_interval = 1 + (pingInterval * pingTimeout / 10)
res, _ = wait_until(retry_interval, 10, removeFakeMgmtServer, self.getFakeMsRunId()) res, _ = wait_until(retry_interval, 100, removeFakeMgmtServer, self.getFakeMsRunId())
if not res: if not res:
self.fail("Management server failed to turn down or remove fake mgmt server") self.fail("Management server failed to turn down or remove fake mgmt server")

View File

@ -4576,6 +4576,7 @@ class Project:
def __init__(self, items): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@classmethod @classmethod
def create(cls, apiclient, services, account=None, domainid=None, userid=None, accountid=None): def create(cls, apiclient, services, account=None, domainid=None, userid=None, accountid=None):
"""Create project""" """Create project"""
@ -6720,7 +6721,7 @@ class GuestOSCategory:
class GuestOS: class GuestOS:
"""Manage Guest OS""" """Manage Guest OS"""
def __init__(self, items, services): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@classmethod @classmethod
@ -6735,7 +6736,7 @@ class GuestOS:
if details is not None: if details is not None:
cmd.details = details cmd.details = details
return (apiclient.addGuestOs(cmd)) return GuestOS(apiclient.addGuestOs(cmd).__dict__)
@classmethod @classmethod
def remove(cls, apiclient, id): def remove(cls, apiclient, id):
@ -6772,10 +6773,13 @@ class GuestOS:
return (apiclient.listOsTypes(cmd)) return (apiclient.listOsTypes(cmd))
def delete(self, apiclient):
self.remove(apiclient, self.id)
class GuestOsMapping: class GuestOsMapping:
"""Manage Guest OS Mappings""" """Manage Guest OS Mappings"""
def __init__(self, items, services): def __init__(self, items):
self.__dict__.update(items) self.__dict__.update(items)
@classmethod @classmethod
@ -6793,7 +6797,7 @@ class GuestOsMapping:
if forced is not None: if forced is not None:
cmd.forced = forced cmd.forced = forced
return (apiclient.addGuestOsMapping(cmd)) return GuestOsMapping(apiclient.addGuestOsMapping(cmd).__dict__)
@classmethod @classmethod
def remove(cls, apiclient, id): def remove(cls, apiclient, id):
@ -6837,6 +6841,9 @@ class GuestOsMapping:
return (apiclient.listGuestOsMapping(cmd)) return (apiclient.listGuestOsMapping(cmd))
def delete(self, apiclient):
self.remove(apiclient, self.id)
class VMSchedule: class VMSchedule:
def __init__(self, items): def __init__(self, items):

View File

@ -1,4 +1,5 @@
{ {
"message.delete.account.not.disabled": "Please disable the account before attempting to delete it.",
"alert.service.domainrouter": "Domain router", "alert.service.domainrouter": "Domain router",
"error.dedicate.bgp.peer.failed":"Failed to dedicate BGP peer", "error.dedicate.bgp.peer.failed":"Failed to dedicate BGP peer",
"error.dedicate.cluster.failed": "Failed to dedicate cluster.", "error.dedicate.cluster.failed": "Failed to dedicate cluster.",
@ -942,6 +943,7 @@
"label.endipv6": "IPv6 end IP", "label.endipv6": "IPv6 end IP",
"label.endpoint": "Endpoint", "label.endpoint": "Endpoint",
"label.endport": "End port", "label.endport": "End port",
"label.enter.account.name": "Enter the account name",
"label.enter.code": "Enter 2FA code to verify", "label.enter.code": "Enter 2FA code to verify",
"label.enter.static.pin": "Enter static PIN to verify", "label.enter.static.pin": "Enter static PIN to verify",
"label.enter.token": "Enter token", "label.enter.token": "Enter token",
@ -1970,6 +1972,7 @@
"label.rolename": "Role", "label.rolename": "Role",
"label.roles": "Roles", "label.roles": "Roles",
"label.roletype": "Role Type", "label.roletype": "Role Type",
"label.rolepermissiontab.searchbar": "Search Rule",
"label.root.certificate": "Root certificate", "label.root.certificate": "Root certificate",
"label.root.disk.size": "Root disk size (GB)", "label.root.disk.size": "Root disk size (GB)",
"label.rootdisk": "ROOT disk", "label.rootdisk": "ROOT disk",
@ -2980,7 +2983,11 @@
"message.dedicating.host": "Dedicating host...", "message.dedicating.host": "Dedicating host...",
"message.dedicating.pod": "Dedicating pod...", "message.dedicating.pod": "Dedicating pod...",
"message.dedicating.zone": "Dedicating zone...", "message.dedicating.zone": "Dedicating zone...",
"message.delete.account": "Please confirm that you want to delete this Account.", "message.delete.account.confirm": "Please confirm that you want to delete this account by entering the name of the account below.",
"message.delete.account.failed": "Delete account failed",
"message.delete.account.processing": "Deleting account",
"message.delete.account.success": "Successfully deleted account",
"message.delete.account.warning": "Deleting this account will delete all of the instances, volumes and snapshots associated with the account.",
"message.delete.acl.processing": "Removing ACL rule...", "message.delete.acl.processing": "Removing ACL rule...",
"message.delete.acl.rule": "Remove ACL rule", "message.delete.acl.rule": "Remove ACL rule",
"message.delete.acl.rule.failed": "Failed to remove ACL rule.", "message.delete.acl.rule.failed": "Failed to remove ACL rule.",
@ -3073,6 +3080,7 @@
"message.enabling.security.group.provider": "Enabling security group provider", "message.enabling.security.group.provider": "Enabling security group provider",
"message.enter.valid.nic.ip": "Please enter a valid IP address for NIC", "message.enter.valid.nic.ip": "Please enter a valid IP address for NIC",
"message.error.access.key": "Please enter access key.", "message.error.access.key": "Please enter access key.",
"message.error.account.delete.name.mismatch": "Name entered doesn't match the account name.",
"message.error.add.guest.network": "Either IPv4 fields or IPv6 fields need to be filled when adding a guest Network.", "message.error.add.guest.network": "Either IPv4 fields or IPv6 fields need to be filled when adding a guest Network.",
"message.error.add.interface.static.route": "Adding interface Static Route failed", "message.error.add.interface.static.route": "Adding interface Static Route failed",
"message.error.add.logical.router": "Adding Logical Router failed", "message.error.add.logical.router": "Adding Logical Router failed",

View File

@ -1390,6 +1390,7 @@
"label.rolename": "Fun\u00e7\u00e3o", "label.rolename": "Fun\u00e7\u00e3o",
"label.roles": "Fun\u00e7\u00f5es", "label.roles": "Fun\u00e7\u00f5es",
"label.roletype": "Tipo de fun\u00e7\u00e3o", "label.roletype": "Tipo de fun\u00e7\u00e3o",
"label.rolepermissiontab.searchbar": "Pesquisa de regras",
"label.root.certificate": "Certificado ra\u00edz", "label.root.certificate": "Certificado ra\u00edz",
"label.root.disk.size": "Tamanho do disco ra\u00edz (GB)", "label.root.disk.size": "Tamanho do disco ra\u00edz (GB)",
"label.rootdisk": "Disco ra\u00edz", "label.rootdisk": "Disco ra\u00edz",

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

@ -75,7 +75,6 @@ function generateRouterMap (section) {
icon: child.icon, icon: child.icon,
docHelp: vueProps.$applyDocHelpMappings(child.docHelp), docHelp: vueProps.$applyDocHelpMappings(child.docHelp),
permission: child.permission, permission: child.permission,
getApiToCall: child.getApiToCall,
resourceType: child.resourceType, resourceType: child.resourceType,
filters: child.filters, filters: child.filters,
params: child.params ? child.params : {}, params: child.params ? child.params : {},

View File

@ -228,11 +228,10 @@ export default {
message: 'message.delete.account', message: 'message.delete.account',
dataView: true, dataView: true,
disabled: (record, store) => { disabled: (record, store) => {
return record.id !== 'undefined' && store.userInfo.accountid === record.id return store.userInfo.accountid === record?.id
}, },
groupAction: true,
popup: true, popup: true,
groupMap: (selection) => { return selection.map(x => { return { id: x } }) } component: shallowRef(defineAsyncComponent(() => import('@/views/iam/DeleteAccountWrapper.vue')))
} }
] ]
} }

View File

@ -29,8 +29,7 @@ export default {
title: 'label.instances', title: 'label.instances',
icon: 'cloud-server-outlined', icon: 'cloud-server-outlined',
docHelp: 'adminguide/virtual_machines.html', docHelp: 'adminguide/virtual_machines.html',
permission: ['listVirtualMachines', 'listVirtualMachinesMetrics'], permission: ['listVirtualMachinesMetrics'],
getApiToCall: () => store.getters.metrics ? 'listVirtualMachinesMetrics' : 'listVirtualMachines',
resourceType: 'UserVm', resourceType: 'UserVm',
params: () => { params: () => {
var params = { details: 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp,backoff' } var params = { details: 'group,nics,secgrp,tmpl,servoff,diskoff,iso,volume,affgrp,backoff' }

View File

@ -26,8 +26,8 @@ export default {
permission: ['listClustersMetrics'], permission: ['listClustersMetrics'],
searchFilters: ['name', 'zoneid', 'podid', 'arch', 'hypervisor'], searchFilters: ['name', 'zoneid', 'podid', 'arch', 'hypervisor'],
columns: () => { columns: () => {
const fields = ['name', 'state', 'allocationstate', 'clustertype', 'arch', 'hypervisortype', 'hosts'] const fields = ['name', 'allocationstate', 'clustertype', 'arch', 'hypervisortype']
const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal', 'drsimbalance'] const metricsFields = ['state', 'hosts', 'cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal', 'drsimbalance']
if (store.getters.metrics) { if (store.getters.metrics) {
fields.push(...metricsFields) fields.push(...metricsFields)
} }

View File

@ -33,12 +33,10 @@ export default {
params: { type: 'routing' }, params: { type: 'routing' },
columns: () => { columns: () => {
const fields = [ const fields = [
'name', 'state', 'resourcestate', 'ipaddress', 'name', 'state', 'resourcestate', 'ipaddress', 'arch', 'hypervisor',
'arch', 'hypervisor', 'instances', { field: 'systeminstances', customTitle: 'system.vms' }, 'version'
{ field: 'systeminstances', customTitle: 'system.vms' },
'powerstate', 'version'
] ]
const metricsFields = ['cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'networkread', 'networkwrite'] const metricsFields = ['instances', 'powerstate', 'cpunumber', 'cputotalghz', 'cpuusedghz', 'cpuallocatedghz', 'memorytotalgb', 'memoryusedgb', 'memoryallocatedgb', 'networkread', 'networkwrite']
if (store.getters.metrics) { if (store.getters.metrics) {
fields.push(...metricsFields) fields.push(...metricsFields)
} }

View File

@ -26,8 +26,8 @@ export default {
permission: ['listZonesMetrics'], permission: ['listZonesMetrics'],
searchFilters: ['name', 'domainid', 'tags'], searchFilters: ['name', 'domainid', 'tags'],
columns: () => { columns: () => {
const fields = ['name', 'allocationstate', 'type', 'networktype', 'clusters'] const fields = ['name', 'allocationstate', 'type', 'networktype']
const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal'] const metricsFields = ['clusters', 'cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal']
if (store.getters.metrics) { if (store.getters.metrics) {
fields.push(...metricsFields) fields.push(...metricsFields)
} }

View File

@ -829,7 +829,12 @@ export default {
} }
if (this.$route && this.$route.meta && this.$route.meta.permission) { if (this.$route && this.$route.meta && this.$route.meta.permission) {
this.apiName = (this.$route.meta.getApiToCall && this.$route.meta.getApiToCall()) || this.$route.meta.permission[0] this.apiName = this.$route.meta.permission[0]
if (!store.getters.metrics && !this.dataView &&
this.apiName && this.apiName.endsWith('Metrics') &&
store.getters.apis[this.apiName.replace(/Metrics$/, '')]) {
this.apiName = this.apiName.replace(/Metrics$/, '')
}
if (this.$route.meta.columns) { if (this.$route.meta.columns) {
const columns = this.$route.meta.columns const columns = this.$route.meta.columns
if (columns && typeof columns === 'function') { if (columns && typeof columns === 'function') {

View File

@ -0,0 +1,176 @@
// 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.
<template>
<a-form
class="form"
:ref="formRef"
:model="form"
:rules="rules"
layout="vertical"
@finish="handleSubmit"
v-ctrl-enter="handleSubmit"
>
<div style="margin-bottom: 10px">
<a-alert type="warning">
<template #message>
<div v-html="$t('message.delete.account.warning')"></div>
</template>
</a-alert>
</div>
<div style="margin-bottom: 10px">
<a-alert>
<template #message>
<div v-html="$t('message.delete.account.confirm')"></div>
</template>
</a-alert>
</div>
<a-form-item name="name" ref="name">
<a-input
v-model:value="form.name"
:placeholder="$t('label.enter.account.name')"
style="width: 100%"/>
</a-form-item>
<p v-if="error" class="error">{{ error }}</p>
<div :span="24" class="actions">
<a-button @click="closeModal">{{ $t('label.cancel') }}</a-button>
<a-button type="primary" ref="submit" @click="handleSubmit">{{ $t('label.ok') }}</a-button>
</div>
</a-form>
</template>
<script>
import { ref, reactive } from 'vue'
import { api } from '@/api'
export default {
name: 'DeleteAccount',
props: {
resource: {
type: Object,
required: true
}
},
data () {
return {
error: '',
isDeleting: false
}
},
created () {
this.initForm()
},
methods: {
initForm () {
this.formRef = ref()
this.form = reactive({})
this.rules = reactive({
name: [{ required: true, message: this.$t('label.required') }]
})
},
closeModal () {
this.$emit('close-action')
},
handleSubmit (e) {
e.preventDefault()
if (this.isDeleting) return // Prevent double submission
this.formRef.value.validate().then(async () => {
if (this.form.name !== this.resource.name) {
this.error = `${this.$t('message.error.account.delete.name.mismatch')}`
return
}
if (this.hasActiveResources) {
return
}
this.isDeleting = true
// Store the account ID and name before we close the modal
const accountId = this.resource.id
const accountName = this.resource.name
// Close the modal first
this.closeModal()
// Immediately navigate to the accounts page to avoid "unable to find account" errors
this.$router.push({ path: '/account' })
.then(() => {
// After successful navigation, start the deletion job
api('deleteAccount', {
id: accountId
}).then(response => {
this.$pollJob({
jobId: response.deleteaccountresponse.jobid,
title: this.$t('label.action.delete.account'),
description: accountId,
successMessage: `${this.$t('message.delete.account.success')} - ${accountName}`,
errorMessage: `${this.$t('message.delete.account.failed')} - ${accountName}`,
loadingMessage: `${this.$t('message.delete.account.processing')} - ${accountName}`,
catchMessage: this.$t('error.fetching.async.job.result')
})
}).catch(error => {
this.$notifyError(error)
this.isDeleting = false
})
})
.catch(err => {
console.error('Navigation failed:', err)
this.isDeleting = false
// If navigation fails, still try to delete the account
// but don't navigate afterwards
api('deleteAccount', {
id: accountId
}).then(response => {
this.$pollJob({
jobId: response.deleteaccountresponse.jobid,
title: this.$t('label.action.delete.account'),
description: accountId,
successMessage: `${this.$t('message.delete.account.success')} - ${accountName}`,
errorMessage: `${this.$t('message.delete.account.failed')} - ${accountName}`,
loadingMessage: `${this.$t('message.delete.account.processing')} - ${accountName}`,
catchMessage: this.$t('error.fetching.async.job.result')
})
}).catch(error => {
this.$notifyError(error)
})
})
}).catch((error) => {
this.formRef.value.scrollToField(error.errorFields[0].name)
})
}
}
}
</script>
<style lang="scss" scoped>
.form {
width: 80vw;
@media (min-width: 500px) {
width: 400px;
}
}
.actions {
display: flex;
justify-content: flex-end;
margin-top: 20px;
button {
&:not(:last-child) {
margin-right: 10px;
}
}
}
.error {
color: red;
margin-top: 10px;
}
</style>

View File

@ -0,0 +1,74 @@
// 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.
<template>
<div>
<!-- Show error message if account is not disabled -->
<template v-if="resource.state !== 'disabled'">
<div style="margin-bottom: 10px">
<a-alert type="error">
<template #message>
<div>{{ $t('message.delete.account.not.disabled') }}</div>
</template>
</a-alert>
</div>
<div :span="24" class="actions">
<a-button type="primary" @click="closeModal">{{ $t('label.ok') }}</a-button>
</div>
</template>
<!-- Show delete form if account is disabled -->
<delete-account
v-else
:resource="resource"
@close-action="closeModal" />
</div>
</template>
<script>
import DeleteAccount from './DeleteAccount.vue'
export default {
name: 'DeleteAccountWrapper',
components: {
DeleteAccount
},
props: {
resource: {
type: Object,
required: true
}
},
methods: {
closeModal () {
this.$emit('close-action')
}
}
}
</script>
<style lang="scss" scoped>
.actions {
display: flex;
justify-content: flex-end;
margin-top: 20px;
button {
&:not(:last-child) {
margin-right: 10px;
}
}
}
</style>

View File

@ -18,15 +18,24 @@
<template> <template>
<loading-outlined v-if="loadingTable" class="main-loading-spinner" /> <loading-outlined v-if="loadingTable" class="main-loading-spinner" />
<div v-else> <div v-else>
<div style="width: 100%; display: flex; margin-bottom: 10px"> <div style="width: 100%; display: flex; margin-bottom: 20px">
<a-button type="dashed" @click="exportRolePermissions" style="width: 100%"> <a-button type="dashed" @click="exportRolePermissions" style="width: 100%">
<template #icon><download-outlined /></template> <template #icon><download-outlined /></template>
{{ $t('label.export.rules') }} {{ $t('label.export.rules') }}
</a-button> </a-button>
</div> </div>
<a-input-search
v-model:value="searchRule"
:placeholder="$t('label.rolepermissiontab.searchbar')"
background-color="gray"
style="width: 100%; margin-bottom: 10px; display: inline-block"
enter-button
@search="searchRulePermission"
/>
<div v-if="updateTable" class="loading-overlay"> <div v-if="updateTable" class="loading-overlay">
<loading-outlined /> <loading-outlined />
</div> </div>
<div <div
class="rules-list ant-list ant-list-bordered" class="rules-list ant-list ant-list-bordered"
:class="{'rules-list--overflow-hidden' : updateTable}" > :class="{'rules-list--overflow-hidden' : updateTable}" >
@ -137,7 +146,8 @@ export default {
newRuleDescription: '', newRuleDescription: '',
newRuleSelectError: false, newRuleSelectError: false,
drag: false, drag: false,
apis: [] apis: [],
searchRule: ''
} }
}, },
created () { created () {
@ -172,6 +182,7 @@ export default {
if (!this.resource.id) return if (!this.resource.id) return
api('listRolePermissions', { roleid: this.resource.id }).then(response => { api('listRolePermissions', { roleid: this.resource.id }).then(response => {
this.rules = response.listrolepermissionsresponse.rolepermission this.rules = response.listrolepermissionsresponse.rolepermission
this.totalRules = this.rules
}).catch(error => { }).catch(error => {
this.$notifyError(error) this.$notifyError(error)
}).finally(() => { }).finally(() => {
@ -258,6 +269,19 @@ export default {
hiddenElement.download = this.resource.name + '_' + this.resource.type + '.csv' hiddenElement.download = this.resource.name + '_' + this.resource.type + '.csv'
hiddenElement.click() hiddenElement.click()
hiddenElement.remove() hiddenElement.remove()
},
searchRulePermission (searchValue) {
searchValue = searchValue.toLowerCase()
if (!searchValue) {
this.rules = this.totalRules
} else {
this.updateTable = true
const searchRules = this.totalRules.filter((rule) => rule.rule.toLowerCase().includes(searchValue))
this.rules = searchRules
setTimeout(() => {
this.updateTable = false
})
}
} }
} }
} }

View File

@ -389,6 +389,9 @@ export default {
values.virtualmachineid = this.resource.id values.virtualmachineid = this.resource.id
values.zoneid = this.resource.zoneid values.zoneid = this.resource.zoneid
} }
if (this.customDiskOffering) {
values.size = values.size.trim()
}
if (this.createVolumeFromSnapshot) { if (this.createVolumeFromSnapshot) {
values.snapshotid = this.resource.id values.snapshotid = this.resource.id
} }

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.
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\"?>" +