Merge branch '4.19' into 4.20

This commit is contained in:
Daan Hoogland 2024-12-20 14:34:39 +01:00
commit b7f0aac519
35 changed files with 399 additions and 128 deletions

View File

@ -74,6 +74,7 @@ public interface VmDetailConstants {
String ENCRYPTED_PASSWORD = "Encrypted.Password"; String ENCRYPTED_PASSWORD = "Encrypted.Password";
String CONFIG_DRIVE_LOCATION = "configDriveLocation"; String CONFIG_DRIVE_LOCATION = "configDriveLocation";
String LAST_CONFIG_DRIVE_LOCATION = "lastConfigDriveLocation";
String SKIP_DRS = "skipFromDRS"; String SKIP_DRS = "skipFromDRS";

View File

@ -47,6 +47,7 @@ import com.cloud.agent.api.CleanupNetworkRulesCmd;
import com.cloud.agent.api.Command; import com.cloud.agent.api.Command;
import com.cloud.agent.api.CreateStoragePoolCommand; import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.DeleteStoragePoolCommand; import com.cloud.agent.api.DeleteStoragePoolCommand;
import com.cloud.agent.api.HandleConfigDriveIsoCommand;
import com.cloud.agent.api.MaintainCommand; import com.cloud.agent.api.MaintainCommand;
import com.cloud.agent.api.MigrateCommand; import com.cloud.agent.api.MigrateCommand;
import com.cloud.agent.api.ModifySshKeysCommand; 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(), 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(), StopCommand.class.toString(), CheckVirtualMachineCommand.class.toString(), PingTestCommand.class.toString(), CheckHealthCommand.class.toString(),
ReadyCommand.class.toString(), ShutdownCommand.class.toString(), SetupCommand.class.toString(), ReadyCommand.class.toString(), ShutdownCommand.class.toString(), SetupCommand.class.toString(), CleanupNetworkRulesCmd.class.toString(),
CleanupNetworkRulesCmd.class.toString(), CheckNetworkCommand.class.toString(), PvlanSetupCommand.class.toString(), CheckOnHostCommand.class.toString(), CheckNetworkCommand.class.toString(), PvlanSetupCommand.class.toString(), CheckOnHostCommand.class.toString(), ModifyTargetsCommand.class.toString(),
ModifyTargetsCommand.class.toString(), ModifySshKeysCommand.class.toString(), ModifySshKeysCommand.class.toString(), CreateStoragePoolCommand.class.toString(), DeleteStoragePoolCommand.class.toString(), ModifyStoragePoolCommand.class.toString(),
CreateStoragePoolCommand.class.toString(), DeleteStoragePoolCommand.class.toString(), ModifyStoragePoolCommand.class.toString(), SetupMSListCommand.class.toString(), RollingMaintenanceCommand.class.toString(), CleanupPersistentNetworkResourceCommand.class.toString(), HandleConfigDriveIsoCommand.class.toString()};
SetupMSListCommand.class.toString(), RollingMaintenanceCommand.class.toString(), CleanupPersistentNetworkResourceCommand.class.toString()};
protected final static String[] s_commandsNotAllowedInConnectingMode = new String[] { StartCommand.class.toString(), CreateCommand.class.toString() }; protected final static String[] s_commandsNotAllowedInConnectingMode = new String[] { StartCommand.class.toString(), CreateCommand.class.toString() };
static { static {
Arrays.sort(s_commandsAllowedInMaintenanceMode); Arrays.sort(s_commandsAllowedInMaintenanceMode);

View File

@ -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}%{_datadir}/%{name}-management/setup
mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management mkdir -p ${RPM_BUILD_ROOT}%{_localstatedir}/log/%{name}/management
mkdir -p ${RPM_BUILD_ROOT}%{_sysconfdir}/%{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}%{_localstatedir}/run
mkdir -p ${RPM_BUILD_ROOT}%{_datadir}/%{name}-management/setup/wheel 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-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/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.service ${RPM_BUILD_ROOT}%{_unitdir}/%{name}-management.service
install -D packaging/systemd/cloudstack-management.default ${RPM_BUILD_ROOT}%{_sysconfdir}/default/%{name}-management 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 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}/default/%{name}-management
%config(noreplace) %{_sysconfdir}/sudoers.d/%{name}-management %config(noreplace) %{_sysconfdir}/sudoers.d/%{name}-management
%config(noreplace) %{_sysconfdir}/security/limits.d/cloud %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/db.properties
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/server.properties %config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/server.properties
%config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/config.json %config(noreplace) %attr(0640,root,cloud) %{_sysconfdir}/%{name}/management/config.json

View 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

View File

