Merge branch '4.22'

This commit is contained in:
Suresh Kumar Anaparti 2026-01-30 21:15:58 +05:30
commit a5b6bc3be6
No known key found for this signature in database
GPG Key ID: D7CEAE3A9E71D0AA
22 changed files with 513 additions and 178 deletions

View File

@ -19,6 +19,7 @@ package org.apache.cloudstack.api.command.user.vm;
import java.util.Objects;
import java.util.stream.Stream;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.api.ACL;
import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
@ -39,7 +40,6 @@ import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.uservm.UserVm;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VirtualMachine;
@APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts an Instance based on a service offering, disk offering, and Template.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class},

View File

@ -21,6 +21,7 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import com.cloud.utils.StringUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.Dhcp;
import com.cloud.vm.VirtualMachine;
@ -44,7 +45,6 @@ import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.vm.lease.VMLeaseManager;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Collection;
import java.util.HashMap;
@ -174,6 +174,9 @@ public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction,
/////////////////////////////////////////////////////
public String getDisplayName() {
if (StringUtils.isBlank(displayName)) {
displayName = name;
}
return displayName;
}

View File

@ -48,9 +48,7 @@ public class SnapshotObjectTO extends DownloadableObjectTO implements DataTO {
private Long physicalSize = (long) 0;
private long accountId;
public SnapshotObjectTO() {
}
@Override

View File

@ -606,7 +606,7 @@ public class SystemVmTemplateRegistration {
template.setBits(64);
template.setAccountId(Account.ACCOUNT_ID_SYSTEM);
template.setUrl(details.getUrl());
template.setChecksum(details.getChecksum());
template.setChecksum(DigestHelper.prependAlgorithm(details.getChecksum()));
template.setEnablePassword(false);
template.setDisplayText(details.getName());
template.setFormat(details.getFormat());
@ -1079,7 +1079,7 @@ public class SystemVmTemplateRegistration {
protected void updateTemplateUrlChecksumAndGuestOsId(VMTemplateVO templateVO,
MetadataTemplateDetails templateDetails) {
templateVO.setUrl(templateDetails.getUrl());
templateVO.setChecksum(templateDetails.getChecksum());
templateVO.setChecksum(DigestHelper.prependAlgorithm(templateDetails.getChecksum()));
GuestOSVO guestOS = guestOSDao.findOneByDisplayName(templateDetails.getGuestOs());
if (guestOS != null) {
templateVO.setGuestOSId(guestOS.getId());

View File

@ -398,13 +398,16 @@ public class SnapshotObject implements SnapshotInfo {
if (answer instanceof CreateObjectAnswer) {
SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CreateObjectAnswer)answer).getData();
snapshotStore.setInstallPath(snapshotTO.getPath());
if (snapshotTO.getPhysicalSize() != null && snapshotTO.getPhysicalSize() > 0L) {
snapshotStore.setPhysicalSize(snapshotTO.getPhysicalSize());
}
snapshotStoreDao.update(snapshotStore.getId(), snapshotStore);
} else if (answer instanceof CopyCmdAnswer) {
SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CopyCmdAnswer)answer).getNewData();
snapshotStore.setInstallPath(snapshotTO.getPath());
if (snapshotTO.getPhysicalSize() != null) {
// For S3 delta snapshot, physical size is currently not set
snapshotStore.setPhysicalSize(snapshotTO.getPhysicalSize());
snapshotStore.setPhysicalSize(snapshotTO.getPhysicalSize());
}
if (snapshotTO.getParentSnapshotPath() == null) {
snapshotStore.setParentSnapshotId(0L);

View File

@ -210,6 +210,29 @@ class HyperVManager:
power_state = "poweroff"
succeed({"status": "success", "power_state": power_state})
def statuses(self):
command = 'Get-VM | Select-Object Name, State | ConvertTo-Json'
output = self.run_ps(command)
if not output or output.strip() in ("", "null"):
vms = []
else:
try:
vms = json.loads(output)
except json.JSONDecodeError:
fail("Failed to parse VM status output: " + output)
power_state = {}
if isinstance(vms, dict):
vms = [vms]
for vm in vms:
state = vm["State"].strip().lower()
if state == "running":
power_state[vm["Name"]] = "poweron"
elif state == "off":
power_state[vm["Name"]] = "poweroff"
else:
power_state[vm["Name"]] = "unknown"
succeed({"status": "success", "power_state": power_state})
def delete(self):
try:
self.run_ps_int(f'Remove-VM -Name "{self.data["vmname"]}" -Force')
@ -286,6 +309,7 @@ def main():
"reboot": manager.reboot,
"delete": manager.delete,
"status": manager.status,
"statuses": manager.statuses,
"getconsole": manager.get_console,
"suspend": manager.suspend,
"resume": manager.resume,

View File

@ -64,7 +64,7 @@ parse_json() {
token="${host_token:-$extension_token}"
secret="${host_secret:-$extension_secret}"
check_required_fields vm_internal_name url user token secret node
check_required_fields url user token secret node
}
urlencode() {
@ -206,6 +206,10 @@ prepare() {
create() {
if [[ -z "$vm_name" ]]; then
if [[ -z "$vm_internal_name" ]]; then
echo '{"error":"Missing required fields: vm_internal_name"}'
exit 1
fi
vm_name="$vm_internal_name"
fi
validate_name "VM" "$vm_name"
@ -331,71 +335,102 @@ get_node_host() {
echo "$host"
}
get_console() {
check_required_fields node vmid
get_console() {
check_required_fields node vmid
local api_resp port ticket
if ! api_resp="$(call_proxmox_api POST "/nodes/${node}/qemu/${vmid}/vncproxy")"; then
echo "$api_resp" | jq -c '{status:"error", error:(.errors.curl // (.errors|tostring))}'
exit 1
fi
local api_resp port ticket
if ! api_resp="$(call_proxmox_api POST "/nodes/${node}/qemu/${vmid}/vncproxy")"; then
echo "$api_resp" | jq -c '{status:"error", error:(.errors.curl // (.errors|tostring))}'
exit 1
fi
port="$(echo "$api_resp" | jq -re '.data.port // empty' 2>/dev/null || true)"
ticket="$(echo "$api_resp" | jq -re '.data.ticket // empty' 2>/dev/null || true)"
port="$(echo "$api_resp" | jq -re '.data.port // empty' 2>/dev/null || true)"
ticket="$(echo "$api_resp" | jq -re '.data.ticket // empty' 2>/dev/null || true)"
if [[ -z "$port" || -z "$ticket" ]]; then
jq -n --arg raw "$api_resp" \
'{status:"error", error:"Proxmox response missing port/ticket", upstream:$raw}'
exit 1
fi
if [[ -z "$port" || -z "$ticket" ]]; then
jq -n --arg raw "$api_resp" \
'{status:"error", error:"Proxmox response missing port/ticket", upstream:$raw}'
exit 1
fi
# Derive host from nodes network info
local host
host="$(get_node_host)"
if [[ -z "$host" ]]; then
jq -n --arg msg "Could not determine host IP for node $node" \
'{status:"error", error:$msg}'
exit 1
fi
# Derive host from nodes network info
local host
host="$(get_node_host)"
if [[ -z "$host" ]]; then
jq -n --arg msg "Could not determine host IP for node $node" \
'{status:"error", error:$msg}'
exit 1
fi
jq -n \
--arg host "$host" \
--arg port "$port" \
--arg password "$ticket" \
--argjson passwordonetimeuseonly true \
'{
status: "success",
message: "Console retrieved",
console: {
host: $host,
port: $port,
password: $password,
passwordonetimeuseonly: $passwordonetimeuseonly,
protocol: "vnc"
}
}'
}
jq -n \
--arg host "$host" \
--arg port "$port" \
--arg password "$ticket" \
--argjson passwordonetimeuseonly true \
'{
status: "success",
message: "Console retrieved",
console: {
host: $host,
port: $port,
password: $password,
passwordonetimeuseonly: $passwordonetimeuseonly,
protocol: "vnc"
}
}'
}
statuses() {
local response
response=$(call_proxmox_api GET "/nodes/${node}/qemu")
if [[ -z "$response" ]]; then
echo '{"status":"error","message":"empty response from Proxmox API"}'
return 1
fi
if ! echo "$response" | jq empty >/dev/null 2>&1; then
echo '{"status":"error","message":"invalid JSON response from Proxmox API"}'
return 1
fi
echo "$response" | jq -c '
def map_state(s):
if s=="running" then "poweron"
elif s=="stopped" then "poweroff"
else "unknown" end;
{
status: "success",
power_state: (
.data
| map(select(.template != 1))
| map({ ( (.name // (.vmid|tostring)) ): map_state(.status) })
| add // {}
)
}'
}
list_snapshots() {
snapshot_response=$(call_proxmox_api GET "/nodes/${node}/qemu/${vmid}/snapshot")
echo "$snapshot_response" | jq '
def to_date:
if . == "-" then "-"
elif . == null then "-"
else (. | tonumber | strftime("%Y-%m-%d %H:%M:%S"))
end;
def to_date:
if . == "-" then "-"
elif . == null then "-"
else (. | tonumber | strftime("%Y-%m-%d %H:%M:%S"))
end;
{
status: "success",
printmessage: "true",
message: [.data[] | {
name: .name,
snaptime: ((.snaptime // "-") | to_date),
description: .description,
parent: (.parent // "-"),
vmstate: (.vmstate // "-")
}]
}
{
status: "success",
printmessage: "true",
message: [.data[] | {
name: .name,
snaptime: ((.snaptime // "-") | to_date),
description: .description,
parent: (.parent // "-"),
vmstate: (.vmstate // "-")
}]
}
'
}
@ -463,9 +498,9 @@ parse_json "$parameters" || exit 1
cleanup_vm=0
cleanup() {
if (( cleanup_vm == 1 )); then
execute_and_wait DELETE "/nodes/${node}/qemu/${vmid}"
fi
if (( cleanup_vm == 1 )); then
execute_and_wait DELETE "/nodes/${node}/qemu/${vmid}"
fi
}
trap cleanup EXIT
@ -492,6 +527,9 @@ case $action in
status)
status
;;
statuses)
statuses
;;
getconsole)
get_console
;;

View File

@ -71,6 +71,7 @@ import com.cloud.agent.api.StartCommand;
import com.cloud.agent.api.StopAnswer;
import com.cloud.agent.api.StopCommand;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.ExternalProvisioner;
@ -128,7 +129,7 @@ public class ExternalPathPayloadProvisioner extends ManagerBase implements Exter
private ExecutorService payloadCleanupExecutor;
private ScheduledExecutorService payloadCleanupScheduler;
private static final List<String> TRIVIAL_ACTIONS = Arrays.asList(
"status"
"status", "statuses"
);
@Override
@ -456,7 +457,7 @@ public class ExternalPathPayloadProvisioner extends ManagerBase implements Exter
@Override
public Map<String, HostVmStateReportEntry> getHostVmStateReport(long hostId, String extensionName,
String extensionRelativePath) {
final Map<String, HostVmStateReportEntry> vmStates = new HashMap<>();
Map<String, HostVmStateReportEntry> vmStates = new HashMap<>();
String extensionPath = getExtensionCheckedPath(extensionName, extensionRelativePath);
if (StringUtils.isEmpty(extensionPath)) {
return vmStates;
@ -466,14 +467,20 @@ public class ExternalPathPayloadProvisioner extends ManagerBase implements Exter
logger.error("Host with ID: {} not found", hostId);
return vmStates;
}
Map<String, Map<String, String>> accessDetails =
extensionsManager.getExternalAccessDetails(host, null);
vmStates = getVmPowerStates(host, accessDetails, extensionName, extensionPath);
if (vmStates != null) {
logger.debug("Found {} VMs on the host {}", vmStates.size(), host);
return vmStates;
}
vmStates = new HashMap<>();
List<UserVmVO> allVms = _uservmDao.listByHostId(hostId);
allVms.addAll(_uservmDao.listByLastHostId(hostId));
if (CollectionUtils.isEmpty(allVms)) {
logger.debug("No VMs found for the {}", host);
return vmStates;
}
Map<String, Map<String, String>> accessDetails =
extensionsManager.getExternalAccessDetails(host, null);
for (UserVmVO vm: allVms) {
VirtualMachine.PowerState powerState = getVmPowerState(vm, accessDetails, extensionName, extensionPath);
vmStates.put(vm.getInstanceName(), new HostVmStateReportEntry(powerState, "host-" + hostId));
@ -714,7 +721,7 @@ public class ExternalPathPayloadProvisioner extends ManagerBase implements Exter
return getPowerStateFromString(response);
}
try {
JsonObject jsonObj = new JsonParser().parse(response).getAsJsonObject();
JsonObject jsonObj = JsonParser.parseString(response).getAsJsonObject();
String powerState = jsonObj.has("power_state") ? jsonObj.get("power_state").getAsString() : null;
return getPowerStateFromString(powerState);
} catch (Exception e) {
@ -724,7 +731,7 @@ public class ExternalPathPayloadProvisioner extends ManagerBase implements Exter
}
}
private VirtualMachine.PowerState getVmPowerState(UserVmVO userVmVO, Map<String, Map<String, String>> accessDetails,
protected VirtualMachine.PowerState getVmPowerState(UserVmVO userVmVO, Map<String, Map<String, String>> accessDetails,
String extensionName, String extensionPath) {
VirtualMachineTO virtualMachineTO = getVirtualMachineTO(userVmVO);
accessDetails.put(ApiConstants.VIRTUAL_MACHINE, virtualMachineTO.getExternalDetails());
@ -740,6 +747,46 @@ public class ExternalPathPayloadProvisioner extends ManagerBase implements Exter
}
return parsePowerStateFromResponse(userVmVO, result.second());
}
protected Map<String, HostVmStateReportEntry> getVmPowerStates(Host host,
Map<String, Map<String, String>> accessDetails, String extensionName, String extensionPath) {
Map<String, Object> modifiedDetails = loadAccessDetails(accessDetails, null);
logger.debug("Trying to get VM power statuses from the external system for {}", host);
Pair<Boolean, String> result = getInstanceStatusesOnExternalSystem(extensionName, extensionPath,
host.getName(), modifiedDetails, AgentManager.Wait.value());
if (!result.first()) {
logger.warn("Failure response received while trying to fetch the power statuses for {} : {}",
host, result.second());
return null;
}
if (StringUtils.isBlank(result.second())) {
logger.warn("Empty response while trying to fetch VM power statuses for host: {}", host);
return null;
}
try {
JsonObject jsonObj = JsonParser.parseString(result.second()).getAsJsonObject();
if (!jsonObj.has("status") || !"success".equalsIgnoreCase(jsonObj.get("status").getAsString())) {
logger.warn("Invalid status in response while trying to fetch VM power statuses for host: {}: {}",
host, result.second());
return null;
}
if (!jsonObj.has("power_state") || !jsonObj.get("power_state").isJsonObject()) {
logger.warn("Missing or invalid power_state in response for host: {}: {}", host, result.second());
return null;
}
JsonObject powerStates = jsonObj.getAsJsonObject("power_state");
Map<String, HostVmStateReportEntry> states = new HashMap<>();
for (Map.Entry<String, com.google.gson.JsonElement> entry : powerStates.entrySet()) {
VirtualMachine.PowerState powerState = getPowerStateFromString(entry.getValue().getAsString());
states.put(entry.getKey(), new HostVmStateReportEntry(powerState, "host-" + host.getId()));
}
return states;
} catch (Exception e) {
logger.warn("Failed to parse VM power statuses response for host: {}: {}", host, e.getMessage());
return null;
}
}
public Pair<Boolean, String> prepareExternalProvisioningInternal(String extensionName, String filename,
String vmUUID, Map<String, Object> accessDetails, int wait) {
return executeExternalCommand(extensionName, "prepare", accessDetails, wait,
@ -783,6 +830,12 @@ public class ExternalPathPayloadProvisioner extends ManagerBase implements Exter
String.format("Failed to get the instance power status %s on external system", vmUUID), filename);
}
public Pair<Boolean, String> getInstanceStatusesOnExternalSystem(String extensionName, String filename,
String hostName, Map<String, Object> accessDetails, int wait) {
return executeExternalCommand(extensionName, "statuses", accessDetails, wait,
String.format("Failed to get the %s instances power status on external system", hostName), filename);
}
public Pair<Boolean, String> getInstanceConsoleOnExternalSystem(String extensionName, String filename,
String vmUUID, Map<String, Object> accessDetails, int wait) {
return executeExternalCommand(extensionName, "getconsole", accessDetails, wait,

View File

@ -79,6 +79,7 @@ import com.cloud.agent.api.StartCommand;
import com.cloud.agent.api.StopAnswer;
import com.cloud.agent.api.StopCommand;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
@ -761,6 +762,37 @@ public class ExternalPathPayloadProvisionerTest {
assertNull(result);
}
@Test
public void getVmPowerStatesReturnsValidStatesWhenResponseIsSuccessful() {
Host host = mock(Host.class);
when(host.getId()).thenReturn(1L);
when(host.getName()).thenReturn("test-host");
Map<String, Map<String, String>> accessDetails = new HashMap<>();
doReturn(new Pair<>(true, "{\"status\":\"success\",\"power_state\":{\"vm1\":\"PowerOn\",\"vm2\":\"PowerOff\"}}"))
.when(provisioner).getInstanceStatusesOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
Map<String, HostVmStateReportEntry> result = provisioner.getVmPowerStates(host, accessDetails, "test-extension", "test-path");
assertNotNull(result);
assertEquals(2, result.size());
assertEquals(VirtualMachine.PowerState.PowerOn, result.get("vm1").getState());
assertEquals(VirtualMachine.PowerState.PowerOff, result.get("vm2").getState());
}
@Test
public void getVmPowerStatesReturnsNullWhenResponseIsFailure() {
Host host = mock(Host.class);
when(host.getName()).thenReturn("test-host");
Map<String, Map<String, String>> accessDetails = new HashMap<>();
doReturn(new Pair<>(false, "Error")).when(provisioner)
.getInstanceStatusesOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
Map<String, HostVmStateReportEntry> result = provisioner.getVmPowerStates(host, accessDetails, "test-extension", "test-path");
assertNull(result);
}
@Test
public void getVirtualMachineTOReturnsValidTOWhenVmIsNotNull() {
VirtualMachine vm = mock(VirtualMachine.class);
@ -986,4 +1018,120 @@ public class ExternalPathPayloadProvisionerTest {
String result = provisioner.getExtensionConfigureError("test-extension", null);
assertEquals("Extension: test-extension not configured", result);
}
@Test
public void getVmPowerStatesReturnsNullWhenResponseIsEmpty() {
Host host = mock(Host.class);
when(host.getName()).thenReturn("test-host");
Map<String, Map<String, String>> accessDetails = new HashMap<>();
doReturn(new Pair<>(true, "")).when(provisioner)
.getInstanceStatusesOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
Map<String, HostVmStateReportEntry> result = provisioner.getVmPowerStates(host, accessDetails, "test-extension", "test-path");
assertNull(result);
}
@Test
public void getVmPowerStatesReturnsNullWhenResponseHasInvalidStatus() {
Host host = mock(Host.class);
when(host.getName()).thenReturn("test-host");
Map<String, Map<String, String>> accessDetails = new HashMap<>();
doReturn(new Pair<>(true, "{\"status\":\"failure\"}")).when(provisioner)
.getInstanceStatusesOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
Map<String, HostVmStateReportEntry> result = provisioner.getVmPowerStates(host, accessDetails, "test-extension", "test-path");
assertNull(result);
}
@Test
public void getVmPowerStatesReturnsNullWhenPowerStateIsMissing() {
Host host = mock(Host.class);
when(host.getName()).thenReturn("test-host");
Map<String, Map<String, String>> accessDetails = new HashMap<>();
doReturn(new Pair<>(true, "{\"status\":\"success\"}")).when(provisioner)
.getInstanceStatusesOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
Map<String, HostVmStateReportEntry> result = provisioner.getVmPowerStates(host, accessDetails, "test-extension", "test-path");
assertNull(result);
}
@Test
public void getVmPowerStatesReturnsNullWhenResponseIsMalformed() {
Host host = mock(Host.class);
when(host.getName()).thenReturn("test-host");
Map<String, Map<String, String>> accessDetails = new HashMap<>();
doReturn(new Pair<>(true, "{status:success")).when(provisioner)
.getInstanceStatusesOnExternalSystem(anyString(), anyString(), anyString(), anyMap(), anyInt());
Map<String, HostVmStateReportEntry> result = provisioner.getVmPowerStates(host, accessDetails, "test-extension", "test-path");
assertNull(result);
}
@Test
public void getInstanceStatusesOnExternalSystemReturnsSuccessWhenCommandExecutesSuccessfully() {
doReturn(new Pair<>(true, "success")).when(provisioner)
.executeExternalCommand(eq("test-extension"), eq("statuses"), anyMap(), eq(30), anyString(), eq("test-file"));
Pair<Boolean, String> result = provisioner.getInstanceStatusesOnExternalSystem(
"test-extension", "test-file", "test-host", new HashMap<>(), 30);
assertTrue(result.first());
assertEquals("success", result.second());
}
@Test
public void getInstanceStatusesOnExternalSystemReturnsFailureWhenCommandFails() {
doReturn(new Pair<>(false, "error")).when(provisioner)
.executeExternalCommand(eq("test-extension"), eq("statuses"), anyMap(), eq(30), anyString(), eq("test-file"));
Pair<Boolean, String> result = provisioner.getInstanceStatusesOnExternalSystem(
"test-extension", "test-file", "test-host", new HashMap<>(), 30);
assertFalse(result.first());
assertEquals("error", result.second());
}
@Test
public void getInstanceStatusesOnExternalSystemHandlesEmptyResponse() {
doReturn(new Pair<>(true, "")).when(provisioner)
.executeExternalCommand(eq("test-extension"), eq("statuses"), anyMap(), eq(30), anyString(), eq("test-file"));
Pair<Boolean, String> result = provisioner.getInstanceStatusesOnExternalSystem(
"test-extension", "test-file", "test-host", new HashMap<>(), 30);
assertTrue(result.first());
assertEquals("", result.second());
}
@Test
public void getInstanceStatusesOnExternalSystemHandlesNullResponse() {
doReturn(new Pair<>(true, null)).when(provisioner)
.executeExternalCommand(eq("test-extension"), eq("statuses"), anyMap(), eq(30), anyString(), eq("test-file"));
Pair<Boolean, String> result = provisioner.getInstanceStatusesOnExternalSystem(
"test-extension", "test-file", "test-host", new HashMap<>(), 30);
assertTrue(result.first());
assertNull(result.second());
}
@Test
public void getInstanceStatusesOnExternalSystemHandlesInvalidFilePath() {
doReturn(new Pair<>(false, "File not found")).when(provisioner)
.executeExternalCommand(eq("test-extension"), eq("statuses"), anyMap(), eq(30), anyString(), eq("invalid-file"));
Pair<Boolean, String> result = provisioner.getInstanceStatusesOnExternalSystem(
"test-extension", "invalid-file", "test-host", new HashMap<>(), 30);
assertFalse(result.first());
assertEquals("File not found", result.second());
}
}

View File

@ -1971,7 +1971,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
for (final String ifNamePattern : ifNamePatterns) {
commonPattern.append("|(").append(ifNamePattern).append(".*)");
}
if(fname.matches(commonPattern.toString())) {
if (fname.matches(commonPattern.toString())) {
return true;
}
return false;
@ -2498,11 +2498,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final Pattern pattern = Pattern.compile("(\\D+)(\\d+)(\\D*)(\\d*)(\\D*)(\\d*)");
final Matcher matcher = pattern.matcher(pif);
LOGGER.debug("getting broadcast uri for pif " + pif + " and bridge " + brName);
if(matcher.find()) {
if (matcher.find()) {
if (brName.startsWith("brvx")){
return BroadcastDomainType.Vxlan.toUri(matcher.group(2)).toString();
}
else{
} else {
if (!matcher.group(6).isEmpty()) {
return BroadcastDomainType.Vlan.toUri(matcher.group(6)).toString();
} else if (!matcher.group(4).isEmpty()) {
@ -3742,7 +3741,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
} else if (volume.getType() == Volume.Type.DATADISK) {
final KVMPhysicalDisk physicalDisk = storagePoolManager.getPhysicalDisk(store.getPoolType(), store.getUuid(), data.getPath());
final KVMStoragePool pool = physicalDisk.getPool();
if(StoragePoolType.RBD.equals(pool.getType())) {
if (StoragePoolType.RBD.equals(pool.getType())) {
final int devId = volume.getDiskSeq().intValue();
final String device = mapRbdDevice(physicalDisk);
if (device != null) {
@ -5200,7 +5199,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
}
for (int i = 0; i < memoryStats.length; i++) {
if(memoryStats[i].getTag() == UNUSEDMEMORY) {
if (memoryStats[i].getTag() == UNUSEDMEMORY) {
freeMemory = memoryStats[i].getValue();
break;
}
@ -5772,12 +5771,12 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return hypervisorType;
}
public String mapRbdDevice(final KVMPhysicalDisk disk){
public String mapRbdDevice(final KVMPhysicalDisk disk) {
final KVMStoragePool pool = disk.getPool();
//Check if rbd image is already mapped
final String[] splitPoolImage = disk.getPath().split("/");
String device = Script.runSimpleBashScript("rbd showmapped | grep \""+splitPoolImage[0]+"[ ]*"+splitPoolImage[1]+"\" | grep -o \"[^ ]*[ ]*$\"");
if(device == null) {
if (device == null) {
//If not mapped, map and return mapped device
Script.runSimpleBashScript("rbd map " + disk.getPath() + " --id " + pool.getAuthUserName());
device = Script.runSimpleBashScript("rbd showmapped | grep \""+splitPoolImage[0]+"[ ]*"+splitPoolImage[1]+"\" | grep -o \"[^ ]*[ ]*$\"");

View File

@ -55,6 +55,9 @@ import javax.xml.xpath.XPathFactory;
import com.cloud.agent.api.Command;
import com.cloud.hypervisor.kvm.resource.LibvirtXMLParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.cloudstack.agent.directdownload.DirectDownloadAnswer;
import org.apache.cloudstack.agent.directdownload.DirectDownloadCommand;
import org.apache.cloudstack.direct.download.DirectDownloadHelper;
@ -365,6 +368,16 @@ public class KVMStorageProcessor implements StorageProcessor {
newTemplate.setPath(primaryVol.getName());
newTemplate.setSize(primaryVol.getSize());
newTemplate.setFormat(getFormat(primaryPool.getType()));
if (List.of(
StoragePoolType.RBD,
StoragePoolType.PowerFlex,
StoragePoolType.Linstor,
StoragePoolType.FiberChannel).contains(primaryPool.getType())) {
newTemplate.setFormat(ImageFormat.RAW);
} else {
newTemplate.setFormat(ImageFormat.QCOW2);
}
data = newTemplate;
} else if (destData.getObjectType() == DataObjectType.VOLUME) {
final VolumeObjectTO volumeObjectTO = new VolumeObjectTO();
@ -752,7 +765,7 @@ public class KVMStorageProcessor implements StorageProcessor {
templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator");
try(FileOutputStream templFo = new FileOutputStream(templateProp);){
try (FileOutputStream templFo = new FileOutputStream(templateProp);) {
templFo.write(templateContent.getBytes());
templFo.flush();
} catch (final IOException e) {
@ -817,11 +830,9 @@ public class KVMStorageProcessor implements StorageProcessor {
if (srcData instanceof VolumeObjectTO) {
isVolume = true;
}
else if (srcData instanceof SnapshotObjectTO) {
} else if (srcData instanceof SnapshotObjectTO) {
isVolume = false;
}
else {
} else {
return new CopyCmdAnswer("unsupported object type");
}
@ -887,8 +898,7 @@ public class KVMStorageProcessor implements StorageProcessor {
if (isVolume) {
templateContent += "volume.name=" + dateFormat.format(date) + System.getProperty("line.separator");
}
else {
} else {
templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator");
}
@ -926,8 +936,7 @@ public class KVMStorageProcessor implements StorageProcessor {
} catch (Exception ex) {
if (isVolume) {
logger.debug("Failed to create template from volume: ", ex);
}
else {
} else {
logger.debug("Failed to create template from snapshot: ", ex);
}
@ -1088,7 +1097,7 @@ public class KVMStorageProcessor implements StorageProcessor {
q.convert(srcFile, destFile);
final File snapFile = new File(snapshotFile);
if(snapFile.exists()) {
if (snapFile.exists()) {
size = snapFile.length();
}
@ -1121,7 +1130,7 @@ public class KVMStorageProcessor implements StorageProcessor {
return new CopyCmdAnswer(result);
}
final File snapFile = new File(snapshotDestPath + "/" + descName);
if(snapFile.exists()){
if (snapFile.exists()) {
size = snapFile.length();
}
}
@ -1460,7 +1469,7 @@ public class KVMStorageProcessor implements StorageProcessor {
if (resource.getHypervisorType() == Hypervisor.HypervisorType.LXC) {
final String device = resource.mapRbdDevice(attachingDisk);
if (device != null) {
logger.debug("RBD device on host is: "+device);
logger.debug("RBD device on host is: " + device);
attachingDisk.setPath(device);
}
}
@ -1487,11 +1496,11 @@ public class KVMStorageProcessor implements StorageProcessor {
}
diskdef.setSerial(serial);
if (attachingPool.getType() == StoragePoolType.RBD) {
if(resource.getHypervisorType() == Hypervisor.HypervisorType.LXC){
if (resource.getHypervisorType() == Hypervisor.HypervisorType.LXC) {
// For LXC, map image to host and then attach to Vm
final String device = resource.mapRbdDevice(attachingDisk);
if (device != null) {
logger.debug("RBD device on host is: "+device);
logger.debug("RBD device on host is: " + device);
diskdef.defBlockBasedDisk(device, devId, busT);
} else {
throw new InternalErrorException("Error while mapping disk "+attachingDisk.getPath()+" on host");
@ -1561,7 +1570,7 @@ public class KVMStorageProcessor implements StorageProcessor {
if ((iopsWriteRateMaxLength != null) && (iopsWriteRateMaxLength > 0)) {
diskdef.setIopsWriteRateMaxLength(iopsWriteRateMaxLength);
}
if(cacheMode != null) {
if (cacheMode != null) {
diskdef.setCacheMode(DiskDef.DiskCacheMode.valueOf(cacheMode.toUpperCase()));
}
@ -1748,7 +1757,7 @@ public class KVMStorageProcessor implements StorageProcessor {
}
final VolumeObjectTO newVol = new VolumeObjectTO();
if(vol != null) {
if (vol != null) {
newVol.setPath(vol.getName());
if (vol.getQemuEncryptFormat() != null) {
newVol.setEncryptFormat(vol.getQemuEncryptFormat().toString());
@ -1853,8 +1862,11 @@ public class KVMStorageProcessor implements StorageProcessor {
}
} else {
if (primaryPool.getType() == StoragePoolType.RBD) {
takeRbdVolumeSnapshotOfStoppedVm(primaryPool, disk, snapshotName);
Long snapshotSize = takeRbdVolumeSnapshotOfStoppedVm(primaryPool, disk, snapshotName);
newSnapshot.setPath(snapshotPath);
if (snapshotSize != null) {
newSnapshot.setPhysicalSize(snapshotSize);
}
} else if (primaryPool.getType() == StoragePoolType.CLVM) {
CreateObjectAnswer result = takeClvmVolumeSnapshotOfStoppedVm(disk, snapshotName);
if (result != null) return result;
@ -2306,7 +2318,8 @@ public class KVMStorageProcessor implements StorageProcessor {
* barriers properly (>2.6.32) this won't be any different then pulling the power
* cord out of a running machine.
*/
private void takeRbdVolumeSnapshotOfStoppedVm(KVMStoragePool primaryPool, KVMPhysicalDisk disk, String snapshotName) {
private Long takeRbdVolumeSnapshotOfStoppedVm(KVMStoragePool primaryPool, KVMPhysicalDisk disk, String snapshotName) {
Long snapshotSize = null;
try {
Rados r = radosConnect(primaryPool);
@ -2317,11 +2330,43 @@ public class KVMStorageProcessor implements StorageProcessor {
logger.debug("Attempting to create RBD snapshot {}@{}", disk.getName(), snapshotName);
image.snapCreate(snapshotName);
image.snapCreate(snapshotName);
long rbdSnapshotSize = getRbdSnapshotSize(primaryPool.getSourceDir(), disk.getName(), snapshotName, primaryPool.getSourceHost(), primaryPool.getAuthUserName(), primaryPool.getAuthSecret());
if (rbdSnapshotSize > 0) {
snapshotSize = rbdSnapshotSize;
}
rbd.close(image);
r.ioCtxDestroy(io);
} catch (final Exception e) {
logger.error("A RBD snapshot operation on [{}] failed. The error was: {}", disk.getName(), e.getMessage(), e);
}
return snapshotSize;
}
private long getRbdSnapshotSize(String poolPath, String diskName, String snapshotName, String rbdMonitor, String authUser, String authSecret) {
logger.debug("Get RBD snapshot size for {}/{}@{}", poolPath, diskName, snapshotName);
//cmd: rbd du <pool>/<disk-name>@<snapshot-name> --format json --mon-host <monitor-host> --id <user> --key <key> 2>/dev/null
String snapshotDetailsInJson = Script.runSimpleBashScript(String.format("rbd du %s/%s@%s --format json --mon-host %s --id %s --key %s 2>/dev/null", poolPath, diskName, snapshotName, rbdMonitor, authUser, authSecret));
if (StringUtils.isNotBlank(snapshotDetailsInJson)) {
ObjectMapper mapper = new ObjectMapper();
try {
JsonNode root = mapper.readTree(snapshotDetailsInJson);
for (JsonNode image : root.path("images")) {
if (snapshotName.equals(image.path("snapshot").asText())) {
long usedSizeInBytes = image.path("used_size").asLong();
logger.debug("RBD snapshot {}/{}@{} used size in bytes: {}", poolPath, diskName, snapshotName, usedSizeInBytes);
return usedSizeInBytes;
}
}
} catch (JsonProcessingException e) {
logger.error("Unable to get the RBD snapshot size, RBD snapshot cmd output: {}", snapshotDetailsInJson, e);
}
} else {
logger.warn("Failed to get RBD snapshot size for {}/{}@{} - no output for RBD snapshot cmd", poolPath, diskName, snapshotName);
}
return 0;
}
/**

View File

@ -99,6 +99,14 @@ status() {
echo '{"status": "success", "power_state": "poweron"}'
}
statuses() {
parse_json "$1" || exit 1
# This external system can not return an output like the following:
# {"status":"success","power_state":{"i-3-23-VM":"poweroff","i-2-25-VM":"poweron"}}
# CloudStack can fallback to retrieving the power state of the single VM using the "status" action
echo '{"status": "error", "message": "Not supported"}'
}
get_console() {
parse_json "$1" || exit 1
local response
@ -145,6 +153,9 @@ case $action in
status)
status "$parameters"
;;
statuses)
statuses "$parameters"
;;
getconsole)
get_console "$parameters"
;;

View File

@ -4479,7 +4479,7 @@ public class ApiResponseHelper implements ResponseGenerator {
} else if (usageRecord.getUsageType() == UsageTypes.BACKUP) {
resourceType = ResourceObjectType.Backup;
final StringBuilder builder = new StringBuilder();
builder.append("Backup usage of size ").append(usageRecord.getUsageDisplay());
builder.append("Backup usage");
if (vmInstance != null) {
resourceId = vmInstance.getId();
usageRecResponse.setResourceName(vmInstance.getInstanceName());
@ -4492,9 +4492,12 @@ public class ApiResponseHelper implements ResponseGenerator {
.append(" (").append(backupOffering.getUuid()).append(", user ad-hoc/scheduled backup allowed: ")
.append(backupOffering.isUserDrivenBackupAllowed()).append(")");
}
}
builder.append(" with size ").append(toHumanReadableSize(usageRecord.getSize()));
builder.append(" and with virtual size ").append(toHumanReadableSize(usageRecord.getVirtualSize()));
usageRecResponse.setDescription(builder.toString());
usageRecResponse.setSize(usageRecord.getSize());
usageRecResponse.setVirtualSize(usageRecord.getVirtualSize());
} else if (usageRecord.getUsageType() == UsageTypes.VM_SNAPSHOT) {
resourceType = ResourceObjectType.VMSnapshot;
VMSnapshotVO vmSnapshotVO = null;

View File

@ -39,8 +39,8 @@
Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>
# Include HTTP configuration **IF SET**
IncludeOptional /etc/apache2/http.conf
# Include CORS configuration **IF SET**
IncludeOptional /etc/apache2/cors.conf
</VirtualHost>
@ -86,8 +86,8 @@
Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>
# Include HTTPS configuration **IF SET**
IncludeOptional /etc/apache2/https.conf
# Include CORS configuration **IF SET**
IncludeOptional /etc/apache2/cors.conf
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.

View File

@ -1233,17 +1233,21 @@ class CsRemoteAccessVpn(CsDataBag):
CsHelper.start_if_stopped("ipsec")
logging.debug("Remote accessvpn data bag %s", self.dbag)
config_changed = False
if not self.config.has_public_network():
interface = self.config.address().get_guest_if_by_network_id()
if interface:
self.configure_l2tpIpsec(interface.get_ip(), self.dbag[public_ip])
config_changed = self.configure_l2tpIpsec(interface.get_ip(), self.dbag[public_ip])
self.remoteaccessvpn_iptables(interface.get_device(), interface.get_ip(), self.dbag[public_ip])
else:
self.configure_l2tpIpsec(public_ip, self.dbag[public_ip])
config_changed = self.configure_l2tpIpsec(public_ip, self.dbag[public_ip])
self.remoteaccessvpn_iptables(self.dbag[public_ip]['public_interface'], public_ip, self.dbag[public_ip])
CsHelper.execute("ipsec update")
CsHelper.execute("systemctl start xl2tpd")
if config_changed:
CsHelper.execute("systemctl restart xl2tpd")
else:
CsHelper.execute("systemctl start xl2tpd")
CsHelper.execute("ipsec rereadsecrets")
else:
logging.debug("Disabling remote access vpn .....")
@ -1266,21 +1270,23 @@ class CsRemoteAccessVpn(CsDataBag):
l2tpfile = CsFile(l2tpconffile)
l2tpfile.addeq(" left=%s" % left)
l2tpfile.addeq(" leftid=%s" % obj['vpn_server_ip'])
l2tpfile.commit()
l2tp_changed = l2tpfile.commit()
secret = CsFile(vpnsecretfilte)
secret.empty()
secret.addeq(": PSK \"%s\"" % (psk))
secret.commit()
secret_changed = secret.commit()
xl2tpdconf = CsFile(xl2tpdconffile)
xl2tpdconf.addeq("ip range = %s" % iprange)
xl2tpdconf.addeq("local ip = %s" % localip)
xl2tpdconf.commit()
xl2tpd_changed = xl2tpdconf.commit()
xl2tpoptions = CsFile(xl2tpoptionsfile)
xl2tpoptions.search("ms-dns ", "ms-dns %s" % localip)
xl2tpoptions.commit()
xl2tpoptions_changed = xl2tpoptions.commit()
return l2tp_changed or secret_changed or xl2tpd_changed or xl2tpoptions_changed
def remoteaccessvpn_iptables(self, publicdev, publicip, obj):
localcidr = obj['local_cidr']

View File

@ -924,9 +924,6 @@ parse_cmd_line() {
privateMtu)
export PRIVATEMTU=$VALUE
;;
useHttpsToUpload)
export USEHTTPS=$VALUE
;;
vncport)
export VNCPORT=$VALUE
;;

View File

@ -50,33 +50,14 @@ setup_secstorage() {
a2enmod proxy_http
a2enmod headers
if [ -z $USEHTTPS ] | $USEHTTPS ; then
if [ -f /etc/apache2/http.conf ]; then
rm -rf /etc/apache2/http.conf
fi
cat >/etc/apache2/https.conf <<HTTPS
RewriteEngine On
RewriteCond %{HTTPS} =on
RewriteCond %{REQUEST_METHOD} =POST
RewriteRule ^/upload/(.*) http://127.0.0.1:8210/upload?uuid=\$1 [P,L]
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, OPTIONS"
Header always set Access-Control-Allow-Headers "x-requested-with, content-type, origin, authorization, accept, client-security-token, x-signature, x-metadata, x-expires"
HTTPS
else
if [ -f /etc/apache2/https.conf ]; then
rm -rf /etc/apache2/https.conf
fi
cat >/etc/apache2/http.conf <<HTTP
cat >/etc/apache2/cors.conf <<CORS
RewriteEngine On
RewriteCond %{REQUEST_METHOD} =POST
RewriteRule ^/upload/(.*) http://127.0.0.1:8210/upload?uuid=\$1 [P,L]
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "POST, OPTIONS"
Header always set Access-Control-Allow-Headers "x-requested-with, content-type, origin, authorization, accept, client-security-token, x-signature, x-metadata, x-expires"
HTTP
fi
CORS
disable_rpfilter
enable_fwding 0

View File

@ -1,17 +1,17 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// 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
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// 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
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
@ -24,7 +24,7 @@
v-for="filter in this.searchFilters"
:key="filter.key + filter.value"
>
<a-col v-if="!['page', 'pagesize', 'q', 'keyword', 'tags'].includes(filter.key)">
<a-col v-if="!['page', 'pagesize', 'q', 'keyword', 'tags', 'projectid'].includes(filter.key)">
<a-tag
v-if="!filter.isTag"
closable
@ -56,7 +56,7 @@
<script>
import { api } from '@/api/index'
import { getAPI } from '@/api'
export default {
name: 'SearchFilter',
@ -175,6 +175,7 @@ export default {
immediate: true,
handler (newFilters) {
const clonedFilters = newFilters.map(filter => ({ ...filter }))
this.searchFilters = clonedFilters.map(f => ({ ...f }))
const promises = []
for (let idx = 0; idx < clonedFilters.length; idx++) {
const filter = clonedFilters[idx]
@ -188,9 +189,10 @@ export default {
resolve()
} else {
this.getSearchFilters(filter.key, filter.value).then((value) => {
const displayValue = (value !== undefined && value !== null && value !== '') ? value : filter.value
clonedFilters[idx] = {
key: filter.key,
value: value,
value: displayValue,
isTag: filter.isTag
}
resolve()
@ -296,7 +298,7 @@ export default {
},
getHypervisor (value) {
return new Promise((resolve) => {
api('listHypervisors').then(json => {
getAPI('listHypervisors').then(json => {
if (json?.listhypervisorsresponse?.hypervisor) {
for (const key in json.listhypervisorsresponse.hypervisor) {
const hypervisor = json.listhypervisorsresponse.hypervisor[key]
@ -316,7 +318,7 @@ export default {
if (!this.$isValidUuid(id)) {
return resolve('')
}
api(apiName, { listAll: true, id: id }).then(json => {
getAPI(apiName, { listAll: true, id: id }).then(json => {
const items = json && json[responseKey1] && json[responseKey1][responseKey2]
if (Array.isArray(items) && items.length > 0 && items[0] && items[0][field] !== undefined) {
resolve(items[0][field])
@ -337,7 +339,7 @@ export default {
},
getAlertType (type) {
return new Promise((resolve) => {
api('listAlertTypes').then(json => {
getAPI('listAlertTypes').then(json => {
const alertTypes = {}
for (const key in json.listalerttypesresponse.alerttype) {
const alerttype = json.listalerttypesresponse.alerttype[key]
@ -351,7 +353,7 @@ export default {
},
getAffinityGroupType (type) {
return new Promise((resolve) => {
api('listAffinityGroupTypes').then(json => {
getAPI('listAffinityGroupTypes').then(json => {
const alertTypes = {}
for (const key in json.listaffinitygrouptypesresponse.affinityGroupType) {
const affinityGroupType = json.listaffinitygrouptypesresponse.affinityGroupType[key]

View File

@ -17,7 +17,10 @@
<template>
<div>
<a-affix :offsetTop="this.$store.getters.maintenanceInitiated || this.$store.getters.shutdownTriggered ? 103 : 78">
<a-affix
:key="'affix-' + showSearchFilters"
:offsetTop="this.$store.getters.maintenanceInitiated || this.$store.getters.shutdownTriggered ? 103 : 78"
>
<a-card
class="breadcrumb-card"
style="z-index: 10"
@ -127,11 +130,11 @@
</a-col>
</a-row>
<a-row
v-if="!dataView && $config.showSearchFilters"
style="min-height: 36px; padding-top: 12px;"
v-if="showSearchFilters"
style="min-height: 36px; padding-top: 12px; padding-left: 12px;"
>
<search-filter
:filters="getActiveFilters()"
:filters="activeFiltersList"
:apiName="apiName"
@removeFilter="removeFilter"
/>
@ -829,6 +832,37 @@ export default {
}
},
computed: {
activeFiltersList () {
const queryParams = Object.assign({}, this.$route.query)
const activeFilters = []
for (const filter in queryParams) {
if (this.$route.name === 'host' && filter === 'type') {
continue
}
if (!filter.startsWith('tags[')) {
activeFilters.push({
key: filter,
value: queryParams[filter],
isTag: false
})
} else if (filter.endsWith('].key')) {
const tagIdx = filter.split('[')[1].split(']')[0]
const tagKey = queryParams[`tags[${tagIdx}].key`]
const tagValue = queryParams[`tags[${tagIdx}].value`]
activeFilters.push({
key: tagKey,
value: tagValue,
isTag: true,
tagIdx: tagIdx
})
}
}
return activeFilters
},
showSearchFilters () {
const excludedKeys = ['page', 'pagesize', 'q', 'keyword', 'tags', 'projectid']
return !this.dataView && this.$config.showSearchFilters && this.activeFiltersList.some(f => !excludedKeys.includes(f.key))
},
hasSelected () {
return this.selectedRowKeys.length > 0
},
@ -1274,30 +1308,6 @@ export default {
eventBus.emit('action-closing', { action: this.currentAction })
this.closeAction()
},
getActiveFilters () {
const queryParams = Object.assign({}, this.$route.query)
const activeFilters = []
for (const filter in queryParams) {
if (!filter.startsWith('tags[')) {
activeFilters.push({
key: filter,
value: queryParams[filter],
isTag: false
})
} else if (filter.endsWith('].key')) {
const tagIdx = filter.split('[')[1].split(']')[0]
const tagKey = queryParams[`tags[${tagIdx}].key`]
const tagValue = queryParams[`tags[${tagIdx}].value`]
activeFilters.push({
key: tagKey,
value: tagValue,
isTag: true,
tagIdx: tagIdx
})
}
}
return activeFilters
},
removeFilter (filter) {
const queryParams = Object.assign({}, this.$route.query)
if (filter.isTag) {

View File

@ -70,12 +70,11 @@ public class BackupUsageParser extends UsageParser {
DecimalFormat dFormat = new DecimalFormat("#.######");
String usageDisplay = dFormat.format(usage);
final Double rawUsage = (double) usageBackup.getSize();
final String description = String.format("Backup usage VM ID: %d, backup offering: %d", vmId, offeringId);
final UsageVO usageRecord =
new UsageVO(zoneId, account.getAccountId(), account.getDomainId(), description, usageDisplay + " Hrs",
UsageTypes.BACKUP, new Double(usage), vmId, null, offeringId, null, vmId,
UsageTypes.BACKUP, (double) usage, vmId, null, offeringId, null, vmId,
usageBackup.getSize(), usageBackup.getProtectedSize(), startDate, endDate);
usageDao.persist(usageRecord);
}

View File

@ -109,7 +109,7 @@ public class VMSnapshotOnPrimaryParser extends UsageParser {
String usageDesc = "VMSnapshot Id: " + vmSnapshotId + " On Primary Usage: VM Id: " + vmId;
usageDesc += " Size: " + toHumanReadableSize(virtualSize);
UsageVO usageRecord = new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", usageType, new Double(usage), vmId, name, null, null,
UsageVO usageRecord = new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", usageType, (double) usage, vmId, name, null, null,
vmSnapshotId, physicalSize, virtualSize, startDate, endDate);
usageDao.persist(usageRecord);
}

View File

@ -142,4 +142,19 @@ public class DigestHelper {
throw new CloudRuntimeException(errMsg, e);
}
}
public static String prependAlgorithm(String checksum) {
if (StringUtils.isEmpty(checksum)) {
return checksum;
}
int checksumLength = checksum.length();
Map<String, Integer> paddingLengths = getChecksumLengthsMap();
for (Map.Entry<String, Integer> entry : paddingLengths.entrySet()) {
if (entry.getValue().equals(checksumLength)) {
String algorithm = entry.getKey();
return String.format("{%s}%s", algorithm, checksum);
}
}
return checksum;
}
}