mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 01:32:18 +02:00
Merge release branch 4.20 to main
* 4.20: VR: apply iptables rules when add/remove static routes (#10064) Certificate and VM hostname validation improvements (#10051) set ulimit for server according to redhat spec (#10040) kvm-storage: provide isVMMigrate information to storage plugins (#10093) Allow config drive deletion of migrated VM, on host maintenance (#10045) linstor: improve heartbeat check with also asking linstor (#10105) server: simplify role change validation (#9173) UI: create VPC network offering with conserve mode (#10082) server: fix typo removeaccessvpn in VirtualRouterElement (#10086) UI: remove duplicated Instance Name in Public IP details page (#10087) UI: Fixes in the Usage UI (#10000) SAML2: add cookie with HttpOnly too #10013 (#10047) ui: Allow font-awesome icon usage and optimise icon size inconsistency (#9744)
This commit is contained in:
commit
9295a1624d
@ -74,6 +74,7 @@ public interface VmDetailConstants {
|
||||
String ENCRYPTED_PASSWORD = "Encrypted.Password";
|
||||
|
||||
String CONFIG_DRIVE_LOCATION = "configDriveLocation";
|
||||
String LAST_CONFIG_DRIVE_LOCATION = "lastConfigDriveLocation";
|
||||
|
||||
String SKIP_DRS = "skipFromDRS";
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ import com.cloud.agent.api.CleanupNetworkRulesCmd;
|
||||
import com.cloud.agent.api.Command;
|
||||
import com.cloud.agent.api.CreateStoragePoolCommand;
|
||||
import com.cloud.agent.api.DeleteStoragePoolCommand;
|
||||
import com.cloud.agent.api.HandleConfigDriveIsoCommand;
|
||||
import com.cloud.agent.api.MaintainCommand;
|
||||
import com.cloud.agent.api.MigrateCommand;
|
||||
import com.cloud.agent.api.ModifySshKeysCommand;
|
||||
@ -122,11 +123,10 @@ public abstract class AgentAttache {
|
||||
|
||||
public final static String[] s_commandsAllowedInMaintenanceMode = new String[] { MaintainCommand.class.toString(), MigrateCommand.class.toString(),
|
||||
StopCommand.class.toString(), CheckVirtualMachineCommand.class.toString(), PingTestCommand.class.toString(), CheckHealthCommand.class.toString(),
|
||||
ReadyCommand.class.toString(), ShutdownCommand.class.toString(), SetupCommand.class.toString(),
|
||||
CleanupNetworkRulesCmd.class.toString(), CheckNetworkCommand.class.toString(), PvlanSetupCommand.class.toString(), CheckOnHostCommand.class.toString(),
|
||||
ModifyTargetsCommand.class.toString(), ModifySshKeysCommand.class.toString(),
|
||||
CreateStoragePoolCommand.class.toString(), DeleteStoragePoolCommand.class.toString(), ModifyStoragePoolCommand.class.toString(),
|
||||
SetupMSListCommand.class.toString(), RollingMaintenanceCommand.class.toString(), CleanupPersistentNetworkResourceCommand.class.toString()};
|
||||
ReadyCommand.class.toString(), ShutdownCommand.class.toString(), SetupCommand.class.toString(), CleanupNetworkRulesCmd.class.toString(),
|
||||
CheckNetworkCommand.class.toString(), PvlanSetupCommand.class.toString(), CheckOnHostCommand.class.toString(), ModifyTargetsCommand.class.toString(),
|
||||
ModifySshKeysCommand.class.toString(), CreateStoragePoolCommand.class.toString(), DeleteStoragePoolCommand.class.toString(), ModifyStoragePoolCommand.class.toString(),
|
||||
SetupMSListCommand.class.toString(), RollingMaintenanceCommand.class.toString(), CleanupPersistentNetworkResourceCommand.class.toString(), HandleConfigDriveIsoCommand.class.toString()};
|
||||
protected final static String[] s_commandsNotAllowedInConnectingMode = new String[] { StartCommand.class.toString(), CreateCommand.class.toString() };
|
||||
static {
|
||||
Arrays.sort(s_commandsAllowedInMaintenanceMode);
|
||||
|
||||
@ -245,6 +245,7 @@ mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/lib
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/run
|
||||
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel
|
||||
|
||||
@ -294,6 +295,7 @@ install -D utils/target/cloud-utils-%{_maventag}-bundled.jar ${RPM_BUILD_ROOT}%{
|
||||
|
||||
install -D packaging/el8/cloud-ipallocator.rc ${RPM_BUILD_ROOT}%{_initrddir}/%{name}-ipallocator
|
||||
install -D packaging/el8/cloud.limits ${RPM_BUILD_ROOT}%{_sysconfdir}/security/limits.d/cloud
|
||||
install -D packaging/el8/filelimit.conf ${RPM_BUILD_ROOT}%{_sysconfdir}/systemd/system/%{name}-management.service.d
|
||||
install -D packaging/systemd/cloudstack-management.service ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-management.service
|
||||
install -D packaging/systemd/cloudstack-management.default ${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-management
|
||||
install -D server/target/conf/cloudstack-sudoers ${RPM_BUILD_ROOT}%{_sysconfdir}/sudoers.d/%{name}-management
|
||||
@ -575,6 +577,7 @@ pip3 install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz
|
||||
%config(noreplace) %{_sysconfdir}/default/%{name}-management
|
||||
%config(noreplace) %{_sysconfdir}/sudoers.d/%{name}-management
|
||||
%config(noreplace) %{_sysconfdir}/security/limits.d/cloud
|
||||
%config(noreplace) %{_sysconfdir}/systemd/system/%{name}-management.service.d
|
||||
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/db.properties
|
||||
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/server.properties
|
||||
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/config.json
|
||||
|
||||
20
packaging/el8/filelimit.conf
Normal file
20
packaging/el8/filelimit.conf
Normal file
@ -0,0 +1,20 @@
|
||||
# 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.
|
||||
|
||||
# should go in /etc/systemd/system/cloudstack-management.service.d/
|
||||
[Service]
|
||||
LimitNPROC=infinity
|
||||
@ -1031,7 +1031,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
hostHealthCheckScriptPath = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEALTH_CHECK_SCRIPT_PATH);
|
||||
if (StringUtils.isNotBlank(hostHealthCheckScriptPath) && !new File(hostHealthCheckScriptPath).exists()) {
|
||||
logger.info(String.format("Unable to find the host health check script at: %s, " +
|
||||
LOGGER.info(String.format("Unable to find the host health check script at: %s, " +
|
||||
"discarding it", hostHealthCheckScriptPath));
|
||||
}
|
||||
|
||||
@ -2796,11 +2796,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
if (hostCpuMaxCapacity > 0) {
|
||||
int updatedCpuShares = (int) Math.ceil((requestedCpuShares * CGROUP_V2_UPPER_LIMIT) / (double) hostCpuMaxCapacity);
|
||||
logger.debug(String.format("This host utilizes cgroupv2 (as the max shares value is [%s]), thus, the VM requested shares of [%s] will be converted to " +
|
||||
LOGGER.debug(String.format("This host utilizes cgroupv2 (as the max shares value is [%s]), thus, the VM requested shares of [%s] will be converted to " +
|
||||
"consider the host limits; the new CPU shares value is [%s].", hostCpuMaxCapacity, requestedCpuShares, updatedCpuShares));
|
||||
return updatedCpuShares;
|
||||
}
|
||||
logger.debug(String.format("This host does not have a maximum CPU shares set; therefore, this host utilizes cgroupv1 and the VM requested CPU shares [%s] will not be " +
|
||||
LOGGER.debug(String.format("This host does not have a maximum CPU shares set; therefore, this host utilizes cgroupv1 and the VM requested CPU shares [%s] will not be " +
|
||||
"converted.", requestedCpuShares));
|
||||
return requestedCpuShares;
|
||||
}
|
||||
@ -2931,7 +2931,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
LOGGER.warn(String.format("Setting System VM's [%s] current memory as max memory [%s].", vmTO.toString(), maxRam));
|
||||
} else {
|
||||
long minRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMinRam());
|
||||
logger.debug(String.format("Setting VM's [%s] current memory as min memory [%s] due to memory ballooning is enabled.", vmTO.toString(), minRam));
|
||||
LOGGER.debug(String.format("Setting VM's [%s] current memory as min memory [%s] due to memory ballooning is enabled.", vmTO.toString(), minRam));
|
||||
retVal = minRam;
|
||||
}
|
||||
return retVal;
|
||||
@ -3002,9 +3002,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
public static boolean useBLOCKDiskType(KVMPhysicalDisk physicalDisk) {
|
||||
return physicalDisk != null &&
|
||||
physicalDisk.getPool().getType() == StoragePoolType.Linstor &&
|
||||
physicalDisk.getPool() != null &&
|
||||
StoragePoolType.Linstor.equals(physicalDisk.getPool().getType()) &&
|
||||
physicalDisk.getFormat() != null &&
|
||||
physicalDisk.getFormat()== PhysicalDiskFormat.RAW;
|
||||
PhysicalDiskFormat.RAW.equals(physicalDisk.getFormat());
|
||||
}
|
||||
|
||||
public static DiskDef.DiskType getDiskType(KVMPhysicalDisk physicalDisk) {
|
||||
@ -3283,7 +3284,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
* (ii) Libvirt >= 6.3.0
|
||||
*/
|
||||
public void setDiskIoDriver(DiskDef disk, IoDriverPolicy ioDriver) {
|
||||
logger.debug(String.format("Disk IO driver policy [%s]. The host supports the io_uring policy [%s]", ioDriver, enableIoUring));
|
||||
LOGGER.debug(String.format("Disk IO driver policy [%s]. The host supports the io_uring policy [%s]", ioDriver, enableIoUring));
|
||||
if (ioDriver != null) {
|
||||
if (IoDriverPolicy.IO_URING != ioDriver) {
|
||||
disk.setIoDriver(ioDriver);
|
||||
@ -3426,13 +3427,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
}
|
||||
if (configdrive != null) {
|
||||
try {
|
||||
LOGGER.debug(String.format("Detaching ConfigDrive ISO of the VM %s, at path %s", vmName, configdrive.getDiskPath()));
|
||||
String result = attachOrDetachISO(conn, vmName, configdrive.getDiskPath(), false, CONFIG_DRIVE_ISO_DEVICE_ID);
|
||||
if (result != null) {
|
||||
LOGGER.warn("Detach ConfigDrive ISO with result: " + result);
|
||||
LOGGER.warn("Detach ConfigDrive ISO of the VM {}, at path {} with result: {}", vmName, configdrive.getDiskPath(), result);
|
||||
}
|
||||
LOGGER.debug(String.format("Attaching ConfigDrive ISO of the VM %s, at path %s", vmName, configdrive.getDiskPath()));
|
||||
result = attachOrDetachISO(conn, vmName, configdrive.getDiskPath(), true, CONFIG_DRIVE_ISO_DEVICE_ID);
|
||||
if (result != null) {
|
||||
LOGGER.warn("Attach ConfigDrive ISO with result: " + result);
|
||||
LOGGER.warn("Attach ConfigDrive ISO of the VM {}, at path {} with result: {}", vmName, configdrive.getDiskPath(), result);
|
||||
}
|
||||
} catch (final LibvirtException | InternalErrorException | URISyntaxException e) {
|
||||
final String msg = "Detach and attach ConfigDrive ISO failed due to " + e.toString();
|
||||
@ -3444,16 +3447,20 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
public synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach, final Integer diskSeq) throws LibvirtException, URISyntaxException,
|
||||
InternalErrorException {
|
||||
final DiskDef iso = new DiskDef();
|
||||
if (isoPath != null && isAttach) {
|
||||
final int index = isoPath.lastIndexOf("/");
|
||||
final String path = isoPath.substring(0, index);
|
||||
final String name = isoPath.substring(index + 1);
|
||||
final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path);
|
||||
final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name);
|
||||
final DiskDef.DiskType diskType = getDiskType(isoVol);
|
||||
isoPath = isoVol.getPath();
|
||||
if (isAttach && StringUtils.isNotBlank(isoPath) && isoPath.lastIndexOf("/") > 0) {
|
||||
if (isoPath.startsWith(getConfigPath() + "/" + ConfigDrive.CONFIGDRIVEDIR) && isoPath.contains(vmName)) {
|
||||
iso.defISODisk(isoPath, diskSeq, DiskDef.DiskType.FILE);
|
||||
} else {
|
||||
final int index = isoPath.lastIndexOf("/");
|
||||
final String path = isoPath.substring(0, index);
|
||||
final String name = isoPath.substring(index + 1);
|
||||
final KVMStoragePool storagePool = storagePoolManager.getStoragePoolByURI(path);
|
||||
final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
|
||||
final DiskDef.DiskType diskType = getDiskType(isoVol);
|
||||
isoPath = isoVol.getPath();
|
||||
|
||||
iso.defISODisk(isoPath, diskSeq, diskType);
|
||||
iso.defISODisk(isoPath, diskSeq, diskType);
|
||||
}
|
||||
} else {
|
||||
iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE);
|
||||
}
|
||||
@ -3640,19 +3647,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
*/
|
||||
private HealthCheckResult getHostHealthCheckResult() {
|
||||
if (StringUtils.isBlank(hostHealthCheckScriptPath)) {
|
||||
logger.debug("Host health check script path is not specified");
|
||||
LOGGER.debug("Host health check script path is not specified");
|
||||
return HealthCheckResult.IGNORE;
|
||||
}
|
||||
File script = new File(hostHealthCheckScriptPath);
|
||||
if (!script.exists() || !script.isFile() || !script.canExecute()) {
|
||||
logger.warn(String.format("The host health check script file set at: %s cannot be executed, " +
|
||||
LOGGER.warn(String.format("The host health check script file set at: %s cannot be executed, " +
|
||||
"reason: %s", hostHealthCheckScriptPath,
|
||||
!script.exists() ? "file does not exist" : "please check file permissions to execute this file"));
|
||||
return HealthCheckResult.IGNORE;
|
||||
}
|
||||
int exitCode = executeBashScriptAndRetrieveExitValue(hostHealthCheckScriptPath);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("Host health check script exit code: %s", exitCode));
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug(String.format("Host health check script exit code: %s", exitCode));
|
||||
}
|
||||
return retrieveHealthCheckResultFromExitCode(exitCode);
|
||||
}
|
||||
@ -3761,17 +3768,17 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
*/
|
||||
protected void calculateHostCpuMaxCapacity(int cpuCores, Long cpuSpeed) {
|
||||
String output = Script.runSimpleBashScript(COMMAND_GET_CGROUP_HOST_VERSION);
|
||||
logger.info(String.format("Host uses control group [%s].", output));
|
||||
LOGGER.info(String.format("Host uses control group [%s].", output));
|
||||
|
||||
if (!CGROUP_V2.equals(output)) {
|
||||
logger.info(String.format("Setting host CPU max capacity to 0, as it uses cgroup v1.", getHostCpuMaxCapacity()));
|
||||
LOGGER.info(String.format("Setting host CPU max capacity to 0, as it uses cgroup v1.", getHostCpuMaxCapacity()));
|
||||
setHostCpuMaxCapacity(0);
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info(String.format("Calculating the max shares of the host."));
|
||||
LOGGER.info(String.format("Calculating the max shares of the host."));
|
||||
setHostCpuMaxCapacity(cpuCores * cpuSpeed.intValue());
|
||||
logger.info(String.format("The max shares of the host is [%d].", getHostCpuMaxCapacity()));
|
||||
LOGGER.info(String.format("The max shares of the host is [%d].", getHostCpuMaxCapacity()));
|
||||
}
|
||||
|
||||
private StartupStorageCommand createLocalStoragePool(String localStoragePath, String localStorageUUID, StartupRoutingCommand cmd) {
|
||||
@ -3834,7 +3841,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
String sourcePath = null;
|
||||
try {
|
||||
String mountResult = Script.runSimpleBashScript("mount | grep \"" + diskPath + "\"");
|
||||
logger.debug("Got mount result for " + diskPath + "\n\n" + mountResult);
|
||||
LOGGER.debug("Got mount result for " + diskPath + "\n\n" + mountResult);
|
||||
if (StringUtils.isNotEmpty(mountResult)) {
|
||||
String[] res = mountResult.strip().split(" ");
|
||||
if (res[0].contains(":")) {
|
||||
@ -3851,7 +3858,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
return new Pair<>(sourceHostIp, sourcePath);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Failed to list source host and IP for " + diskPath + ex.toString());
|
||||
LOGGER.warn("Failed to list source host and IP for " + diskPath + ex.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -3864,14 +3871,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
domainNames.add(names[i]);
|
||||
}
|
||||
} catch (final LibvirtException e) {
|
||||
logger.warn("Failed to list defined domains", e);
|
||||
LOGGER.warn("Failed to list defined domains", e);
|
||||
}
|
||||
|
||||
int[] ids = null;
|
||||
try {
|
||||
ids = conn.listDomains();
|
||||
} catch (final LibvirtException e) {
|
||||
logger.warn("Failed to list domains", e);
|
||||
LOGGER.warn("Failed to list domains", e);
|
||||
return domainNames;
|
||||
}
|
||||
|
||||
@ -4959,7 +4966,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
public boolean setupTungstenVRouter(final String oper, final String inf, final String subnet, final String route,
|
||||
final String vrf) {
|
||||
final Script cmd = new Script(setupTungstenVrouterPath, timeout, logger);
|
||||
final Script cmd = new Script(setupTungstenVrouterPath, timeout, LOGGER);
|
||||
cmd.add(oper);
|
||||
cmd.add(inf);
|
||||
cmd.add(subnet);
|
||||
@ -4972,7 +4979,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
public boolean updateTungstenLoadbalancerStats(final String lbUuid, final String lbStatsPort,
|
||||
final String lbStatsUri, final String lbStatsAuth) {
|
||||
final Script cmd = new Script(updateTungstenLoadbalancerStatsPath, timeout, logger);
|
||||
final Script cmd = new Script(updateTungstenLoadbalancerStatsPath, timeout, LOGGER);
|
||||
cmd.add(lbUuid);
|
||||
cmd.add(lbStatsPort);
|
||||
cmd.add(lbStatsUri);
|
||||
@ -4984,7 +4991,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
|
||||
public boolean updateTungstenLoadbalancerSsl(final String lbUuid, final String sslCertName,
|
||||
final String certificateKey, final String privateKey, final String privateIp, final String port) {
|
||||
final Script cmd = new Script(updateTungstenLoadbalancerSslPath, timeout, logger);
|
||||
final Script cmd = new Script(updateTungstenLoadbalancerSslPath, timeout, LOGGER);
|
||||
cmd.add(lbUuid);
|
||||
cmd.add(sslCertName);
|
||||
cmd.add(certificateKey);
|
||||
@ -4997,7 +5004,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
}
|
||||
|
||||
public boolean setupTfRoute(final String privateIpAddress, final String fromNetwork, final String toNetwork) {
|
||||
final Script setupTfRouteScript = new Script(routerProxyPath, timeout, logger);
|
||||
final Script setupTfRouteScript = new Script(routerProxyPath, timeout, LOGGER);
|
||||
setupTfRouteScript.add("setup_tf_route.py");
|
||||
setupTfRouteScript.add(privateIpAddress);
|
||||
setupTfRouteScript.add(fromNetwork);
|
||||
@ -5006,7 +5013,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
final OutputInterpreter.OneLineParser setupTfRouteParser = new OutputInterpreter.OneLineParser();
|
||||
final String result = setupTfRouteScript.execute(setupTfRouteParser);
|
||||
if (result != null) {
|
||||
logger.debug("Failed to execute setup TF Route:" + result);
|
||||
LOGGER.debug("Failed to execute setup TF Route:" + result);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -5525,7 +5532,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
interfaceDef.setMultiQueueNumber(nicMultiqueueNumberInteger);
|
||||
}
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.warn(String.format("VM details %s is not a valid integer value %s", VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber));
|
||||
LOGGER.warn(String.format("VM details %s is not a valid integer value %s", VmDetailConstants.NIC_MULTIQUEUE_NUMBER, nicMultiqueueNumber));
|
||||
}
|
||||
}
|
||||
String nicPackedEnabled = details.get(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED);
|
||||
@ -5533,7 +5540,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
try {
|
||||
interfaceDef.setPackedVirtQueues(Boolean.valueOf(nicPackedEnabled));
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.warn(String.format("VM details %s is not a valid Boolean value %s", VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, nicPackedEnabled));
|
||||
LOGGER.warn(String.format("VM details %s is not a valid Boolean value %s", VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED, nicPackedEnabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5548,11 +5555,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
command.append(remoteFile);
|
||||
command.append(" " + tmpPath);
|
||||
command.append(outputFile);
|
||||
logger.debug(String.format("Converting remote disk file: %s, output file: %s%s (timeout: %d secs)", remoteFile, tmpPath, outputFile, timeoutInSecs));
|
||||
LOGGER.debug(String.format("Converting remote disk file: %s, output file: %s%s (timeout: %d secs)", remoteFile, tmpPath, outputFile, timeoutInSecs));
|
||||
SshHelper.sshExecute(srcIp, 22, username, null, password, command.toString(), timeoutInSecs * 1000);
|
||||
logger.debug("Copying converted remote disk file " + outputFile + " to: " + localDir);
|
||||
LOGGER.debug("Copying converted remote disk file " + outputFile + " to: " + localDir);
|
||||
SshHelper.scpFrom(srcIp, 22, username, null, password, localDir, tmpPath + outputFile);
|
||||
logger.debug("Successfully copied converted remote disk file to: " + localDir + "/" + outputFile);
|
||||
LOGGER.debug("Successfully copied converted remote disk file to: " + localDir + "/" + outputFile);
|
||||
return outputFile;
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
|
||||
@ -39,6 +39,9 @@ public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper<Ge
|
||||
String ip = null;
|
||||
boolean result = false;
|
||||
String vmName = command.getVmName();
|
||||
if (!NetUtils.verifyDomainNameLabel(vmName, true)) {
|
||||
return new Answer(command, result, ip);
|
||||
}
|
||||
String sanitizedVmName = sanitizeBashCommandArgument(vmName);
|
||||
String networkCidr = command.getVmNetworkCidr();
|
||||
List<String[]> commands = new ArrayList<>();
|
||||
|
||||
@ -118,7 +118,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp
|
||||
|
||||
skipDisconnect = true;
|
||||
|
||||
if (!storagePoolMgr.connectPhysicalDisksViaVmSpec(vm)) {
|
||||
if (!storagePoolMgr.connectPhysicalDisksViaVmSpec(vm, true)) {
|
||||
return new PrepareForMigrationAnswer(command, "failed to connect physical disks to host");
|
||||
}
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ import com.cloud.resource.ResourceWrapper;
|
||||
import com.cloud.utils.FileUtil;
|
||||
import com.cloud.utils.PropertiesUtil;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
import com.cloud.utils.script.Script;
|
||||
|
||||
@ResourceWrapper(handles = SetupDirectDownloadCertificateCommand.class)
|
||||
@ -131,6 +132,9 @@ public class LibvirtSetupDirectDownloadCertificateCommandWrapper extends Command
|
||||
public Answer execute(SetupDirectDownloadCertificateCommand cmd, LibvirtComputingResource serverResource) {
|
||||
String certificate = cmd.getCertificate();
|
||||
String certificateName = cmd.getCertificateName();
|
||||
if (!NetUtils.verifyDomainNameLabel(certificateName, false)) {
|
||||
return new Answer(cmd, false, "The provided certificate name is invalid");
|
||||
}
|
||||
|
||||
try {
|
||||
File agentFile = getAgentPropertiesFile();
|
||||
|
||||
@ -75,7 +75,7 @@ public final class LibvirtStartCommandWrapper extends CommandWrapper<StartComman
|
||||
|
||||
libvirtComputingResource.createVbd(conn, vmSpec, vmName, vm);
|
||||
|
||||
if (!storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)) {
|
||||
if (!storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)) {
|
||||
return new StartAnswer(command, "Failed to connect physical disks to host");
|
||||
}
|
||||
|
||||
|
||||
@ -84,7 +84,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<String, String> details) {
|
||||
public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<String, String> details, boolean isVMMigrate) {
|
||||
// ex. sudo iscsiadm -m node -T iqn.2012-03.com.test:volume1 -p 192.168.233.10:3260 -o new
|
||||
Script iScsiAdmCmd = new Script(true, "iscsiadm", 0, logger);
|
||||
|
||||
|
||||
@ -106,7 +106,7 @@ public class IscsiAdmStoragePool implements KVMStoragePool {
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String name, Map<String, String> details) {
|
||||
return this._storageAdaptor.connectPhysicalDisk(name, this, details);
|
||||
return this._storageAdaptor.connectPhysicalDisk(name, this, details, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -161,10 +161,10 @@ public class KVMStoragePoolManager {
|
||||
StorageAdaptor adaptor = getStorageAdaptor(type);
|
||||
KVMStoragePool pool = adaptor.getStoragePool(poolUuid);
|
||||
|
||||
return adaptor.connectPhysicalDisk(volPath, pool, details);
|
||||
return adaptor.connectPhysicalDisk(volPath, pool, details, false);
|
||||
}
|
||||
|
||||
public boolean connectPhysicalDisksViaVmSpec(VirtualMachineTO vmSpec) {
|
||||
public boolean connectPhysicalDisksViaVmSpec(VirtualMachineTO vmSpec, boolean isVMMigrate) {
|
||||
boolean result = false;
|
||||
|
||||
final String vmName = vmSpec.getName();
|
||||
@ -187,7 +187,7 @@ public class KVMStoragePoolManager {
|
||||
KVMStoragePool pool = getStoragePool(store.getPoolType(), store.getUuid());
|
||||
StorageAdaptor adaptor = getStorageAdaptor(pool.getType());
|
||||
|
||||
result = adaptor.connectPhysicalDisk(vol.getPath(), pool, disk.getDetails());
|
||||
result = adaptor.connectPhysicalDisk(vol.getPath(), pool, disk.getDetails(), isVMMigrate);
|
||||
|
||||
if (!result) {
|
||||
logger.error("Failed to connect disks via vm spec for vm: " + vmName + " volume:" + vol.toString());
|
||||
@ -315,6 +315,7 @@ public class KVMStoragePoolManager {
|
||||
URI storageUri = null;
|
||||
|
||||
try {
|
||||
logger.debug("Get storage pool by uri: " + uri);
|
||||
storageUri = new URI(uri);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new CloudRuntimeException(e.toString());
|
||||
@ -324,7 +325,7 @@ public class KVMStoragePoolManager {
|
||||
String uuid = null;
|
||||
String sourceHost = "";
|
||||
StoragePoolType protocol = null;
|
||||
final String scheme = storageUri.getScheme().toLowerCase();
|
||||
final String scheme = (storageUri.getScheme() != null) ? storageUri.getScheme().toLowerCase() : "";
|
||||
List<String> acceptedSchemes = List.of("nfs", "networkfilesystem", "filesystem");
|
||||
if (acceptedSchemes.contains(scheme)) {
|
||||
sourcePath = storageUri.getPath();
|
||||
|
||||
@ -1018,7 +1018,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String name, KVMStoragePool pool, Map<String, String> details) {
|
||||
public boolean connectPhysicalDisk(String name, KVMStoragePool pool, Map<String, String> details, boolean isVMMigrate) {
|
||||
// this is for managed storage that needs to prep disks prior to use
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor {
|
||||
* creates a nfs storage pool using libvirt
|
||||
*/
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<String, String> details) {
|
||||
public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<String, String> details, boolean isVMMigrate) {
|
||||
|
||||
StoragePool sp = null;
|
||||
Connect conn = null;
|
||||
|
||||
@ -182,7 +182,7 @@ public abstract class MultipathSCSIAdapterBase implements StorageAdaptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<String, String> details) {
|
||||
public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<String, String> details, boolean isVMMigrate) {
|
||||
LOGGER.info("connectPhysicalDisk called for [" + volumePath + "]");
|
||||
|
||||
if (StringUtils.isEmpty(volumePath)) {
|
||||
|
||||
@ -78,7 +78,7 @@ public class MultipathSCSIPool implements KVMStoragePool {
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details) {
|
||||
return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details);
|
||||
return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -181,7 +181,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!connectPhysicalDisk(name, pool, null)) {
|
||||
if(!connectPhysicalDisk(name, pool, null, false)) {
|
||||
throw new CloudRuntimeException(String.format("Failed to ensure disk %s was present", name));
|
||||
}
|
||||
|
||||
@ -224,7 +224,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<String, String> details) {
|
||||
public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<String, String> details, boolean isMigration) {
|
||||
if (StringUtils.isEmpty(volumePath) || pool == null) {
|
||||
logger.error("Unable to connect physical disk due to insufficient data");
|
||||
throw new CloudRuntimeException("Unable to connect physical disk due to insufficient data");
|
||||
|
||||
@ -89,7 +89,7 @@ public class ScaleIOStoragePool implements KVMStoragePool {
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details) {
|
||||
return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details);
|
||||
return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -52,8 +52,15 @@ public interface StorageAdaptor {
|
||||
public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool,
|
||||
PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase);
|
||||
|
||||
// given disk path (per database) and pool, prepare disk on host
|
||||
public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<String, String> details);
|
||||
/**
|
||||
* given disk path (per database) and pool, prepare disk on host
|
||||
* @param volumePath volume path
|
||||
* @param pool storage pool the disk is part of
|
||||
* @param details disk details map
|
||||
* @param isVMMigrate Indicates if request is while VM is migration
|
||||
* @return true if connect was a success
|
||||
*/
|
||||
public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<String, String> details, boolean isVMMigrate);
|
||||
|
||||
// given disk path (per database) and pool, clean up disk on host
|
||||
public boolean disconnectPhysicalDisk(String volumePath, KVMStoragePool pool);
|
||||
|
||||
@ -1477,7 +1477,7 @@ public class LibvirtComputingResourceTest {
|
||||
|
||||
when(libvirtComputingResourceMock.getVifDriver(nicTO.getType(), nicTO.getName())).thenReturn(vifDriver);
|
||||
when(libvirtComputingResourceMock.getStoragePoolMgr()).thenReturn(storagePoolManager);
|
||||
when(storagePoolManager.connectPhysicalDisksViaVmSpec(vm)).thenReturn(true);
|
||||
when(storagePoolManager.connectPhysicalDisksViaVmSpec(vm, true)).thenReturn(true);
|
||||
|
||||
final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
|
||||
assertNotNull(wrapper);
|
||||
@ -5023,7 +5023,7 @@ public class LibvirtComputingResourceTest {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(false);
|
||||
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(false);
|
||||
|
||||
final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
|
||||
assertNotNull(wrapper);
|
||||
@ -5224,7 +5224,7 @@ public class LibvirtComputingResourceTest {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true);
|
||||
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true);
|
||||
try {
|
||||
doNothing().when(libvirtComputingResourceMock).createVifs(vmSpec, vmDef);
|
||||
|
||||
@ -5303,7 +5303,7 @@ public class LibvirtComputingResourceTest {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true);
|
||||
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true);
|
||||
try {
|
||||
doNothing().when(libvirtComputingResourceMock).createVifs(vmSpec, vmDef);
|
||||
|
||||
@ -5380,7 +5380,7 @@ public class LibvirtComputingResourceTest {
|
||||
fail(e.getMessage());
|
||||
}
|
||||
|
||||
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true);
|
||||
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true);
|
||||
|
||||
final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
|
||||
assertNotNull(wrapper);
|
||||
|
||||
@ -190,7 +190,7 @@ public class ScaleIOStoragePoolTest {
|
||||
|
||||
when(adapter.getPhysicalDisk(volumeId, pool)).thenReturn(disk);
|
||||
|
||||
final boolean result = adapter.connectPhysicalDisk(volumePath, pool, null);
|
||||
final boolean result = adapter.connectPhysicalDisk(volumePath, pool, null, false);
|
||||
assertTrue(result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,20 +61,19 @@ public final class CitrixGetVmIpAddressCommandWrapper extends CommandWrapper<Get
|
||||
}
|
||||
|
||||
if (vmIp != null) {
|
||||
logger.debug("VM " +vmName + " ip address got retrieved "+vmIp);
|
||||
logger.debug("VM {} ip address got retrieved {}", vmName, vmIp);
|
||||
result = true;
|
||||
return new Answer(command, result, vmIp);
|
||||
}
|
||||
|
||||
}catch (Types.XenAPIException e) {
|
||||
logger.debug("Got exception in GetVmIpAddressCommand "+ e.getMessage());
|
||||
errorMsg = "Failed to retrived vm ip addr, exception: "+e.getMessage();
|
||||
}catch (XmlRpcException e) {
|
||||
logger.debug("Got exception in GetVmIpAddressCommand "+ e.getMessage());
|
||||
errorMsg = "Failed to retrived vm ip addr, exception: "+e.getMessage();
|
||||
} catch (Types.XenAPIException e) {
|
||||
logger.debug("Got exception in GetVmIpAddressCommand " + e.getMessage());
|
||||
errorMsg = "Failed to retrived vm ip addr, exception: " + e.getMessage();
|
||||
} catch (XmlRpcException e) {
|
||||
logger.debug("Got exception in GetVmIpAddressCommand " + e.getMessage());
|
||||
errorMsg = "Failed to retrived vm ip addr, exception: " + e.getMessage();
|
||||
}
|
||||
|
||||
return new Answer(command, result, errorMsg);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,18 @@ All notable changes to Linstor CloudStack plugin will be documented in this file
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2024-12-13]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Linstor heartbeat check now also ask linstor-controller if there is no connection between nodes
|
||||
|
||||
## [2024-12-11]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Only set allow-two-primaries if a live migration is performed
|
||||
|
||||
## [2024-10-28]
|
||||
|
||||
### Fixed
|
||||
|
||||
@ -46,6 +46,7 @@ import com.linbit.linstor.api.Configuration;
|
||||
import com.linbit.linstor.api.DevelopersApi;
|
||||
import com.linbit.linstor.api.model.ApiCallRc;
|
||||
import com.linbit.linstor.api.model.ApiCallRcList;
|
||||
import com.linbit.linstor.api.model.Node;
|
||||
import com.linbit.linstor.api.model.Properties;
|
||||
import com.linbit.linstor.api.model.ProviderKind;
|
||||
import com.linbit.linstor.api.model.Resource;
|
||||
@ -277,6 +278,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
|
||||
* @throws ApiException if any problem connecting to the Linstor controller
|
||||
*/
|
||||
private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws ApiException {
|
||||
logger.debug("enabling allow-two-primaries");
|
||||
String inUseNode = LinstorUtil.isResourceInUse(api, rscName);
|
||||
if (inUseNode != null && !inUseNode.equalsIgnoreCase(localNodeName)) {
|
||||
// allow 2 primaries for live migration, should be removed by disconnect on the other end
|
||||
@ -292,7 +294,8 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map<String, String> details)
|
||||
public boolean connectPhysicalDisk(
|
||||
String volumePath, KVMStoragePool pool, Map<String, String> details, boolean isVMMigration)
|
||||
{
|
||||
logger.debug("Linstor: connectPhysicalDisk {}:{} -> {}", pool.getUuid(), volumePath, details);
|
||||
if (volumePath == null) {
|
||||
@ -315,12 +318,13 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
|
||||
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
allow2PrimariesIfInUse(api, rscName);
|
||||
} catch (ApiException apiEx) {
|
||||
logger.error(apiEx);
|
||||
// do not fail here as adding allow-two-primaries property is only a problem while live migrating
|
||||
if (isVMMigration) {
|
||||
try {
|
||||
allow2PrimariesIfInUse(api, rscName);
|
||||
} catch (ApiException apiEx) {
|
||||
logger.error(apiEx);
|
||||
// do not fail here as adding allow-two-primaries property is only a problem while live migrating
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -716,4 +720,19 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
|
||||
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNodeOnline(LinstorStoragePool pool, String nodeName) {
|
||||
DevelopersApi linstorApi = getLinstorAPI(pool);
|
||||
try {
|
||||
List<Node> node = linstorApi.nodeList(Collections.singletonList(nodeName), Collections.emptyList(), null, null);
|
||||
if (node == null || node.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Node.ConnectionStatusEnum.ONLINE.equals(node.get(0).getConnectionStatus());
|
||||
} catch (ApiException apiEx) {
|
||||
logger.error(apiEx.getMessage());
|
||||
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ public class LinstorStoragePool implements KVMStoragePool {
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details)
|
||||
{
|
||||
return _storageAdaptor.connectPhysicalDisk(volumeUuid, this, details);
|
||||
return _storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -280,22 +280,52 @@ public class LinstorStoragePool implements KVMStoragePool {
|
||||
return sc.execute(parser);
|
||||
}
|
||||
|
||||
private boolean checkLinstorNodeOnline(String nodeName) {
|
||||
return ((LinstorStorageAdaptor)_storageAdaptor).isNodeOnline(this, nodeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks output of drbdsetup status output if this node has any valid connection to the specified
|
||||
* otherNodeName.
|
||||
* If there is no connection, ask the Linstor controller if the node is seen online and return false if not.
|
||||
* If there is a connection but not connected(valid) return false.
|
||||
* @param output Output of the drbdsetup status --json command
|
||||
* @param otherNodeName Name of the node to check against
|
||||
* @return true if we could say that this node thinks the node in question is reachable, otherwise false.
|
||||
*/
|
||||
private boolean checkDrbdSetupStatusOutput(String output, String otherNodeName) {
|
||||
JsonParser jsonParser = new JsonParser();
|
||||
JsonArray jResources = (JsonArray) jsonParser.parse(output);
|
||||
boolean connectionFound = false;
|
||||
for (JsonElement jElem : jResources) {
|
||||
JsonObject jRes = (JsonObject) jElem;
|
||||
JsonArray jConnections = jRes.getAsJsonArray("connections");
|
||||
for (JsonElement jConElem : jConnections) {
|
||||
JsonObject jConn = (JsonObject) jConElem;
|
||||
if (jConn.getAsJsonPrimitive("name").getAsString().equals(otherNodeName)
|
||||
&& jConn.getAsJsonPrimitive("connection-state").getAsString().equalsIgnoreCase("Connected")) {
|
||||
return true;
|
||||
if (jConn.getAsJsonPrimitive("name").getAsString().equals(otherNodeName))
|
||||
{
|
||||
connectionFound = true;
|
||||
if (jConn.getAsJsonPrimitive("connection-state").getAsString()
|
||||
.equalsIgnoreCase("Connected")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LOGGER.warn(String.format("checkDrbdSetupStatusOutput: no resource connected to %s.", otherNodeName));
|
||||
return false;
|
||||
boolean otherNodeOnline = false;
|
||||
if (connectionFound) {
|
||||
LOGGER.warn(String.format(
|
||||
"checkingHeartBeat: connection found, but not in state 'Connected' to %s", otherNodeName));
|
||||
} else {
|
||||
LOGGER.warn(String.format(
|
||||
"checkingHeartBeat: no resource connected to %s, checking LINSTOR", otherNodeName));
|
||||
otherNodeOnline = checkLinstorNodeOnline(otherNodeName);
|
||||
}
|
||||
LOGGER.info(String.format(
|
||||
"checkingHeartBeat: other node %s is %s.",
|
||||
otherNodeName,
|
||||
otherNodeOnline ? "online on controller" : "down"));
|
||||
return otherNodeOnline;
|
||||
}
|
||||
|
||||
private String executeDrbdEventsNow(OutputInterpreter.AllLinesParser parser) {
|
||||
|
||||
@ -249,7 +249,7 @@ public class StorPoolStorageAdaptor implements StorageAdaptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<String, String> details) {
|
||||
public boolean connectPhysicalDisk(String volumeUuid, KVMStoragePool pool, Map<String, String> details, boolean isVMMigrate) {
|
||||
SP_LOG("StorPoolStorageAdaptor.connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool);
|
||||
|
||||
LOGGER.debug(String.format("connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool));
|
||||
|
||||
@ -132,7 +132,7 @@ public class StorPoolStoragePool implements KVMStoragePool {
|
||||
|
||||
@Override
|
||||
public boolean connectPhysicalDisk(String name, Map<String, String> details) {
|
||||
return _storageAdaptor.connectPhysicalDisk(name, this, details);
|
||||
return _storageAdaptor.connectPhysicalDisk(name, this, details, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -321,6 +321,7 @@ public class SAMLUtils {
|
||||
String sessionKeyCookie = String.format("%s=%s;Domain=%s;Path=%s;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), domain, path, sameSite);
|
||||
LOGGER.debug("Adding sessionkey cookie to response: " + sessionKeyCookie);
|
||||
resp.addHeader("SET-COOKIE", sessionKeyCookie);
|
||||
resp.addHeader("SET-COOKIE", String.format("%s=%s;HttpOnly;Path=/client/api;%s", ApiConstants.SESSIONKEY, loginResponse.getSessionKey(), sameSite));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -345,10 +345,10 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
try {
|
||||
if (isConfigDriveIsoOnHostCache(vm.getId())) {
|
||||
vm.setConfigDriveLocation(Location.HOST);
|
||||
configureConfigDriveData(vm, nic, dest);
|
||||
|
||||
// Create the config drive on dest host cache
|
||||
createConfigDriveIsoOnHostCache(nic, vm, dest.getHost().getId());
|
||||
if (configureConfigDriveData(vm, nic, dest)) {
|
||||
// Create the config drive on dest host cache
|
||||
createConfigDriveIsoOnHostCache(vm, dest.getHost().getId());
|
||||
}
|
||||
} else {
|
||||
vm.setConfigDriveLocation(getConfigDriveLocation(vm.getId()));
|
||||
boolean result = addPasswordAndUserdata(network, nic, vm, dest, context);
|
||||
@ -380,7 +380,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
@Override
|
||||
public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) {
|
||||
try {
|
||||
if (isConfigDriveIsoOnHostCache(vm.getId())) {
|
||||
if (isLastConfigDriveIsoOnHostCache(vm.getId())) {
|
||||
vm.setConfigDriveLocation(Location.HOST);
|
||||
// Delete the config drive on src host cache
|
||||
deleteConfigDriveIsoOnHostCache(vm.getVirtualMachine(), vm.getHostId());
|
||||
@ -537,7 +537,18 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean createConfigDriveIsoOnHostCache(NicProfile nic, VirtualMachineProfile profile, Long hostId) throws ResourceUnavailableException {
|
||||
private boolean isLastConfigDriveIsoOnHostCache(long vmId) {
|
||||
final UserVmDetailVO vmDetailLastConfigDriveLocation = _userVmDetailsDao.findDetail(vmId, VmDetailConstants.LAST_CONFIG_DRIVE_LOCATION);
|
||||
if (vmDetailLastConfigDriveLocation == null) {
|
||||
return isConfigDriveIsoOnHostCache(vmId);
|
||||
}
|
||||
if (Location.HOST.toString().equalsIgnoreCase(vmDetailLastConfigDriveLocation.getValue())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean createConfigDriveIsoOnHostCache(VirtualMachineProfile profile, Long hostId) throws ResourceUnavailableException {
|
||||
if (hostId == null) {
|
||||
throw new ResourceUnavailableException("Config drive iso creation failed, dest host not available",
|
||||
ConfigDriveNetworkElement.class, 0L);
|
||||
@ -549,7 +560,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
|
||||
final String isoFileName = ConfigDrive.configIsoFileName(profile.getInstanceName());
|
||||
final String isoPath = ConfigDrive.createConfigDrivePath(profile.getInstanceName());
|
||||
List<NicProfile> nicProfiles = _networkOrchestrationService.getNicProfiles(nic.getVirtualMachineId(), profile.getHypervisorType());
|
||||
List<NicProfile> nicProfiles = _networkOrchestrationService.getNicProfiles(profile.getVirtualMachine().getId(), profile.getHypervisorType());
|
||||
final Map<Long, List<Service>> supportedServices = getSupportedServicesByElementForNetwork(nicProfiles);
|
||||
final String isoData = ConfigDriveBuilder.buildConfigDrive(nicProfiles, profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), customUserdataParamMap, supportedServices);
|
||||
final HandleConfigDriveIsoCommand configDriveIsoCommand = new HandleConfigDriveIsoCommand(isoPath, isoData, null, false, true, true);
|
||||
@ -565,7 +576,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
}
|
||||
|
||||
profile.setConfigDriveLocation(answer.getConfigDriveLocation());
|
||||
_userVmDetailsDao.addDetail(profile.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION, answer.getConfigDriveLocation().toString(), false);
|
||||
updateConfigDriveLocationInVMDetails(profile.getId(), answer.getConfigDriveLocation());
|
||||
addConfigDriveDisk(profile, null);
|
||||
return true;
|
||||
}
|
||||
@ -650,11 +661,23 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
answer.getDetails()), ConfigDriveNetworkElement.class, 0L);
|
||||
}
|
||||
profile.setConfigDriveLocation(answer.getConfigDriveLocation());
|
||||
_userVmDetailsDao.addDetail(profile.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION, answer.getConfigDriveLocation().toString(), false);
|
||||
updateConfigDriveLocationInVMDetails(profile.getId(), answer.getConfigDriveLocation());
|
||||
addConfigDriveDisk(profile, dataStore);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateConfigDriveLocationInVMDetails(long vmId, NetworkElement.Location configDriveLocation) {
|
||||
final UserVmDetailVO vmDetailConfigDriveLocation = _userVmDetailsDao.findDetail(vmId, VmDetailConstants.CONFIG_DRIVE_LOCATION);
|
||||
if (vmDetailConfigDriveLocation != null) {
|
||||
if (!configDriveLocation.toString().equalsIgnoreCase(vmDetailConfigDriveLocation.getValue())) {
|
||||
_userVmDetailsDao.addDetail(vmId, VmDetailConstants.LAST_CONFIG_DRIVE_LOCATION, vmDetailConfigDriveLocation.getValue(), false);
|
||||
} else {
|
||||
_userVmDetailsDao.removeDetail(vmId, VmDetailConstants.LAST_CONFIG_DRIVE_LOCATION);
|
||||
}
|
||||
}
|
||||
_userVmDetailsDao.addDetail(vmId, VmDetailConstants.CONFIG_DRIVE_LOCATION, configDriveLocation.toString(), false);
|
||||
}
|
||||
|
||||
private Map<String, String> getVMCustomUserdataParamMap(long vmId) {
|
||||
UserVmVO userVm = _userVmDao.findById(vmId);
|
||||
String userDataDetails = userVm.getUserDataDetails();
|
||||
@ -769,7 +792,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
|
||||
|
||||
private boolean configureConfigDriveData(final VirtualMachineProfile profile, final NicProfile nic, final DeployDestination dest) {
|
||||
final UserVmVO vm = _userVmDao.findById(profile.getId());
|
||||
if (vm.getType() != VirtualMachine.Type.User) {
|
||||
if (vm == null || vm.getType() != VirtualMachine.Type.User) {
|
||||
return false;
|
||||
}
|
||||
final Nic defaultNic = _networkModel.getDefaultNic(vm.getId());
|
||||
|
||||
@ -543,7 +543,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
|
||||
// Set capabilities for vpn
|
||||
final Map<Capability, String> vpnCapabilities = new HashMap<Capability, String>();
|
||||
vpnCapabilities.put(Capability.SupportedVpnProtocols, "pptp,l2tp,ipsec");
|
||||
vpnCapabilities.put(Capability.VpnTypes, "removeaccessvpn");
|
||||
vpnCapabilities.put(Capability.VpnTypes, "remoteaccessvpn");
|
||||
capabilities.put(Service.Vpn, vpnCapabilities);
|
||||
|
||||
final Map<Capability, String> dnsCapabilities = new HashMap<Capability, String>();
|
||||
|
||||
@ -122,8 +122,6 @@ import com.cloud.network.IpAddress;
|
||||
import com.cloud.network.IpAddressManager;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.network.NetworkModel;
|
||||
import com.cloud.network.security.SecurityGroupService;
|
||||
import com.cloud.network.security.SecurityGroupVO;
|
||||
import com.cloud.network.VpnUserVO;
|
||||
import com.cloud.network.as.AutoScaleManager;
|
||||
import com.cloud.network.dao.AccountGuestVlanMapDao;
|
||||
@ -137,6 +135,8 @@ import com.cloud.network.dao.RemoteAccessVpnVO;
|
||||
import com.cloud.network.dao.VpnUserDao;
|
||||
import com.cloud.network.router.VirtualRouter;
|
||||
import com.cloud.network.security.SecurityGroupManager;
|
||||
import com.cloud.network.security.SecurityGroupService;
|
||||
import com.cloud.network.security.SecurityGroupVO;
|
||||
import com.cloud.network.security.dao.SecurityGroupDao;
|
||||
import com.cloud.network.vpc.Vpc;
|
||||
import com.cloud.network.vpc.VpcManager;
|
||||
@ -1335,16 +1335,33 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
return _userAccountDao.findById(userId);
|
||||
}
|
||||
|
||||
private boolean isValidRoleChange(Account account, Role role) {
|
||||
Long currentAccRoleId = account.getRoleId();
|
||||
Role currentRole = roleService.findRole(currentAccRoleId);
|
||||
|
||||
if (role.getRoleType().ordinal() < currentRole.getRoleType().ordinal() && ((account.getType() == Account.Type.NORMAL && role.getRoleType().getAccountType().ordinal() > Account.Type.NORMAL.ordinal()) ||
|
||||
account.getType().ordinal() > Account.Type.NORMAL.ordinal() && role.getRoleType().getAccountType().ordinal() < account.getType().ordinal() && role.getRoleType().getAccountType().ordinal() > 0)) {
|
||||
throw new PermissionDeniedException(String.format("Unable to update account role to %s as you are " +
|
||||
"attempting to escalate the account %s to account type %s which has higher privileges", role.getName(), account.getAccountName(), role.getRoleType().getAccountType().name()));
|
||||
/*
|
||||
Role change should follow the below conditions:
|
||||
- Caller should not be of Unknown role type
|
||||
- New role's type should not be Unknown
|
||||
- Caller should not be able to escalate or de-escalate an account's role which is of higher role type
|
||||
- New role should not be of type Admin with domain other than ROOT domain
|
||||
*/
|
||||
protected void validateRoleChange(Account account, Role role, Account caller) {
|
||||
Role currentRole = roleService.findRole(account.getRoleId());
|
||||
Role callerRole = roleService.findRole(caller.getRoleId());
|
||||
String errorMsg = String.format("Unable to update account role to %s, ", role.getName());
|
||||
if (RoleType.Unknown.equals(callerRole.getRoleType())) {
|
||||
throw new PermissionDeniedException(String.format("%s as the caller privileges are unknown", errorMsg));
|
||||
}
|
||||
if (RoleType.Unknown.equals(role.getRoleType())) {
|
||||
throw new PermissionDeniedException(String.format("%s as the new role privileges are unknown", errorMsg));
|
||||
}
|
||||
if (!callerRole.getRoleType().equals(RoleType.Admin) &&
|
||||
(role.getRoleType().ordinal() < callerRole.getRoleType().ordinal() ||
|
||||
currentRole.getRoleType().ordinal() < callerRole.getRoleType().ordinal())) {
|
||||
throw new PermissionDeniedException(String.format("%s as either current or new role has higher " +
|
||||
"privileges than the caller", errorMsg));
|
||||
}
|
||||
if (role.getRoleType().equals(RoleType.Admin) && account.getDomainId() != Domain.ROOT_DOMAIN) {
|
||||
throw new PermissionDeniedException(String.format("%s as the user does not belong to the ROOT domain",
|
||||
errorMsg));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2136,7 +2153,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
}
|
||||
|
||||
Role role = roleService.findRole(roleId);
|
||||
isValidRoleChange(account, role);
|
||||
validateRoleChange(account, role, caller);
|
||||
acctForUpdate.setRoleId(roleId);
|
||||
acctForUpdate.setType(role.getRoleType().getAccountType());
|
||||
checkRoleEscalation(getCurrentCallingAccount(), acctForUpdate);
|
||||
|
||||
@ -742,8 +742,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
}
|
||||
|
||||
private class VmIpAddrFetchThread extends ManagedContextRunnable {
|
||||
|
||||
|
||||
long nicId;
|
||||
long vmId;
|
||||
String vmName;
|
||||
@ -766,7 +764,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
boolean decrementCount = true;
|
||||
|
||||
try {
|
||||
logger.debug("Trying for vm "+ vmId +" nic Id "+nicId +" ip retrieval ...");
|
||||
logger.debug("Trying IP retrieval for VM {} ({}), nic Id {}", vmName, vmId, nicId);
|
||||
Answer answer = _agentMgr.send(hostId, cmd);
|
||||
NicVO nic = _nicDao.findById(nicId);
|
||||
if (answer.getResult()) {
|
||||
@ -777,12 +775,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
if (nic != null) {
|
||||
nic.setIPv4Address(vmIp);
|
||||
_nicDao.update(nicId, nic);
|
||||
logger.debug("Vm "+ vmId +" IP "+vmIp +" got retrieved successfully");
|
||||
logger.debug("VM {} ({}) - IP {} retrieved successfully", vmName, vmId, vmIp);
|
||||
vmIdCountMap.remove(nicId);
|
||||
decrementCount = false;
|
||||
ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM,
|
||||
Domain.ROOT_DOMAIN, EventTypes.EVENT_NETWORK_EXTERNAL_DHCP_VM_IPFETCH,
|
||||
"VM " + vmId + " nic id " + nicId + " ip address " + vmIp + " got fetched successfully", vmId, ApiCommandResourceType.VirtualMachine.toString());
|
||||
"VM " + vmId + ", nic id " + nicId + ", IP address " + vmIp + " fetched successfully", vmId, ApiCommandResourceType.VirtualMachine.toString());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -793,7 +791,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
_nicDao.update(nicId, nic);
|
||||
}
|
||||
if (answer.getDetails() != null) {
|
||||
logger.debug("Failed to get vm ip for Vm "+ vmId + answer.getDetails());
|
||||
logger.debug("Failed to get vm ip for Vm {} ({}), details: {}", vmName, vmId, answer.getDetails());
|
||||
}
|
||||
}
|
||||
} catch (OperationTimedoutException e) {
|
||||
@ -804,7 +802,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
if (decrementCount) {
|
||||
VmAndCountDetails vmAndCount = vmIdCountMap.get(nicId);
|
||||
vmAndCount.decrementCount();
|
||||
logger.debug("Ip is not retrieved for VM " + vmId +" nic "+nicId + " ... decremented count to "+vmAndCount.getRetrievalCount());
|
||||
logger.debug("Ip is not retrieved for VM {} ({}) nic {} ... decremented count to {}", vmName, vmId, nicId, vmAndCount.getRetrievalCount());
|
||||
vmIdCountMap.put(nicId, vmAndCount);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@ import com.cloud.storage.dao.VMTemplatePoolDao;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
import com.cloud.utils.security.CertificateHelper;
|
||||
|
||||
import sun.security.x509.X509CertImpl;
|
||||
@ -469,10 +470,18 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
|
||||
@Override
|
||||
public Pair<DirectDownloadCertificate, List<HostCertificateStatus>> uploadCertificateToHosts(
|
||||
String certificateCer, String alias, String hypervisor, Long zoneId, Long hostId) {
|
||||
if (alias != null && (alias.equalsIgnoreCase("cloud") || alias.startsWith("cloudca"))) {
|
||||
if (StringUtils.isBlank(alias)) {
|
||||
throw new CloudRuntimeException("Certificate name not provided, please provide a valid name");
|
||||
}
|
||||
|
||||
if (alias.equalsIgnoreCase("cloud") || alias.startsWith("cloudca")) {
|
||||
throw new CloudRuntimeException("Please provide a different alias name for the certificate");
|
||||
}
|
||||
|
||||
if (!NetUtils.verifyDomainNameLabel(alias, false)) {
|
||||
throw new CloudRuntimeException("The provided certificate name is invalid, please provide a valid name");
|
||||
}
|
||||
|
||||
List<HostVO> hosts;
|
||||
DirectDownloadCertificateVO certificateVO;
|
||||
HypervisorType hypervisorType = HypervisorType.getType(hypervisor);
|
||||
|
||||
@ -27,11 +27,14 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
||||
@ -50,6 +53,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
|
||||
import com.cloud.acl.DomainChecker;
|
||||
@ -120,6 +124,8 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
||||
|
||||
@Mock
|
||||
ConfigKey<Boolean> enableUserTwoFactorAuthenticationMock;
|
||||
@Mock
|
||||
RoleService roleService;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
@ -1225,4 +1231,112 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
||||
accountManagerImpl.deleteWebhooksForAccount(1L);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = PermissionDeniedException.class)
|
||||
public void testValidateRoleChangeUnknownCaller() {
|
||||
Account account = Mockito.mock(Account.class);
|
||||
Mockito.when(account.getRoleId()).thenReturn(1L);
|
||||
Role role = Mockito.mock(Role.class);
|
||||
Mockito.when(role.getRoleType()).thenReturn(RoleType.Unknown);
|
||||
Account caller = Mockito.mock(Account.class);
|
||||
Mockito.when(caller.getRoleId()).thenReturn(2L);
|
||||
Mockito.when(roleService.findRole(2L)).thenReturn(role);
|
||||
accountManagerImpl.validateRoleChange(account, Mockito.mock(Role.class), caller);
|
||||
}
|
||||
|
||||
@Test(expected = PermissionDeniedException.class)
|
||||
public void testValidateRoleChangeUnknownNewRole() {
|
||||
Account account = Mockito.mock(Account.class);
|
||||
Mockito.when(account.getRoleId()).thenReturn(1L);
|
||||
Role newRole = Mockito.mock(Role.class);
|
||||
Mockito.when(newRole.getRoleType()).thenReturn(RoleType.Unknown);
|
||||
Role callerRole = Mockito.mock(Role.class);
|
||||
Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin);
|
||||
Account caller = Mockito.mock(Account.class);
|
||||
Mockito.when(caller.getRoleId()).thenReturn(2L);
|
||||
Mockito.when(roleService.findRole(2L)).thenReturn(callerRole);
|
||||
accountManagerImpl.validateRoleChange(account, newRole, caller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRoleNewRoleSameCaller() {
|
||||
Account account = Mockito.mock(Account.class);
|
||||
Mockito.when(account.getRoleId()).thenReturn(1L);
|
||||
Role currentRole = Mockito.mock(Role.class);
|
||||
Mockito.when(currentRole.getRoleType()).thenReturn(RoleType.User);
|
||||
Mockito.when(roleService.findRole(1L)).thenReturn(currentRole);
|
||||
Role newRole = Mockito.mock(Role.class);
|
||||
Mockito.when(newRole.getRoleType()).thenReturn(RoleType.DomainAdmin);
|
||||
Role callerRole = Mockito.mock(Role.class);
|
||||
Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin);
|
||||
Account caller = Mockito.mock(Account.class);
|
||||
Mockito.when(caller.getRoleId()).thenReturn(2L);
|
||||
Mockito.when(roleService.findRole(2L)).thenReturn(callerRole);
|
||||
accountManagerImpl.validateRoleChange(account, newRole, caller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRoleCurrentRoleSameCaller() {
|
||||
Account account = Mockito.mock(Account.class);
|
||||
Mockito.when(account.getRoleId()).thenReturn(1L);
|
||||
Role accountRole = Mockito.mock(Role.class);
|
||||
Mockito.when(accountRole.getRoleType()).thenReturn(RoleType.DomainAdmin);
|
||||
Role newRole = Mockito.mock(Role.class);
|
||||
Mockito.when(newRole.getRoleType()).thenReturn(RoleType.User);
|
||||
Role callerRole = Mockito.mock(Role.class);
|
||||
Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin);
|
||||
Account caller = Mockito.mock(Account.class);
|
||||
Mockito.when(caller.getRoleId()).thenReturn(2L);
|
||||
Mockito.when(roleService.findRole(1L)).thenReturn(accountRole);
|
||||
Mockito.when(roleService.findRole(2L)).thenReturn(callerRole);
|
||||
accountManagerImpl.validateRoleChange(account, newRole, caller);
|
||||
}
|
||||
|
||||
@Test(expected = PermissionDeniedException.class)
|
||||
public void testValidateRoleNewRoleHigherCaller() {
|
||||
Account account = Mockito.mock(Account.class);
|
||||
Mockito.when(account.getRoleId()).thenReturn(1L);
|
||||
Role newRole = Mockito.mock(Role.class);
|
||||
Mockito.when(newRole.getRoleType()).thenReturn(RoleType.Admin);
|
||||
Role callerRole = Mockito.mock(Role.class);
|
||||
Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin);
|
||||
Account caller = Mockito.mock(Account.class);
|
||||
Mockito.when(caller.getRoleId()).thenReturn(2L);
|
||||
Mockito.when(roleService.findRole(2L)).thenReturn(callerRole);
|
||||
accountManagerImpl.validateRoleChange(account, newRole, caller);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateRoleNewRoleLowerCaller() {
|
||||
Account account = Mockito.mock(Account.class);
|
||||
Mockito.when(account.getRoleId()).thenReturn(1L);
|
||||
Role newRole = Mockito.mock(Role.class);
|
||||
Mockito.when(newRole.getRoleType()).thenReturn(RoleType.User);
|
||||
Role accountRole = Mockito.mock(Role.class);
|
||||
Mockito.when(accountRole.getRoleType()).thenReturn(RoleType.User);
|
||||
Role callerRole = Mockito.mock(Role.class);
|
||||
Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.DomainAdmin);
|
||||
Account caller = Mockito.mock(Account.class);
|
||||
Mockito.when(caller.getRoleId()).thenReturn(2L);
|
||||
Mockito.when(roleService.findRole(1L)).thenReturn(accountRole);
|
||||
Mockito.when(roleService.findRole(2L)).thenReturn(callerRole);
|
||||
accountManagerImpl.validateRoleChange(account, newRole, caller);
|
||||
}
|
||||
|
||||
@Test(expected = PermissionDeniedException.class)
|
||||
public void testValidateRoleAdminCannotEscalateAdminFromNonRootDomain() {
|
||||
Account account = Mockito.mock(Account.class);
|
||||
Mockito.when(account.getRoleId()).thenReturn(1L);
|
||||
Mockito.when(account.getDomainId()).thenReturn(2L);
|
||||
Role newRole = Mockito.mock(Role.class);
|
||||
Mockito.when(newRole.getRoleType()).thenReturn(RoleType.Admin);
|
||||
Role accountRole = Mockito.mock(Role.class);
|
||||
Role callerRole = Mockito.mock(Role.class);
|
||||
Mockito.when(callerRole.getRoleType()).thenReturn(RoleType.Admin);
|
||||
Account caller = Mockito.mock(Account.class);
|
||||
Mockito.when(caller.getRoleId()).thenReturn(2L);
|
||||
Mockito.when(roleService.findRole(1L)).thenReturn(accountRole);
|
||||
Mockito.when(roleService.findRole(2L)).thenReturn(callerRole);
|
||||
accountManagerImpl.validateRoleChange(account, newRole, caller);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1646,7 +1646,7 @@ def main(argv):
|
||||
("dhcp", {"process_iptables": False, "executor": [CsDhcp("dhcpentry", config)]}),
|
||||
("load_balancer", {"process_iptables": True, "executor": []}),
|
||||
("monitor_service", {"process_iptables": False, "executor": [CsMonitor("monitorservice", config)]}),
|
||||
("static_routes", {"process_iptables": False, "executor": [CsStaticRoutes("staticroutes", config)]})
|
||||
("static_routes", {"process_iptables": True, "executor": [CsStaticRoutes("staticroutes", config)]})
|
||||
])
|
||||
|
||||
if not config.is_vpc():
|
||||
|
||||
@ -1530,6 +1530,7 @@
|
||||
"label.networkspeed": "Network speed",
|
||||
"label.networktype": "Network type",
|
||||
"label.networkwrite": "Network write",
|
||||
"label.never": "Never",
|
||||
"label.new": "New",
|
||||
"label.new.autoscale.vmgroup": "New AutoScaling Group",
|
||||
"label.new.instance.group": "New Instance group",
|
||||
|
||||
@ -58,6 +58,12 @@
|
||||
v-if="item.meta.icon && typeof (item.meta.icon) === 'string'"
|
||||
:icon="item.meta.icon"
|
||||
@click="() => { handleClickParentMenu(item) }" />
|
||||
<font-awesome-icon
|
||||
v-else-if="item.meta.icon && Array.isArray(item.meta.icon)"
|
||||
:icon="item.meta.icon"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]"
|
||||
@click="() => { handleClickParentMenu(item) }" />
|
||||
<span>{{ $t(item.meta.title) }}</span>
|
||||
</router-link>
|
||||
</a-menu-item>
|
||||
|
||||
@ -34,12 +34,12 @@
|
||||
<resource-icon :image="getImage(resource.icon && resource.icon.base64image || images.template || images.iso || resourceIcon)" size="4x" style="margin-right: 5px"/>
|
||||
</span>
|
||||
<span v-else>
|
||||
<os-logo v-if="resource.ostypeid || resource.ostypename" :osId="resource.ostypeid" :osName="resource.ostypename" size="4x" @update-osname="setResourceOsType"/>
|
||||
<os-logo v-if="resource.ostypeid || resource.ostypename" :osId="resource.ostypeid" :osName="resource.ostypename" size="3x" @update-osname="setResourceOsType"/>
|
||||
<render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 36px" :icon="$route.meta.icon" />
|
||||
<font-awesome-icon
|
||||
v-else-if="$route.meta.icon && Array.isArray($route.meta.icon)"
|
||||
:icon="$route.meta.icon"
|
||||
size="4x"
|
||||
size="3x"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]" />
|
||||
<render-icon v-else style="font-size: 36px" :svgIcon="$route.meta.icon" />
|
||||
@ -185,7 +185,10 @@
|
||||
<div class="resource-detail-item" v-if="('cpunumber' in resource && 'cpuspeed' in resource) || resource.cputotal">
|
||||
<div class="resource-detail-item__label">{{ $t('label.cpu') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<appstore-outlined />
|
||||
<font-awesome-icon
|
||||
:icon="['fa-solid', 'fa-microchip']"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]" />
|
||||
<span v-if="'cpunumber' in resource && 'cpuspeed' in resource">{{ resource.cpunumber }} CPU x {{ (resource.cpuspeed / 1000.0).toFixed(2) }} GHz
|
||||
<a-tooltip placement="top">
|
||||
<template #title>
|
||||
@ -223,7 +226,11 @@
|
||||
<div class="resource-detail-item" v-if="'memory' in resource">
|
||||
<div class="resource-detail-item__label">{{ $t('label.memory') }}</div>
|
||||
<div class="resource-detail-item__details">
|
||||
<bulb-outlined />{{ resource.memory + ' ' + $t('label.mb.memory') }}
|
||||
<font-awesome-icon
|
||||
:icon="['fa-solid', 'fa-memory']"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]" />
|
||||
{{ resource.memory + ' ' + $t('label.mb.memory') }}
|
||||
</div>
|
||||
<div>
|
||||
<span v-if="resource.memorykbs && resource.memoryintfreekbs">
|
||||
@ -364,7 +371,10 @@
|
||||
v-for="(eth, index) in resource.nic"
|
||||
:key="eth.id"
|
||||
style="margin-left: -24px; margin-top: 5px;">
|
||||
<api-outlined />
|
||||
<font-awesome-icon
|
||||
:icon="['fa-solid', 'fa-ethernet']"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]" />
|
||||
<strong>eth{{ index }}</strong>
|
||||
<copy-label :label="eth.ip6address ? eth.ipaddress + ', ' + eth.ip6address : eth.ipaddress" />
|
||||
<a-tag v-if="eth.isdefault">
|
||||
@ -389,7 +399,11 @@
|
||||
v-for="network in resource.networks"
|
||||
:key="network.id"
|
||||
style="margin-top: 5px;">
|
||||
<api-outlined />{{ network.name }}
|
||||
<font-awesome-icon
|
||||
:icon="['fa-solid', 'fa-ethernet']"
|
||||
class="anticon"
|
||||
:style="[$store.getters.darkMode ? { color: 'rgba(255, 255, 255, 0.65)' } : { color: '#888' }]" />
|
||||
{{ network.name }}
|
||||
<span v-if="resource.defaultnetworkid === network.id">
|
||||
({{ $t('label.default') }})
|
||||
</span>
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
:rowSelection="explicitlyAllowRowSelection || enableGroupAction() || $route.name === 'event' ? {selectedRowKeys: selectedRowKeys, onChange: onSelectChange, columnWidth: 30} : null"
|
||||
:rowClassName="getRowClassName"
|
||||
@resizeColumn="handleResizeColumn"
|
||||
style="overflow-y: auto"
|
||||
:style="{ 'overflow-y': this.$route.name === 'usage' ? 'hidden' : 'auto' }"
|
||||
>
|
||||
<template #customFilterDropdown>
|
||||
<div style="padding: 8px" class="filter-dropdown">
|
||||
@ -44,7 +44,7 @@
|
||||
<span v-if="record.icon && record.icon.base64image">
|
||||
<resource-icon :image="record.icon.base64image" size="2x"/>
|
||||
</span>
|
||||
<os-logo v-else :osId="record.ostypeid" :osName="record.osdisplayname" size="2x" />
|
||||
<os-logo v-else :osId="record.ostypeid" :osName="record.osdisplayname" size="xl" />
|
||||
</span>
|
||||
<span style="min-width: 120px" >
|
||||
<QuickView
|
||||
@ -58,12 +58,12 @@
|
||||
</span>
|
||||
<span v-if="$showIcon() && !['vm', 'vnfapp'].includes($route.path.split('/')[1])" style="margin-right: 5px">
|
||||
<resource-icon v-if="$showIcon() && record.icon && record.icon.base64image" :image="record.icon.base64image" size="2x"/>
|
||||
<os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="2x" />
|
||||
<os-logo v-else-if="record.ostypename" :osName="record.ostypename" size="xl" />
|
||||
<render-icon v-else-if="typeof $route.meta.icon ==='string'" style="font-size: 16px;" :icon="$route.meta.icon"/>
|
||||
<render-icon v-else style="font-size: 16px;" :svgIcon="$route.meta.icon" />
|
||||
</span>
|
||||
<span v-else :style="{ 'margin-right': record.ostypename ? '5px' : '0' }">
|
||||
<os-logo v-if="record.ostypename" :osName="record.ostypename" size="1x" />
|
||||
<os-logo v-if="record.ostypename" :osName="record.ostypename" size="xl" />
|
||||
</span>
|
||||
|
||||
<span v-if="record.hasannotations">
|
||||
|
||||
@ -769,7 +769,7 @@ export default {
|
||||
fields.push(...['domain', 'zonename'])
|
||||
return fields
|
||||
},
|
||||
details: ['ipaddress', 'id', 'associatednetworkname', 'virtualmachinename', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'domain', 'zonename'],
|
||||
details: ['ipaddress', 'id', 'associatednetworkname', 'networkid', 'issourcenat', 'isstaticnat', 'virtualmachinename', 'vmipaddress', 'vlan', 'allocated', 'account', 'domain', 'zonename'],
|
||||
filters: ['allocated', 'reserved', 'free'],
|
||||
component: shallowRef(() => import('@/views/network/PublicIpResource.vue')),
|
||||
tabs: [{
|
||||
|
||||
@ -18,15 +18,11 @@
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
|
||||
// import { fab } from '@fortawesome/free-brands-svg-icons'
|
||||
// import { fas } from '@fortawesome/free-solid-svg-icons'
|
||||
// import { far } from '@fortawesome/free-regular-svg-icons'
|
||||
|
||||
import { faCentos, faUbuntu, faDebian, faSuse, faRedhat, faFedora, faLinux, faFreebsd, faApple, faWindows, faJava } from '@fortawesome/free-brands-svg-icons'
|
||||
import { faCompactDisc, faCameraRetro, faDharmachakra } from '@fortawesome/free-solid-svg-icons'
|
||||
import { fas, faCompactDisc, faCameraRetro, faDharmachakra } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(faCentos, faUbuntu, faDebian, faSuse, faRedhat, faFedora, faLinux, faFreebsd, faApple, faWindows, faJava)
|
||||
library.add(faCompactDisc, faCameraRetro, faDharmachakra)
|
||||
library.add(fas, faCompactDisc, faCameraRetro, faDharmachakra)
|
||||
|
||||
export default {
|
||||
install: (app) => {
|
||||
|
||||
@ -50,6 +50,7 @@
|
||||
<a-row justify="end">
|
||||
<a-col>
|
||||
<tooltip-button
|
||||
v-if="'generateUsageRecords' in $store.getters.apis"
|
||||
type="primary"
|
||||
icon="hdd-outlined"
|
||||
:tooltip="$t('label.usage.records.generate')"
|
||||
@ -58,6 +59,7 @@
|
||||
</a-col>
|
||||
<a-col>
|
||||
<tooltip-button
|
||||
v-if="'removeRawUsageRecords' in $store.getters.apis"
|
||||
type="danger"
|
||||
icon="delete-outlined"
|
||||
:tooltip="$t('label.usage.records.purge')"
|
||||
@ -70,7 +72,7 @@
|
||||
</a-card>
|
||||
</a-affix>
|
||||
<a-col>
|
||||
<a-card size="small" :loading="serverMetricsLoading">
|
||||
<a-card size="small" :loading="serverMetricsLoading" v-if="'listUsageServerMetrics' in $store.getters.apis">
|
||||
<a-row justify="space-around">
|
||||
<a-card-grid style="width: 30%; text-align: center; font-size: small;">
|
||||
<a-statistic
|
||||
@ -86,10 +88,10 @@
|
||||
<a-card-grid style="width: 35%; text-align: center; font-size: small;">
|
||||
<a-statistic
|
||||
:title="$t('label.lastheartbeat')"
|
||||
:value="$toLocaleDate(serverStats.lastheartbeat)"
|
||||
:value="serverStats.lastheartbeat ? $toLocaleDate(serverStats.lastheartbeat) : $t('label.never')"
|
||||
valueStyle="font-size: medium"
|
||||
/>
|
||||
<a-card-meta :description="getTimeSince(serverStats.collectiontime)" />
|
||||
<a-card-meta v-if="!!serverStats.lastheartbeat" :description="getTimeSince(serverStats.collectiontime)" />
|
||||
</a-card-grid>
|
||||
<a-card-grid style="width: 35%; text-align: center; font-size: small;">
|
||||
<a-statistic
|
||||
@ -159,7 +161,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="3">
|
||||
<a-col :span="3" v-if="'listUsageTypes' in $store.getters.apis">
|
||||
<a-form-item
|
||||
ref="type"
|
||||
name="type"
|
||||
@ -173,7 +175,7 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="3">
|
||||
<a-col :span="3" v-if="'listUsageTypes' in $store.getters.apis">
|
||||
<a-form-item
|
||||
ref="id"
|
||||
name="id"
|
||||
@ -501,6 +503,11 @@ export default {
|
||||
}
|
||||
},
|
||||
listUsageServerMetrics () {
|
||||
if (!('listUsageServerMetrics' in this.$store.getters.apis)) {
|
||||
this.serverMetricsLoading = false
|
||||
return
|
||||
}
|
||||
|
||||
this.serverMetricsLoading = true
|
||||
api('listUsageServerMetrics').then(json => {
|
||||
this.stats = []
|
||||
@ -637,6 +644,10 @@ export default {
|
||||
})
|
||||
},
|
||||
getUsageTypes () {
|
||||
if (!('listUsageTypes' in this.$store.getters.apis)) {
|
||||
return
|
||||
}
|
||||
|
||||
api('listUsageTypes').then(json => {
|
||||
if (json && json.listusagetypesresponse && json.listusagetypesresponse.usagetype) {
|
||||
this.usageTypes = [{ id: null, value: '' }, ...json.listusagetypesresponse.usagetype.map(x => {
|
||||
|
||||
@ -452,7 +452,7 @@
|
||||
<a-form-item
|
||||
name="conservemode"
|
||||
ref="conservemode"
|
||||
v-if="(guestType === 'shared' || guestType === 'isolated') && !isVpcVirtualRouterForAtLeastOneService && !forNsx && networkmode !== 'ROUTED'">
|
||||
v-if="(guestType === 'shared' || guestType === 'isolated') && !forNsx && networkmode !== 'ROUTED'">
|
||||
<template #label>
|
||||
<tooltip-label :title="$t('label.conservemode')" :tooltip="apiParams.conservemode.description"/>
|
||||
</template>
|
||||
|
||||
@ -102,6 +102,10 @@ public class NetUtils {
|
||||
public final static int IPV6_EUI64_11TH_BYTE = -1;
|
||||
public final static int IPV6_EUI64_12TH_BYTE = -2;
|
||||
|
||||
// Regex
|
||||
public final static Pattern HOSTNAME_PATTERN = Pattern.compile("[a-zA-Z0-9-]+");
|
||||
public final static Pattern START_HOSTNAME_PATTERN = Pattern.compile("^[0-9-].*");
|
||||
|
||||
public static String extractHost(String uri) throws URISyntaxException {
|
||||
return (new URI(uri)).getHost();
|
||||
}
|
||||
@ -1064,13 +1068,13 @@ public class NetUtils {
|
||||
if (hostName.length() > 63 || hostName.length() < 1) {
|
||||
LOGGER.warn("Domain name label must be between 1 and 63 characters long");
|
||||
return false;
|
||||
} else if (!hostName.toLowerCase().matches("[a-z0-9-]*")) {
|
||||
} else if (!HOSTNAME_PATTERN.matcher(hostName).matches()) {
|
||||
LOGGER.warn("Domain name label may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner)");
|
||||
return false;
|
||||
} else if (hostName.startsWith("-") || hostName.endsWith("-")) {
|
||||
LOGGER.warn("Domain name label can not start with a hyphen and digit, and must not end with a hyphen");
|
||||
LOGGER.warn("Domain name label can not start or end with a hyphen");
|
||||
return false;
|
||||
} else if (isHostName && hostName.matches("^[0-9-].*")) {
|
||||
} else if (isHostName && START_HOSTNAME_PATTERN.matcher(hostName).matches()) {
|
||||
LOGGER.warn("Host name can't start with digit");
|
||||
return false;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user