@ -1031,7 +1031,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
hostHealthCheckScriptPath = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEALTH_CHECK_SCRIPT_PATH); hostHealthCheckScriptPath = AgentPropertiesFileHandler.getPropertyValue(AgentProperties.HEALTH_CHECK_SCRIPT_PATH);
if (StringUtils.isNotBlank(hostHealthCheckScriptPath) && !new File(hostHealthCheckScriptPath).exists()) { 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)); "discarding it", hostHealthCheckScriptPath));
} }
@ -2796,11 +2796,11 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
if (hostCpuMaxCapacity > 0) { if (hostCpuMaxCapacity > 0) {
int updatedCpuShares = (int) Math.ceil((requestedCpuShares * CGROUP_V2_UPPER_LIMIT) / (double) hostCpuMaxCapacity); 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)); "consider the host limits; the new CPU shares value is [%s].", hostCpuMaxCapacity, requestedCpuShares, updatedCpuShares));
return 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)); "converted.", requestedCpuShares));
return 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)); LOGGER.warn(String.format("Setting System VM's [%s] current memory as max memory [%s].", vmTO.toString(), maxRam));
} else { } else {
long minRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMinRam()); 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; retVal = minRam;
} }
return retVal; return retVal;
@ -3002,9 +3002,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
public static boolean useBLOCKDiskType(KVMPhysicalDisk physicalDisk) { public static boolean useBLOCKDiskType(KVMPhysicalDisk physicalDisk) {
return physicalDisk != null && return physicalDisk != null &&
physicalDisk.getPool().getType() == StoragePoolType.Linstor && physicalDisk.getPool() != null &&
StoragePoolType.Linstor.equals(physicalDisk.getPool().getType()) &&
physicalDisk.getFormat() != null && physicalDisk.getFormat() != null &&
physicalDisk.getFormat()== PhysicalDiskFormat.RAW; PhysicalDiskFormat.RAW.equals(physicalDisk.getFormat());
} }
public static DiskDef.DiskType getDiskType(KVMPhysicalDisk physicalDisk) { public static DiskDef.DiskType getDiskType(KVMPhysicalDisk physicalDisk) {
@ -3283,7 +3284,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
* (ii) Libvirt >= 6.3.0 * (ii) Libvirt >= 6.3.0
*/ */
public void setDiskIoDriver(DiskDef disk, IoDriverPolicy ioDriver) { 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 (ioDriver != null) {
if (IoDriverPolicy.IO_URING != ioDriver) { if (IoDriverPolicy.IO_URING != ioDriver) {
disk.setIoDriver(ioDriver); disk.setIoDriver(ioDriver);
@ -3426,13 +3427,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
} }
if (configdrive != null) { if (configdrive != null) {
try { 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); String result = attachOrDetachISO(conn, vmName, configdrive.getDiskPath(), false, CONFIG_DRIVE_ISO_DEVICE_ID);
if (result != null) { 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); result = attachOrDetachISO(conn, vmName, configdrive.getDiskPath(), true, CONFIG_DRIVE_ISO_DEVICE_ID);
if (result != null) { 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) { } catch (final LibvirtException | InternalErrorException | URISyntaxException e) {
final String msg = "Detach and attach ConfigDrive ISO failed due to " + e.toString(); 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, public synchronized String attachOrDetachISO(final Connect conn, final String vmName, String isoPath, final boolean isAttach, final Integer diskSeq) throws LibvirtException, URISyntaxException,
InternalErrorException { InternalErrorException {
final DiskDef iso = new DiskDef(); final DiskDef iso = new DiskDef();
if (isoPath != null && isAttach) { 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 int index = isoPath.lastIndexOf("/");
final String path = isoPath.substring(0, index); final String path = isoPath.substring(0, index);
final String name = isoPath.substring(index + 1); final String name = isoPath.substring(index + 1);
final KVMStoragePool secondaryPool = storagePoolManager.getStoragePoolByURI(path); final KVMStoragePool storagePool = storagePoolManager.getStoragePoolByURI(path);
final KVMPhysicalDisk isoVol = secondaryPool.getPhysicalDisk(name); final KVMPhysicalDisk isoVol = storagePool.getPhysicalDisk(name);
final DiskDef.DiskType diskType = getDiskType(isoVol); final DiskDef.DiskType diskType = getDiskType(isoVol);
isoPath = isoVol.getPath(); isoPath = isoVol.getPath();
iso.defISODisk(isoPath, diskSeq, diskType); iso.defISODisk(isoPath, diskSeq, diskType);
}
} else { } else {
iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE); iso.defISODisk(null, diskSeq, DiskDef.DiskType.FILE);
} }
@ -3640,19 +3647,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
*/ */
private HealthCheckResult getHostHealthCheckResult() { private HealthCheckResult getHostHealthCheckResult() {
if (StringUtils.isBlank(hostHealthCheckScriptPath)) { 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; return HealthCheckResult.IGNORE;
} }
File script = new File(hostHealthCheckScriptPath); File script = new File(hostHealthCheckScriptPath);
if (!script.exists() || !script.isFile() || !script.canExecute()) { 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, "reason: %s", hostHealthCheckScriptPath,
!script.exists() ? "file does not exist" : "please check file permissions to execute this file")); !script.exists() ? "file does not exist" : "please check file permissions to execute this file"));
return HealthCheckResult.IGNORE; return HealthCheckResult.IGNORE;
} }
int exitCode = executeBashScriptAndRetrieveExitValue(hostHealthCheckScriptPath); int exitCode = executeBashScriptAndRetrieveExitValue(hostHealthCheckScriptPath);
if (logger.isDebugEnabled()) { if (LOGGER.isDebugEnabled()) {
logger.debug(String.format("Host health check script exit code: %s", exitCode)); LOGGER.debug(String.format("Host health check script exit code: %s", exitCode));
} }
return retrieveHealthCheckResultFromExitCode(exitCode); return retrieveHealthCheckResultFromExitCode(exitCode);
} }
@ -3761,17 +3768,17 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
*/ */
protected void calculateHostCpuMaxCapacity(int cpuCores, Long cpuSpeed) { protected void calculateHostCpuMaxCapacity(int cpuCores, Long cpuSpeed) {
String output = Script.runSimpleBashScript(COMMAND_GET_CGROUP_HOST_VERSION); 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)) { 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); setHostCpuMaxCapacity(0);
return; 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()); 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) { private StartupStorageCommand createLocalStoragePool(String localStoragePath, String localStorageUUID, StartupRoutingCommand cmd) {
@ -3834,7 +3841,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
String sourcePath = null; String sourcePath = null;
try { try {
String mountResult = Script.runSimpleBashScript("mount | grep \"" + diskPath + "\""); 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)) { if (StringUtils.isNotEmpty(mountResult)) {
String[] res = mountResult.strip().split(" "); String[] res = mountResult.strip().split(" ");
if (res[0].contains(":")) { if (res[0].contains(":")) {
@ -3851,7 +3858,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return new Pair<>(sourceHostIp, sourcePath); return new Pair<>(sourceHostIp, sourcePath);
} }
} catch (Exception ex) { } 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; return null;
} }
@ -3864,14 +3871,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
domainNames.add(names[i]); domainNames.add(names[i]);
} }
} catch (final LibvirtException e) { } catch (final LibvirtException e) {
logger.warn("Failed to list defined domains", e); LOGGER.warn("Failed to list defined domains", e);
} }
int[] ids = null; int[] ids = null;
try { try {
ids = conn.listDomains(); ids = conn.listDomains();
} catch (final LibvirtException e) { } catch (final LibvirtException e) {
logger.warn("Failed to list domains", e); LOGGER.warn("Failed to list domains", e);
return domainNames; 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, public boolean setupTungstenVRouter(final String oper, final String inf, final String subnet, final String route,
final String vrf) { final String vrf) {
final Script cmd = new Script(setupTungstenVrouterPath, timeout, logger); final Script cmd = new Script(setupTungstenVrouterPath, timeout, LOGGER);
cmd.add(oper); cmd.add(oper);
cmd.add(inf); cmd.add(inf);
cmd.add(subnet); cmd.add(subnet);
@ -4972,7 +4979,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
public boolean updateTungstenLoadbalancerStats(final String lbUuid, final String lbStatsPort, public boolean updateTungstenLoadbalancerStats(final String lbUuid, final String lbStatsPort,
final String lbStatsUri, final String lbStatsAuth) { 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(lbUuid);
cmd.add(lbStatsPort); cmd.add(lbStatsPort);
cmd.add(lbStatsUri); cmd.add(lbStatsUri);
@ -4984,7 +4991,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
public boolean updateTungstenLoadbalancerSsl(final String lbUuid, final String sslCertName, public boolean updateTungstenLoadbalancerSsl(final String lbUuid, final String sslCertName,
final String certificateKey, final String privateKey, final String privateIp, final String port) { 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(lbUuid);
cmd.add(sslCertName); cmd.add(sslCertName);
cmd.add(certificateKey); 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) { 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("setup_tf_route.py");
setupTfRouteScript.add(privateIpAddress); setupTfRouteScript.add(privateIpAddress);
setupTfRouteScript.add(fromNetwork); setupTfRouteScript.add(fromNetwork);
@ -5006,7 +5013,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
final OutputInterpreter.OneLineParser setupTfRouteParser = new OutputInterpreter.OneLineParser(); final OutputInterpreter.OneLineParser setupTfRouteParser = new OutputInterpreter.OneLineParser();
final String result = setupTfRouteScript.execute(setupTfRouteParser); final String result = setupTfRouteScript.execute(setupTfRouteParser);
if (result != null) { if (result != null) {
logger.debug("Failed to execute setup TF Route:" + result); LOGGER.debug("Failed to execute setup TF Route:" + result);
return false; return false;
} }
return true; return true;
@ -5525,7 +5532,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
interfaceDef.setMultiQueueNumber(nicMultiqueueNumberInteger); interfaceDef.setMultiQueueNumber(nicMultiqueueNumberInteger);
} }
} catch (NumberFormatException ex) { } 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); String nicPackedEnabled = details.get(VmDetailConstants.NIC_PACKED_VIRTQUEUES_ENABLED);
@ -5533,7 +5540,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
try { try {
interfaceDef.setPackedVirtQueues(Boolean.valueOf(nicPackedEnabled)); interfaceDef.setPackedVirtQueues(Boolean.valueOf(nicPackedEnabled));
} catch (NumberFormatException ex) { } 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(remoteFile);
command.append(" " + tmpPath); command.append(" " + tmpPath);
command.append(outputFile); 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); 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); 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; return outputFile;
} catch (Exception e) { } catch (Exception e) {
try { try {

View File

@ -39,6 +39,9 @@ public final class LibvirtGetVmIpAddressCommandWrapper extends CommandWrapper<Ge
String ip = null; String ip = null;
boolean result = false; boolean result = false;
String vmName = command.getVmName(); String vmName = command.getVmName();
if (!NetUtils.verifyDomainNameLabel(vmName, true)) {
return new Answer(command, result, ip);
}
String sanitizedVmName = sanitizeBashCommandArgument(vmName); String sanitizedVmName = sanitizeBashCommandArgument(vmName);
String networkCidr = command.getVmNetworkCidr(); String networkCidr = command.getVmNetworkCidr();
List<String[]> commands = new ArrayList<>(); List<String[]> commands = new ArrayList<>();

View File

@ -118,7 +118,7 @@ public final class LibvirtPrepareForMigrationCommandWrapper extends CommandWrapp
skipDisconnect = true; skipDisconnect = true;
if (!storagePoolMgr.connectPhysicalDisksViaVmSpec(vm)) { if (!storagePoolMgr.connectPhysicalDisksViaVmSpec(vm, true)) {
return new PrepareForMigrationAnswer(command, "failed to connect physical disks to host"); return new PrepareForMigrationAnswer(command, "failed to connect physical disks to host");
} }

View File

@ -36,6 +36,7 @@ import com.cloud.resource.ResourceWrapper;
import com.cloud.utils.FileUtil; import com.cloud.utils.FileUtil;
import com.cloud.utils.PropertiesUtil; import com.cloud.utils.PropertiesUtil;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.utils.script.Script; import com.cloud.utils.script.Script;
@ResourceWrapper(handles = SetupDirectDownloadCertificateCommand.class) @ResourceWrapper(handles = SetupDirectDownloadCertificateCommand.class)
@ -131,6 +132,9 @@ public class LibvirtSetupDirectDownloadCertificateCommandWrapper extends Command
public Answer execute(SetupDirectDownloadCertificateCommand cmd, LibvirtComputingResource serverResource) { public Answer execute(SetupDirectDownloadCertificateCommand cmd, LibvirtComputingResource serverResource) {
String certificate = cmd.getCertificate(); String certificate = cmd.getCertificate();
String certificateName = cmd.getCertificateName(); String certificateName = cmd.getCertificateName();
if (!NetUtils.verifyDomainNameLabel(certificateName, false)) {
return new Answer(cmd, false, "The provided certificate name is invalid");
}
try { try {
File agentFile = getAgentPropertiesFile(); File agentFile = getAgentPropertiesFile();

View File

@ -75,7 +75,7 @@ public final class LibvirtStartCommandWrapper extends CommandWrapper<StartComman
libvirtComputingResource.createVbd(conn, vmSpec, vmName, vm); 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"); return new StartAnswer(command, "Failed to connect physical disks to host");
} }

View File

@ -84,7 +84,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor {
} }
@Override @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 // 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); Script iScsiAdmCmd = new Script(true, "iscsiadm", 0, logger);

View File

@ -106,7 +106,7 @@ public class IscsiAdmStoragePool implements KVMStoragePool {
@Override @Override
public boolean connectPhysicalDisk(String name, Map<String, String> details) { 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 @Override

View File

@ -161,10 +161,10 @@ public class KVMStoragePoolManager {
StorageAdaptor adaptor = getStorageAdaptor(type); StorageAdaptor adaptor = getStorageAdaptor(type);
KVMStoragePool pool = adaptor.getStoragePool(poolUuid); 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; boolean result = false;
final String vmName = vmSpec.getName(); final String vmName = vmSpec.getName();
@ -187,7 +187,7 @@ public class KVMStoragePoolManager {
KVMStoragePool pool = getStoragePool(store.getPoolType(), store.getUuid()); KVMStoragePool pool = getStoragePool(store.getPoolType(), store.getUuid());
StorageAdaptor adaptor = getStorageAdaptor(pool.getType()); StorageAdaptor adaptor = getStorageAdaptor(pool.getType());
result = adaptor.connectPhysicalDisk(vol.getPath(), pool, disk.getDetails()); result = adaptor.connectPhysicalDisk(vol.getPath(), pool, disk.getDetails(), isVMMigrate);
if (!result) { if (!result) {
logger.error("Failed to connect disks via vm spec for vm: " + vmName + " volume:" + vol.toString()); 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; URI storageUri = null;
try { try {
logger.debug("Get storage pool by uri: " + uri);
storageUri = new URI(uri); storageUri = new URI(uri);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
throw new CloudRuntimeException(e.toString()); throw new CloudRuntimeException(e.toString());
@ -324,7 +325,7 @@ public class KVMStoragePoolManager {
String uuid = null; String uuid = null;
String sourceHost = ""; String sourceHost = "";
StoragePoolType protocol = null; 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"); List<String> acceptedSchemes = List.of("nfs", "networkfilesystem", "filesystem");
if (acceptedSchemes.contains(scheme)) { if (acceptedSchemes.contains(scheme)) {
sourcePath = storageUri.getPath(); sourcePath = storageUri.getPath();

View File

@ -1018,7 +1018,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor {
} }
@Override @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 // this is for managed storage that needs to prep disks prior to use
return true; return true;
} }

View File

@ -97,7 +97,7 @@ public class ManagedNfsStorageAdaptor implements StorageAdaptor {
* creates a nfs storage pool using libvirt * creates a nfs storage pool using libvirt
*/ */
@Override @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; StoragePool sp = null;
Connect conn = null; Connect conn = null;

View File

@ -182,7 +182,7 @@ public abstract class MultipathSCSIAdapterBase implements StorageAdaptor {
} }
@Override @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 + "]"); LOGGER.info("connectPhysicalDisk called for [" + volumePath + "]");
if (StringUtils.isEmpty(volumePath)) { if (StringUtils.isEmpty(volumePath)) {

View File

@ -78,7 +78,7 @@ public class MultipathSCSIPool implements KVMStoragePool {
@Override @Override
public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details) { public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details) {
return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details); return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false);
} }
@Override @Override

View File

@ -181,7 +181,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
return null; 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)); throw new CloudRuntimeException(String.format("Failed to ensure disk %s was present", name));
} }
@ -224,7 +224,7 @@ public class ScaleIOStorageAdaptor implements StorageAdaptor {
} }
@Override @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) { if (StringUtils.isEmpty(volumePath) || pool == null) {
logger.error("Unable to connect physical disk due to insufficient data"); logger.error("Unable to connect physical disk due to insufficient data");
throw new CloudRuntimeException("Unable to connect physical disk due to insufficient data"); throw new CloudRuntimeException("Unable to connect physical disk due to insufficient data");

View File

@ -89,7 +89,7 @@ public class ScaleIOStoragePool implements KVMStoragePool {
@Override @Override
public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details) { public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details) {
return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details); return storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false);
} }
@Override @Override

View File

@ -52,8 +52,15 @@ public interface StorageAdaptor {
public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool,
PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, byte[] passphrase); 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 // given disk path (per database) and pool, clean up disk on host
public boolean disconnectPhysicalDisk(String volumePath, KVMStoragePool pool); public boolean disconnectPhysicalDisk(String volumePath, KVMStoragePool pool);

View File

@ -1477,7 +1477,7 @@ public class LibvirtComputingResourceTest {
when(libvirtComputingResourceMock.getVifDriver(nicTO.getType(), nicTO.getName())).thenReturn(vifDriver); when(libvirtComputingResourceMock.getVifDriver(nicTO.getType(), nicTO.getName())).thenReturn(vifDriver);
when(libvirtComputingResourceMock.getStoragePoolMgr()).thenReturn(storagePoolManager); when(libvirtComputingResourceMock.getStoragePoolMgr()).thenReturn(storagePoolManager);
when(storagePoolManager.connectPhysicalDisksViaVmSpec(vm)).thenReturn(true); when(storagePoolManager.connectPhysicalDisksViaVmSpec(vm, true)).thenReturn(true);
final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
assertNotNull(wrapper); assertNotNull(wrapper);
@ -5023,7 +5023,7 @@ public class LibvirtComputingResourceTest {
fail(e.getMessage()); fail(e.getMessage());
} }
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(false); when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(false);
final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
assertNotNull(wrapper); assertNotNull(wrapper);
@ -5224,7 +5224,7 @@ public class LibvirtComputingResourceTest {
fail(e.getMessage()); fail(e.getMessage());
} }
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true); when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true);
try { try {
doNothing().when(libvirtComputingResourceMock).createVifs(vmSpec, vmDef); doNothing().when(libvirtComputingResourceMock).createVifs(vmSpec, vmDef);
@ -5303,7 +5303,7 @@ public class LibvirtComputingResourceTest {
fail(e.getMessage()); fail(e.getMessage());
} }
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true); when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true);
try { try {
doNothing().when(libvirtComputingResourceMock).createVifs(vmSpec, vmDef); doNothing().when(libvirtComputingResourceMock).createVifs(vmSpec, vmDef);
@ -5380,7 +5380,7 @@ public class LibvirtComputingResourceTest {
fail(e.getMessage()); fail(e.getMessage());
} }
when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec)).thenReturn(true); when(storagePoolMgr.connectPhysicalDisksViaVmSpec(vmSpec, false)).thenReturn(true);
final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
assertNotNull(wrapper); assertNotNull(wrapper);

View File

@ -190,7 +190,7 @@ public class ScaleIOStoragePoolTest {
when(adapter.getPhysicalDisk(volumeId, pool)).thenReturn(disk); 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); assertTrue(result);
} }
} }

View File

@ -61,7 +61,7 @@ public final class CitrixGetVmIpAddressCommandWrapper extends CommandWrapper<Get
} }
if (vmIp != null) { if (vmIp != null) {
logger.debug("VM " +vmName + " ip address got retrieved "+vmIp); logger.debug("VM {} ip address got retrieved {}", vmName, vmIp);
result = true; result = true;
return new Answer(command, result, vmIp); return new Answer(command, result, vmIp);
} }
@ -75,6 +75,5 @@ public final class CitrixGetVmIpAddressCommandWrapper extends CommandWrapper<Get
} }
return new Answer(command, result, errorMsg); return new Answer(command, result, errorMsg);
} }
} }

View File

@ -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/), 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). 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] ## [2024-10-28]
### Fixed ### Fixed

View File

@ -46,6 +46,7 @@ import com.linbit.linstor.api.Configuration;
import com.linbit.linstor.api.DevelopersApi; import com.linbit.linstor.api.DevelopersApi;
import com.linbit.linstor.api.model.ApiCallRc; import com.linbit.linstor.api.model.ApiCallRc;
import com.linbit.linstor.api.model.ApiCallRcList; 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.Properties;
import com.linbit.linstor.api.model.ProviderKind; import com.linbit.linstor.api.model.ProviderKind;
import com.linbit.linstor.api.model.Resource; 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 * @throws ApiException if any problem connecting to the Linstor controller
*/ */
private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws ApiException { private void allow2PrimariesIfInUse(DevelopersApi api, String rscName) throws ApiException {
logger.debug("enabling allow-two-primaries");
String inUseNode = LinstorUtil.isResourceInUse(api, rscName); String inUseNode = LinstorUtil.isResourceInUse(api, rscName);
if (inUseNode != null && !inUseNode.equalsIgnoreCase(localNodeName)) { if (inUseNode != null && !inUseNode.equalsIgnoreCase(localNodeName)) {
// allow 2 primaries for live migration, should be removed by disconnect on the other end // 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 @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); logger.debug("Linstor: connectPhysicalDisk {}:{} -> {}", pool.getUuid(), volumePath, details);
if (volumePath == null) { if (volumePath == null) {
@ -315,13 +318,14 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx);
} }
try if (isVMMigration) {
{ try {
allow2PrimariesIfInUse(api, rscName); allow2PrimariesIfInUse(api, rscName);
} catch (ApiException apiEx) { } catch (ApiException apiEx) {
logger.error(apiEx); logger.error(apiEx);
// do not fail here as adding allow-two-primaries property is only a problem while live migrating // do not fail here as adding allow-two-primaries property is only a problem while live migrating
} }
}
return true; return true;
} }
@ -716,4 +720,19 @@ public class LinstorStorageAdaptor implements StorageAdaptor {
throw new CloudRuntimeException(apiEx.getBestMessage(), apiEx); 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);
}
}
} }

View File

@ -75,7 +75,7 @@ public class LinstorStoragePool implements KVMStoragePool {
@Override @Override
public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details) public boolean connectPhysicalDisk(String volumeUuid, Map<String, String> details)
{ {
return _storageAdaptor.connectPhysicalDisk(volumeUuid, this, details); return _storageAdaptor.connectPhysicalDisk(volumeUuid, this, details, false);
} }
@Override @Override
@ -280,22 +280,52 @@ public class LinstorStoragePool implements KVMStoragePool {
return sc.execute(parser); 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) { private boolean checkDrbdSetupStatusOutput(String output, String otherNodeName) {
JsonParser jsonParser = new JsonParser(); JsonParser jsonParser = new JsonParser();
JsonArray jResources = (JsonArray) jsonParser.parse(output); JsonArray jResources = (JsonArray) jsonParser.parse(output);
boolean connectionFound = false;
for (JsonElement jElem : jResources) { for (JsonElement jElem : jResources) {
JsonObject jRes = (JsonObject) jElem; JsonObject jRes = (JsonObject) jElem;
JsonArray jConnections = jRes.getAsJsonArray("connections"); JsonArray jConnections = jRes.getAsJsonArray("connections");
for (JsonElement jConElem : jConnections) { for (JsonElement jConElem : jConnections) {
JsonObject jConn = (JsonObject) jConElem; JsonObject jConn = (JsonObject) jConElem;
if (jConn.getAsJsonPrimitive("name").getAsString().equals(otherNodeName) if (jConn.getAsJsonPrimitive("name").getAsString().equals(otherNodeName))
&& jConn.getAsJsonPrimitive("connection-state").getAsString().equalsIgnoreCase("Connected")) { {
connectionFound = true;
if (jConn.getAsJsonPrimitive("connection-state").getAsString()
.equalsIgnoreCase("Connected")) {
return true; 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) { private String executeDrbdEventsNow(OutputInterpreter.AllLinesParser parser) {

View File

@ -249,7 +249,7 @@ public class StorPoolStorageAdaptor implements StorageAdaptor {
} }
@Override @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); SP_LOG("StorPoolStorageAdaptor.connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool);
LOGGER.debug(String.format("connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool)); LOGGER.debug(String.format("connectPhysicalDisk: uuid=%s, pool=%s", volumeUuid, pool));

View File

@ -132,7 +132,7 @@ public class StorPoolStoragePool implements KVMStoragePool {
@Override @Override
public boolean connectPhysicalDisk(String name, Map<String, String> details) { public boolean connectPhysicalDisk(String name, Map<String, String> details) {
return _storageAdaptor.connectPhysicalDisk(name, this, details); return _storageAdaptor.connectPhysicalDisk(name, this, details, false);
} }
@Override @Override

View File

@ -345,10 +345,10 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
try { try {
if (isConfigDriveIsoOnHostCache(vm.getId())) { if (isConfigDriveIsoOnHostCache(vm.getId())) {
vm.setConfigDriveLocation(Location.HOST); vm.setConfigDriveLocation(Location.HOST);
configureConfigDriveData(vm, nic, dest); if (configureConfigDriveData(vm, nic, dest)) {
// Create the config drive on dest host cache // Create the config drive on dest host cache
createConfigDriveIsoOnHostCache(nic, vm, dest.getHost().getId()); createConfigDriveIsoOnHostCache(vm, dest.getHost().getId());
}
} else { } else {
vm.setConfigDriveLocation(getConfigDriveLocation(vm.getId())); vm.setConfigDriveLocation(getConfigDriveLocation(vm.getId()));
boolean result = addPasswordAndUserdata(network, nic, vm, dest, context); boolean result = addPasswordAndUserdata(network, nic, vm, dest, context);
@ -380,7 +380,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
@Override @Override
public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) { public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) {
try { try {
if (isConfigDriveIsoOnHostCache(vm.getId())) { if (isLastConfigDriveIsoOnHostCache(vm.getId())) {
vm.setConfigDriveLocation(Location.HOST); vm.setConfigDriveLocation(Location.HOST);
// Delete the config drive on src host cache // Delete the config drive on src host cache
deleteConfigDriveIsoOnHostCache(vm.getVirtualMachine(), vm.getHostId()); deleteConfigDriveIsoOnHostCache(vm.getVirtualMachine(), vm.getHostId());
@ -537,7 +537,18 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
return false; 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) { if (hostId == null) {
throw new ResourceUnavailableException("Config drive iso creation failed, dest host not available", throw new ResourceUnavailableException("Config drive iso creation failed, dest host not available",
ConfigDriveNetworkElement.class, 0L); ConfigDriveNetworkElement.class, 0L);
@ -549,7 +560,7 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
final String isoFileName = ConfigDrive.configIsoFileName(profile.getInstanceName()); final String isoFileName = ConfigDrive.configIsoFileName(profile.getInstanceName());
final String isoPath = ConfigDrive.createConfigDrivePath(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 Map<Long, List<Service>> supportedServices = getSupportedServicesByElementForNetwork(nicProfiles);
final String isoData = ConfigDriveBuilder.buildConfigDrive(nicProfiles, profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), customUserdataParamMap, supportedServices); final String isoData = ConfigDriveBuilder.buildConfigDrive(nicProfiles, profile.getVmData(), isoFileName, profile.getConfigDriveLabel(), customUserdataParamMap, supportedServices);
final HandleConfigDriveIsoCommand configDriveIsoCommand = new HandleConfigDriveIsoCommand(isoPath, isoData, null, false, true, true); 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()); profile.setConfigDriveLocation(answer.getConfigDriveLocation());
_userVmDetailsDao.addDetail(profile.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION, answer.getConfigDriveLocation().toString(), false); updateConfigDriveLocationInVMDetails(profile.getId(), answer.getConfigDriveLocation());
addConfigDriveDisk(profile, null); addConfigDriveDisk(profile, null);
return true; return true;
} }
@ -650,11 +661,23 @@ public class ConfigDriveNetworkElement extends AdapterBase implements NetworkEle
answer.getDetails()), ConfigDriveNetworkElement.class, 0L); answer.getDetails()), ConfigDriveNetworkElement.class, 0L);
} }
profile.setConfigDriveLocation(answer.getConfigDriveLocation()); profile.setConfigDriveLocation(answer.getConfigDriveLocation());
_userVmDetailsDao.addDetail(profile.getId(), VmDetailConstants.CONFIG_DRIVE_LOCATION, answer.getConfigDriveLocation().toString(), false); updateConfigDriveLocationInVMDetails(profile.getId(), answer.getConfigDriveLocation());
addConfigDriveDisk(profile, dataStore); addConfigDriveDisk(profile, dataStore);
return true; 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) { private Map<String, String> getVMCustomUserdataParamMap(long vmId) {
UserVmVO userVm = _userVmDao.findById(vmId); UserVmVO userVm = _userVmDao.findById(vmId);
String userDataDetails = userVm.getUserDataDetails(); 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) { private boolean configureConfigDriveData(final VirtualMachineProfile profile, final NicProfile nic, final DeployDestination dest) {
final UserVmVO vm = _userVmDao.findById(profile.getId()); final UserVmVO vm = _userVmDao.findById(profile.getId());
if (vm.getType() != VirtualMachine.Type.User) { if (vm == null || vm.getType() != VirtualMachine.Type.User) {
return false; return false;
} }
final Nic defaultNic = _networkModel.getDefaultNic(vm.getId()); final Nic defaultNic = _networkModel.getDefaultNic(vm.getId());

View File

@ -543,7 +543,7 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ
// Set capabilities for vpn // Set capabilities for vpn
final Map<Capability, String> vpnCapabilities = new HashMap<Capability, String>(); final Map<Capability, String> vpnCapabilities = new HashMap<Capability, String>();
vpnCapabilities.put(Capability.SupportedVpnProtocols, "pptp,l2tp,ipsec"); vpnCapabilities.put(Capability.SupportedVpnProtocols, "pptp,l2tp,ipsec");
vpnCapabilities.put(Capability.VpnTypes, "removeaccessvpn"); vpnCapabilities.put(Capability.VpnTypes, "remoteaccessvpn");
capabilities.put(Service.Vpn, vpnCapabilities); capabilities.put(Service.Vpn, vpnCapabilities);
final Map<Capability, String> dnsCapabilities = new HashMap<Capability, String>(); final Map<Capability, String> dnsCapabilities = new HashMap<Capability, String>();

View File

@ -122,8 +122,6 @@ import com.cloud.network.IpAddress;
import com.cloud.network.IpAddressManager; import com.cloud.network.IpAddressManager;
import com.cloud.network.Network; import com.cloud.network.Network;
import com.cloud.network.NetworkModel; 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.VpnUserVO;
import com.cloud.network.as.AutoScaleManager; import com.cloud.network.as.AutoScaleManager;
import com.cloud.network.dao.AccountGuestVlanMapDao; 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.dao.VpnUserDao;
import com.cloud.network.router.VirtualRouter; import com.cloud.network.router.VirtualRouter;
import com.cloud.network.security.SecurityGroupManager; 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.security.dao.SecurityGroupDao;
import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.VpcManager; import com.cloud.network.vpc.VpcManager;
@ -1335,16 +1335,33 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
return _userAccountDao.findById(userId); return _userAccountDao.findById(userId);
} }
private boolean isValidRoleChange(Account account, Role role) { /*
Long currentAccRoleId = account.getRoleId(); Role change should follow the below conditions:
Role currentRole = roleService.findRole(currentAccRoleId); - Caller should not be of Unknown role type
- New role's type should not be Unknown
if (role.getRoleType().ordinal() < currentRole.getRoleType().ordinal() && ((account.getType() == Account.Type.NORMAL && role.getRoleType().getAccountType().ordinal() > Account.Type.NORMAL.ordinal()) || - Caller should not be able to escalate or de-escalate an account's role which is of higher role type
account.getType().ordinal() > Account.Type.NORMAL.ordinal() && role.getRoleType().getAccountType().ordinal() < account.getType().ordinal() && role.getRoleType().getAccountType().ordinal() > 0)) { - New role should not be of type Admin with domain other than ROOT domain
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())); 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); Role role = roleService.findRole(roleId);
isValidRoleChange(account, role); validateRoleChange(account, role, caller);
acctForUpdate.setRoleId(roleId); acctForUpdate.setRoleId(roleId);
acctForUpdate.setType(role.getRoleType().getAccountType()); acctForUpdate.setType(role.getRoleType().getAccountType());
checkRoleEscalation(getCurrentCallingAccount(), acctForUpdate); checkRoleEscalation(getCurrentCallingAccount(), acctForUpdate);

View File

@ -742,8 +742,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
} }
private class VmIpAddrFetchThread extends ManagedContextRunnable { private class VmIpAddrFetchThread extends ManagedContextRunnable {
long nicId; long nicId;
long vmId; long vmId;
String vmName; String vmName;
@ -766,7 +764,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
boolean decrementCount = true; boolean decrementCount = true;
try { 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); Answer answer = _agentMgr.send(hostId, cmd);
NicVO nic = _nicDao.findById(nicId); NicVO nic = _nicDao.findById(nicId);
if (answer.getResult()) { if (answer.getResult()) {
@ -777,12 +775,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (nic != null) { if (nic != null) {
nic.setIPv4Address(vmIp); nic.setIPv4Address(vmIp);
_nicDao.update(nicId, nic); _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); vmIdCountMap.remove(nicId);
decrementCount = false; decrementCount = false;
ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM,
Domain.ROOT_DOMAIN, EventTypes.EVENT_NETWORK_EXTERNAL_DHCP_VM_IPFETCH, 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 { } else {
@ -793,7 +791,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
_nicDao.update(nicId, nic); _nicDao.update(nicId, nic);
} }
if (answer.getDetails() != null) { 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) { } catch (OperationTimedoutException e) {
@ -804,7 +802,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
if (decrementCount) { if (decrementCount) {
VmAndCountDetails vmAndCount = vmIdCountMap.get(nicId); VmAndCountDetails vmAndCount = vmIdCountMap.get(nicId);
vmAndCount.decrementCount(); 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); vmIdCountMap.put(nicId, vmAndCount);
} }
} }

View File

@ -102,6 +102,7 @@ import com.cloud.storage.dao.VMTemplatePoolDao;
import com.cloud.utils.component.ManagerBase; import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.utils.security.CertificateHelper; import com.cloud.utils.security.CertificateHelper;
import sun.security.x509.X509CertImpl; import sun.security.x509.X509CertImpl;
@ -469,10 +470,18 @@ public class DirectDownloadManagerImpl extends ManagerBase implements DirectDown
@Override @Override
public Pair<DirectDownloadCertificate, List<HostCertificateStatus>> uploadCertificateToHosts( public Pair<DirectDownloadCertificate, List<HostCertificateStatus>> uploadCertificateToHosts(
String certificateCer, String alias, String hypervisor, Long zoneId, Long hostId) { 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"); 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; List<HostVO> hosts;
DirectDownloadCertificateVO certificateVO; DirectDownloadCertificateVO certificateVO;
HypervisorType hypervisorType = HypervisorType.getType(hypervisor); HypervisorType hypervisorType = HypervisorType.getType(hypervisor);

View File

@ -27,11 +27,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.cloud.event.ActionEventUtils; 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.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd; import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd; 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.GetUserKeysCmd;
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd; import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse; import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
@ -50,6 +53,7 @@ import org.mockito.Mock;
import org.mockito.MockedStatic; import org.mockito.MockedStatic;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import com.cloud.acl.DomainChecker; import com.cloud.acl.DomainChecker;
@ -120,6 +124,8 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
@Mock @Mock
ConfigKey<Boolean> enableUserTwoFactorAuthenticationMock; ConfigKey<Boolean> enableUserTwoFactorAuthenticationMock;
@Mock
RoleService roleService;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
@ -1225,4 +1231,112 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
accountManagerImpl.deleteWebhooksForAccount(1L); 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);
}
} }

View File

@ -1646,7 +1646,7 @@ def main(argv):
("dhcp", {"process_iptables": False, "executor": [CsDhcp("dhcpentry", config)]}), ("dhcp", {"process_iptables": False, "executor": [CsDhcp("dhcpentry", config)]}),
("load_balancer", {"process_iptables": True, "executor": []}), ("load_balancer", {"process_iptables": True, "executor": []}),
("monitor_service", {"process_iptables": False, "executor": [CsMonitor("monitorservice", config)]}), ("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(): if not config.is_vpc():

View File

@ -102,6 +102,10 @@ public class NetUtils {
public final static int IPV6_EUI64_11TH_BYTE = -1; public final static int IPV6_EUI64_11TH_BYTE = -1;
public final static int IPV6_EUI64_12TH_BYTE = -2; 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 { public static String extractHost(String uri) throws URISyntaxException {
return (new URI(uri)).getHost(); return (new URI(uri)).getHost();
} }
@ -1064,13 +1068,13 @@ public class NetUtils {
if (hostName.length() > 63 || hostName.length() < 1) { if (hostName.length() > 63 || hostName.length() < 1) {
LOGGER.warn("Domain name label must be between 1 and 63 characters long"); LOGGER.warn("Domain name label must be between 1 and 63 characters long");
return false; 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)"); LOGGER.warn("Domain name label may contain only the ASCII letters 'a' through 'z' (in a case-insensitive manner)");
return false; return false;
} else if (hostName.startsWith("-") || hostName.endsWith("-")) { } 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; 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"); LOGGER.warn("Host name can't start with digit");
return false; return false;
} }