You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pe...@apache.org on 2026/09/14 19:34:06 UTC
(cloudstack) branch support-multi-vlan-guestnet updated (baebacedeef -> c2ebb3a6ec5)
This is an automated email from the ASF dual-hosted git repository.
Pearl1594 pushed a change to branch support-multi-vlan-guestnet
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
discard baebacedeef Support multi-VLAN trunk NICs
discard 1cd55e1590f Merge PR #14033 (engine-schema: upgrade path for 24.0.0) into support-multi-vlan-guestnet
omit cf0519a683f fix imports
omit b4256869f5b fix upgrade unit tests for cutover
omit 3dbe6566530 fix CS version
omit ccc870cc2b2 engine-schema: upgrade path for 24.0.0
new e45ea9592ee engine-schema: upgrade path for 24.0.0
new c2ebb3a6ec5 Support multi-VLAN trunk NICs
This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version. This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:
* -- * -- B -- O -- O -- O (baebacedeef)
\
N -- N -- N refs/heads/support-multi-vlan-guestnet (c2ebb3a6ec5)
You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.
Any revisions marked "omit" are not gone; other references still
refer to them. Any revisions marked "discard" are gone forever.
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
(cloudstack) 02/02: Support multi-VLAN trunk NICs
Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
Pearl1594 pushed a commit to branch support-multi-vlan-guestnet
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit c2ebb3a6ec5be616ff9e2c1bbecd5d99b657ff99
Author: Pearl1594 <pe...@gmail.com>
AuthorDate: Mon Sep 14 15:33:15 2026 -0400
Support multi-VLAN trunk NICs
---
api/src/main/java/com/cloud/host/Host.java | 2 +
api/src/main/java/com/cloud/vm/Nic.java | 2 +
.../org/apache/cloudstack/api/ApiConstants.java | 2 +
.../cloudstack/api/response/HostResponse.java | 24 ++++
.../com/cloud/agent/manager/AgentManagerImpl.java | 12 +-
.../schema/src/main/java/com/cloud/vm/NicVO.java | 14 ++-
.../java/com/cloud/vm/dao/NicNetworkMapDao.java | 32 +++++
.../com/cloud/vm/dao/NicNetworkMapDaoImpl.java | 77 ++++++++++++
.../java/com/cloud/vm/dao/NicNetworkMapVO.java | 140 +++++++++++++++++++++
.../spring-engine-schema-core-daos-context.xml | 1 +
.../resources/META-INF/db/schema-42300to2400.sql | 22 ++++
.../kvm/resource/LibvirtComputingResource.java | 41 ++++++
.../wrapper/LibvirtReadyCommandWrapper.java | 3 +
.../com/cloud/api/query/dao/HostJoinDaoImpl.java | 2 +
14 files changed, 372 insertions(+), 2 deletions(-)
diff --git a/api/src/main/java/com/cloud/host/Host.java b/api/src/main/java/com/cloud/host/Host.java
index c110e4ca94e..3cf36db8ad5 100644
--- a/api/src/main/java/com/cloud/host/Host.java
+++ b/api/src/main/java/com/cloud/host/Host.java
@@ -64,6 +64,8 @@ public interface Host extends StateObject<Status>, Identity, Partition, HAResour
String HOST_VIRTV2V_VERSION = "host.virtv2v.version";
String HOST_SSH_PORT = "host.ssh.port";
String HOST_CDROM_MAX_COUNT = "host.cdrom.max.count";
+ String HOST_VLAN_FILTERING_ENABLED = "vlan.filtering.enabled";
+ String HOST_VLAN_TRUNK_XML_SUPPORTED = "vlan.trunk.xml.supported";
String GUEST_OS_CATEGORY_ID = "guest.os.category.id";
String GUEST_OS_RULE = "guest.os.rule";
diff --git a/api/src/main/java/com/cloud/vm/Nic.java b/api/src/main/java/com/cloud/vm/Nic.java
index 3722e5769c9..71612cd355f 100644
--- a/api/src/main/java/com/cloud/vm/Nic.java
+++ b/api/src/main/java/com/cloud/vm/Nic.java
@@ -146,6 +146,8 @@ public interface Nic extends Identity, InternalIdentity {
boolean getSecondaryIp();
+ boolean getMultiNetwork();
+
//
// IPv4
//
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index f74c4616118..de6aeed50a0 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -318,6 +318,8 @@ public class ApiConstants {
public static final String MOVE_ACL_CONSISTENCY_HASH = "aclconsistencyhash";
public static final String IMAGE_PATH = "imagepath";
public static final String INSTANCE_CONVERSION_SUPPORTED = "instanceconversionsupported";
+ public static final String VLAN_FILTERING_ENABLED = "vlanfilteringenabled";
+ public static final String VLAN_TRUNK_XML_SUPPORTED = "vlantrunkxmlsupported";
public static final String INTERNAL_DNS1 = "internaldns1";
public static final String INTERNAL_DNS2 = "internaldns2";
public static final String INTERNET_PROTOCOL = "internetprotocol";
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java
index 10bd62804fb..52804cc980c 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/HostResponse.java
@@ -315,6 +315,14 @@ public class HostResponse extends BaseResponseWithAnnotations {
@Param(description = "true if the host supports instance conversion (using virt-v2v)", since = "4.19.1")
private Boolean instanceConversionSupported;
+ @SerializedName(ApiConstants.VLAN_FILTERING_ENABLED)
+ @Param(description = "true if vlan_filtering is enabled on this host's guest bridge, so it can accept multi-VLAN trunk nics", since = "24.0.0")
+ private Boolean vlanFilteringEnabled;
+
+ @SerializedName(ApiConstants.VLAN_TRUNK_XML_SUPPORTED)
+ @Param(description = "true if this host's libvirt version supports trunk vlan tap membership natively", since = "24.0.0")
+ private Boolean vlanTrunkXmlSupported;
+
@SerializedName(ApiConstants.ARCH)
@Param(description = "CPU Arch of the host", since = "4.20")
private String arch;
@@ -904,6 +912,14 @@ public class HostResponse extends BaseResponseWithAnnotations {
this.instanceConversionSupported = instanceConversionSupported;
}
+ public void setVlanFilteringEnabled(Boolean vlanFilteringEnabled) {
+ this.vlanFilteringEnabled = vlanFilteringEnabled;
+ }
+
+ public void setVlanTrunkXmlSupported(Boolean vlanTrunkXmlSupported) {
+ this.vlanTrunkXmlSupported = vlanTrunkXmlSupported;
+ }
+
public Boolean getIsTagARule() {
return isTagARule;
}
@@ -1000,6 +1016,14 @@ public class HostResponse extends BaseResponseWithAnnotations {
return instanceConversionSupported;
}
+ public Boolean getVlanFilteringEnabled() {
+ return vlanFilteringEnabled;
+ }
+
+ public Boolean getVlanTrunkXmlSupported() {
+ return vlanTrunkXmlSupported;
+ }
+
public void setExtensionId(String extensionId) {
this.extensionId = extensionId;
}
diff --git a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
index 1215829d92f..eede907b187 100644
--- a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentManagerImpl.java
@@ -808,8 +808,10 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
String vddkSupport = detailsMap.get(Host.HOST_VDDK_SUPPORT);
String vddkLibDir = detailsMap.get(Host.HOST_VDDK_LIB_DIR);
String vddkVersion = detailsMap.get(Host.HOST_VDDK_VERSION);
+ String vlanFilteringEnabled = detailsMap.get(Host.HOST_VLAN_FILTERING_ENABLED);
+ String vlanTrunkXmlSupported = detailsMap.get(Host.HOST_VLAN_TRUNK_XML_SUPPORTED);
logger.debug("Got HOST_UEFI_ENABLE [{}] for host [{}]:", uefiEnabled, host);
- if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion, vddkSupport, vddkLibDir, vddkVersion)) {
+ if (ObjectUtils.anyNotNull(uefiEnabled, virtv2vVersion, ovftoolVersion, vddkSupport, vddkLibDir, vddkVersion, vlanFilteringEnabled, vlanTrunkXmlSupported)) {
_hostDao.loadDetails(host);
boolean updateNeeded = false;
if (StringUtils.isNotBlank(uefiEnabled) && !uefiEnabled.equals(host.getDetails().get(Host.HOST_UEFI_ENABLE))) {
@@ -828,6 +830,14 @@ public class AgentManagerImpl extends ManagerBase implements AgentManager, Handl
host.getDetails().put(Host.HOST_VDDK_SUPPORT, vddkSupport);
updateNeeded = true;
}
+ if (StringUtils.isNotBlank(vlanFilteringEnabled) && !vlanFilteringEnabled.equals(host.getDetails().get(Host.HOST_VLAN_FILTERING_ENABLED))) {
+ host.getDetails().put(Host.HOST_VLAN_FILTERING_ENABLED, vlanFilteringEnabled);
+ updateNeeded = true;
+ }
+ if (StringUtils.isNotBlank(vlanTrunkXmlSupported) && !vlanTrunkXmlSupported.equals(host.getDetails().get(Host.HOST_VLAN_TRUNK_XML_SUPPORTED))) {
+ host.getDetails().put(Host.HOST_VLAN_TRUNK_XML_SUPPORTED, vlanTrunkXmlSupported);
+ updateNeeded = true;
+ }
if (!StringUtils.defaultString(vddkLibDir).equals(StringUtils.defaultString(host.getDetails().get(Host.HOST_VDDK_LIB_DIR)))) {
if (StringUtils.isBlank(vddkLibDir)) {
host.getDetails().remove(Host.HOST_VDDK_LIB_DIR);
diff --git a/engine/schema/src/main/java/com/cloud/vm/NicVO.java b/engine/schema/src/main/java/com/cloud/vm/NicVO.java
index 65946b8d821..bcda4f78775 100644
--- a/engine/schema/src/main/java/com/cloud/vm/NicVO.java
+++ b/engine/schema/src/main/java/com/cloud/vm/NicVO.java
@@ -128,6 +128,9 @@ public class NicVO implements Nic {
@Column(name = "secondary_ip")
boolean secondaryIp;
+ @Column(name = "multi_network")
+ boolean multiNetwork;
+
@Column(name = "mtu")
Integer mtu;
@@ -337,7 +340,7 @@ public class NicVO implements Nic {
return String.format("Nic %s",
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
this, "id", "uuid", "instanceId",
- "deviceId", "broadcastUri", "reservationId", "iPv4Address"));
+ "deviceId", "broadcastUri", "reservationId", "iPv4Address", "multiNetwork"));
}
@Override
@@ -381,6 +384,15 @@ public class NicVO implements Nic {
this.secondaryIp = secondaryIp;
}
+ @Override
+ public boolean getMultiNetwork() {
+ return multiNetwork;
+ }
+
+ public void setMultiNetwork(boolean multiNetwork) {
+ this.multiNetwork = multiNetwork;
+ }
+
public void setVmType(VirtualMachine.Type vmType) {
this.vmType = vmType;
}
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapDao.java
new file mode 100644
index 00000000000..b5a1ed3137a
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapDao.java
@@ -0,0 +1,32 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.vm.dao;
+
+import java.util.List;
+
+import com.cloud.utils.db.GenericDao;
+
+public interface NicNetworkMapDao extends GenericDao<NicNetworkMapVO, Long> {
+
+ List<NicNetworkMapVO> listByNicId(long nicId);
+
+ List<NicNetworkMapVO> listByNetworkId(long networkId);
+
+ NicNetworkMapVO findByNicIdAndNetworkId(long nicId, long networkId);
+
+ List<Long> listNicIdsByNetworkId(long networkId);
+}
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapDaoImpl.java
new file mode 100644
index 00000000000..f905809350e
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapDaoImpl.java
@@ -0,0 +1,77 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.vm.dao;
+
+import java.util.List;
+
+import org.springframework.stereotype.Component;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.GenericSearchBuilder;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.SearchCriteria.Func;
+import com.cloud.utils.db.SearchCriteria.Op;
+
+@Component
+public class NicNetworkMapDaoImpl extends GenericDaoBase<NicNetworkMapVO, Long> implements NicNetworkMapDao {
+
+ private final SearchBuilder<NicNetworkMapVO> AllFieldsSearch;
+ private final GenericSearchBuilder<NicNetworkMapVO, Long> NicIdsByNetworkSearch;
+
+ public NicNetworkMapDaoImpl() {
+ super();
+ AllFieldsSearch = createSearchBuilder();
+ AllFieldsSearch.and("nicId", AllFieldsSearch.entity().getNicId(), Op.EQ);
+ AllFieldsSearch.and("networkId", AllFieldsSearch.entity().getNetworkId(), Op.EQ);
+ AllFieldsSearch.done();
+
+ NicIdsByNetworkSearch = createSearchBuilder(Long.class);
+ NicIdsByNetworkSearch.select(null, Func.DISTINCT, NicIdsByNetworkSearch.entity().getNicId());
+ NicIdsByNetworkSearch.and("networkId", NicIdsByNetworkSearch.entity().getNetworkId(), Op.EQ);
+ NicIdsByNetworkSearch.done();
+ }
+
+ @Override
+ public List<NicNetworkMapVO> listByNicId(long nicId) {
+ SearchCriteria<NicNetworkMapVO> sc = AllFieldsSearch.create();
+ sc.setParameters("nicId", nicId);
+ return listBy(sc);
+ }
+
+ @Override
+ public List<NicNetworkMapVO> listByNetworkId(long networkId) {
+ SearchCriteria<NicNetworkMapVO> sc = AllFieldsSearch.create();
+ sc.setParameters("networkId", networkId);
+ return listBy(sc);
+ }
+
+ @Override
+ public NicNetworkMapVO findByNicIdAndNetworkId(long nicId, long networkId) {
+ SearchCriteria<NicNetworkMapVO> sc = AllFieldsSearch.create();
+ sc.setParameters("nicId", nicId);
+ sc.setParameters("networkId", networkId);
+ return findOneBy(sc);
+ }
+
+ @Override
+ public List<Long> listNicIdsByNetworkId(long networkId) {
+ SearchCriteria<Long> sc = NicIdsByNetworkSearch.create();
+ sc.setParameters("networkId", networkId);
+ return customSearch(sc, null);
+ }
+}
diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapVO.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapVO.java
new file mode 100644
index 00000000000..79344535460
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicNetworkMapVO.java
@@ -0,0 +1,140 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.vm.dao;
+
+import java.util.Date;
+import java.util.UUID;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+import org.apache.cloudstack.api.Identity;
+import org.apache.cloudstack.api.InternalIdentity;
+import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
+
+import com.cloud.utils.db.GenericDao;
+
+@Entity
+@Table(name = "nic_network_map")
+public class NicNetworkMapVO implements Identity, InternalIdentity {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
+ private long id;
+
+ @Column(name = "uuid")
+ private String uuid = UUID.randomUUID().toString();
+
+ @Column(name = "nic_id")
+ private long nicId;
+
+ @Column(name = "network_id")
+ private long networkId;
+
+ @Column(name = "ip4_address")
+ private String ip4Address;
+
+ @Column(name = "ip6_address")
+ private String ip6Address;
+
+ @Column(name = GenericDao.CREATED_COLUMN)
+ private Date created;
+
+ @Column(name = GenericDao.REMOVED_COLUMN)
+ private Date removed;
+
+ protected NicNetworkMapVO() {
+ }
+
+ public NicNetworkMapVO(long nicId, long networkId) {
+ this.nicId = nicId;
+ this.networkId = networkId;
+ }
+
+ public NicNetworkMapVO(long nicId, long networkId, String ip4Address, String ip6Address) {
+ this.nicId = nicId;
+ this.networkId = networkId;
+ this.ip4Address = ip4Address;
+ this.ip6Address = ip6Address;
+ }
+
+ @Override
+ public long getId() {
+ return id;
+ }
+
+ @Override
+ public String getUuid() {
+ return uuid;
+ }
+
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public long getNicId() {
+ return nicId;
+ }
+
+ public void setNicId(long nicId) {
+ this.nicId = nicId;
+ }
+
+ public long getNetworkId() {
+ return networkId;
+ }
+
+ public void setNetworkId(long networkId) {
+ this.networkId = networkId;
+ }
+
+ public String getIp4Address() {
+ return ip4Address;
+ }
+
+ public void setIp4Address(String ip4Address) {
+ this.ip4Address = ip4Address;
+ }
+
+ public String getIp6Address() {
+ return ip6Address;
+ }
+
+ public void setIp6Address(String ip6Address) {
+ this.ip6Address = ip6Address;
+ }
+
+ public Date getCreated() {
+ return created;
+ }
+
+ public Date getRemoved() {
+ return removed;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("NicNetworkMap %s",
+ ReflectionToStringBuilderUtils.reflectOnlySelectedFields(
+ this, "id", "uuid", "nicId", "networkId", "ip4Address", "ip6Address"));
+ }
+}
diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index 932db538f30..e89d87242b2 100644
--- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -135,6 +135,7 @@
<bean id="networkServiceMapDaoImpl" class="com.cloud.network.dao.NetworkServiceMapDaoImpl" />
<bean id="nicDetailsDaoImpl" class="com.cloud.vm.dao.NicDetailsDaoImpl" />
<bean id="nicExtraDhcpOptionDaoImpl" class="com.cloud.vm.dao.NicExtraDhcpOptionDaoImpl" />
+ <bean id="nicNetworkMapDaoImpl" class="com.cloud.vm.dao.NicNetworkMapDaoImpl" />
<bean id="nicSecondaryIpDaoImpl" class="com.cloud.vm.dao.NicSecondaryIpDaoImpl" />
<bean id="nicIpAliasDaoImpl" class="com.cloud.vm.dao.NicIpAliasDaoImpl" />
<bean id="objectInDataStoreDaoImpl" class="org.apache.cloudstack.storage.db.ObjectInDataStoreDaoImpl" />
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql b/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql
index 7c11013a17d..aaedd564b77 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql
@@ -18,3 +18,25 @@
--;
-- Schema upgrade from 4.23.0.0 to 24.0.0
--;
+
+-- Multi-VLAN trunk nics: a nic may be associated with additional networks beyond its primary nics.network_id.
+CREATE TABLE IF NOT EXISTS `cloud`.`nic_network_map` (
+ `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT,
+ `uuid` varchar(40),
+ `nic_id` bigint unsigned NOT NULL COMMENT 'nic this association belongs to',
+ `network_id` bigint unsigned NOT NULL COMMENT 'additional network this nic is associated with',
+ `ip4_address` char(40) COMMENT 'ip4 address assigned to this nic from this network',
+ `ip6_address` char(40) COMMENT 'ip6 address assigned to this nic from this network',
+ `created` datetime NOT NULL COMMENT 'date created',
+ `removed` datetime COMMENT 'date removed if not null',
+ PRIMARY KEY (`id`),
+ CONSTRAINT `fk_nic_network_map__nic_id` FOREIGN KEY (`nic_id`) REFERENCES `nics`(`id`) ON DELETE CASCADE,
+ CONSTRAINT `fk_nic_network_map__network_id` FOREIGN KEY (`network_id`) REFERENCES `networks`(`id`),
+ CONSTRAINT `uc_nic_network_map__uuid` UNIQUE (`uuid`),
+ UNIQUE KEY `uk_nic_network_map__nic_id_network_id` (`nic_id`, `network_id`),
+ INDEX `i_nic_network_map__nic_id` (`nic_id`),
+ INDEX `i_nic_network_map__network_id` (`network_id`),
+ INDEX `i_nic_network_map__removed` (`removed`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics', 'multi_network', 'tinyint(1) NOT NULL DEFAULT 0 COMMENT "true if this nic has additional network associations in nic_network_map"');
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 8e8ae314312..71c862ccfc2 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -23,6 +23,8 @@ import static com.cloud.host.Host.HOST_VDDK_LIB_DIR;
import static com.cloud.host.Host.HOST_VDDK_SUPPORT;
import static com.cloud.host.Host.HOST_VDDK_VERSION;
import static com.cloud.host.Host.HOST_VIRTV2V_VERSION;
+import static com.cloud.host.Host.HOST_VLAN_FILTERING_ENABLED;
+import static com.cloud.host.Host.HOST_VLAN_TRUNK_XML_SUPPORTED;
import static com.cloud.host.Host.HOST_VOLUME_ENCRYPTION;
import static org.apache.cloudstack.utils.linux.KVMHostInfo.isHostS390x;
@@ -352,6 +354,10 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
* It is supported since Libvirt 0.9.0
*/
private static final int MIN_LIBVIRT_VERSION_FOR_GUEST_CPU_TUNE = 9000;
+ /**
+ * Libvirt supports multi-tag trunk mode (<vlan trunk='yes'>) on a standard Linux bridge since 11.0.0.
+ */
+ private static final long MIN_LIBVIRT_VERSION_FOR_VLAN_TRUNK = 11000000;
/**
* Constant that defines ARM64 (aarch64) guest architectures.
*/
@@ -4415,6 +4421,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
cmd.getHostDetails().put(HOST_INSTANCE_CONVERSION, String.valueOf(instanceConversionSupported));
cmd.getHostDetails().put(HOST_VDDK_SUPPORT, String.valueOf(hostSupportsVddk()));
cmd.getHostDetails().put(HOST_CDROM_MAX_COUNT, String.valueOf(LibvirtVMDef.MAX_CDROMS_PER_VM));
+ cmd.getHostDetails().put(HOST_VLAN_FILTERING_ENABLED, String.valueOf(hostSupportsVlanFiltering()));
+ cmd.getHostDetails().put(HOST_VLAN_TRUNK_XML_SUPPORTED, String.valueOf(hostSupportsVlanTrunkXml()));
if (StringUtils.isNotBlank(vddkLibDir)) {
cmd.getHostDetails().put(HOST_VDDK_LIB_DIR, vddkLibDir);
}
@@ -6236,6 +6244,39 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
return exitValue == 0;
}
+ /**
+ * Static capability: whether this host's libvirt understands multi-tag trunk vlan XML
+ * (<vlan trunk='yes'>) on a standard Linux bridge interface.
+ */
+ public boolean hostSupportsVlanTrunkXml() {
+ return hypervisorLibvirtVersion >= MIN_LIBVIRT_VERSION_FOR_VLAN_TRUNK;
+ }
+
+ /**
+ * Live state: whether vlan_filtering is currently enabled on this host's guest bridge.
+ * A vlan_filtering=0 bridge floods every tagged frame to every port regardless of tag,
+ * so this must be true before any multi-VLAN trunk nic can be placed on this host.
+ */
+ public boolean hostSupportsVlanFiltering() {
+ return isBridgeVlanFilteringEnabled(guestBridgeName);
+ }
+
+ protected boolean isBridgeVlanFilteringEnabled(String bridgeName) {
+ if (StringUtils.isBlank(bridgeName)) {
+ return false;
+ }
+ File vlanFilteringFile = new File("/sys/class/net/" + bridgeName + "/bridge/vlan_filtering");
+ if (!vlanFilteringFile.exists()) {
+ return false;
+ }
+ try {
+ return "1".equals(FileUtils.readFileToString(vlanFilteringFile).trim());
+ } catch (IOException e) {
+ LOGGER.warn("Failed to read vlan_filtering state for bridge " + bridgeName, e);
+ return false;
+ }
+ }
+
public boolean hostSupportsVddk() {
return hostSupportsVddk(null);
}
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java
index 5a7d6d2c203..8af4e5f95d0 100644
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtReadyCommandWrapper.java
@@ -59,6 +59,9 @@ public final class LibvirtReadyCommandWrapper extends CommandWrapper<ReadyComman
hostDetails.put(Host.HOST_OVFTOOL_VERSION, libvirtComputingResource.getHostOvfToolVersion());
}
+ hostDetails.put(Host.HOST_VLAN_FILTERING_ENABLED, Boolean.toString(libvirtComputingResource.hostSupportsVlanFiltering()));
+ hostDetails.put(Host.HOST_VLAN_TRUNK_XML_SUPPORTED, Boolean.toString(libvirtComputingResource.hostSupportsVlanTrunkXml()));
+
return new ReadyAnswer(command, hostDetails);
}
diff --git a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
index 2e4025ca001..f942a38746c 100644
--- a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
@@ -283,6 +283,8 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
} else {
hostResponse.setUefiCapability(new Boolean(false));
}
+ hostResponse.setVlanFilteringEnabled(Boolean.parseBoolean((String) hostDetails.get(Host.HOST_VLAN_FILTERING_ENABLED)));
+ hostResponse.setVlanTrunkXmlSupported(Boolean.parseBoolean((String) hostDetails.get(Host.HOST_VLAN_TRUNK_XML_SUPPORTED)));
}
if (details.contains(HostDetails.all) &&
Arrays.asList(Hypervisor.HypervisorType.KVM,
(cloudstack) 01/02: engine-schema: upgrade path for 24.0.0
Posted by pe...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
Pearl1594 pushed a commit to branch support-multi-vlan-guestnet
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit e45ea9592ee2c26ae72f26f0c93857569447da28
Author: Abhishek Kumar <ab...@gmail.com>
AuthorDate: Mon Sep 14 15:29:12 2026 -0400
engine-schema: upgrade path for 24.0.0
Adds a 4.23.0 to 24.0.0 upgrade path (squashed from sb/upgradepath-424:
engine-schema: upgrade path for 24.0.0, fix CS version, fix upgrade
unit tests for cutover, fix imports).
---
.../com/cloud/upgrade/DatabaseUpgradeChecker.java | 15 ++++--
.../com/cloud/upgrade/dao/Upgrade42300to2400.java | 30 +++++++++++
.../META-INF/db/schema-42300to2400-cleanup.sql | 20 +++++++
.../resources/META-INF/db/schema-42300to2400.sql | 20 +++++++
.../cloud/upgrade/DatabaseUpgradeCheckerTest.java | 59 ++++++++++----------
.../apache/cloudstack/veeam/api/dto/Version.java | 8 ++-
.../apache/cloudstack/utils/CloudStackVersion.java | 62 +++++++++++++++++-----
.../cloudstack/utils/CloudStackVersionTest.java | 43 +++++++++++++--
8 files changed, 205 insertions(+), 52 deletions(-)
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
index c3a982aa70e..f3ec76a27c7 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
@@ -91,11 +91,12 @@ import com.cloud.upgrade.dao.Upgrade42000to42010;
import com.cloud.upgrade.dao.Upgrade42020to42030;
import com.cloud.upgrade.dao.Upgrade42030to42040;
import com.cloud.upgrade.dao.Upgrade42040to42100;
-import com.cloud.upgrade.dao.Upgrade42100to42200;
-import com.cloud.upgrade.dao.Upgrade42200to42210;
import com.cloud.upgrade.dao.Upgrade420to421;
+import com.cloud.upgrade.dao.Upgrade42100to42200;
import com.cloud.upgrade.dao.Upgrade421to430;
+import com.cloud.upgrade.dao.Upgrade42200to42210;
import com.cloud.upgrade.dao.Upgrade42210to42300;
+import com.cloud.upgrade.dao.Upgrade42300to2400;
import com.cloud.upgrade.dao.Upgrade430to440;
import com.cloud.upgrade.dao.Upgrade431to440;
import com.cloud.upgrade.dao.Upgrade432to440;
@@ -248,6 +249,7 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
.next("4.21.0.0", new Upgrade42100to42200())
.next("4.22.0.0", new Upgrade42200to42210())
.next("4.22.1.0", new Upgrade42210to42300())
+ .next("4.23.0.0", new Upgrade42300to2400())
.build();
}
@@ -513,8 +515,13 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
String csVersion = parseSystemVmMetadata();
final CloudStackVersion sysVmVersion = CloudStackVersion.parse(csVersion);
final CloudStackVersion currentVersion = CloudStackVersion.parse(currentVersionValue);
- SystemVmTemplateRegistration.CS_MAJOR_VERSION = sysVmVersion.getMajorRelease() + "." + sysVmVersion.getMinorRelease();
- SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getPatchRelease());
+ if (sysVmVersion.usesNewVersioning()) {
+ SystemVmTemplateRegistration.CS_MAJOR_VERSION = String.valueOf(sysVmVersion.getMajorRelease());
+ SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getMajorRelease());
+ } else {
+ SystemVmTemplateRegistration.CS_MAJOR_VERSION = String.format("%d.%d", sysVmVersion.getMajorRelease(), sysVmVersion.getMinorRelease());
+ SystemVmTemplateRegistration.CS_TINY_VERSION = String.valueOf(sysVmVersion.getPatchRelease());
+ }
LOGGER.info("DB version = {} Code Version = {}", dbVersion, currentVersion);
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to2400.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to2400.java
new file mode 100644
index 00000000000..ce217cef9e7
--- /dev/null
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade42300to2400.java
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.upgrade.dao;
+
+public class Upgrade42300to2400 extends DbUpgradeAbstractImpl implements DbUpgrade, DbUpgradeSystemVmTemplate {
+
+ @Override
+ public String[] getUpgradableVersionRange() {
+ return new String[]{"4.23.0.0", "24.0.0"};
+ }
+
+ @Override
+ public String getUpgradedVersion() {
+ return "24.0.0";
+ }
+}
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42300to2400-cleanup.sql b/engine/schema/src/main/resources/META-INF/db/schema-42300to2400-cleanup.sql
new file mode 100644
index 00000000000..861a038fe7a
--- /dev/null
+++ b/engine/schema/src/main/resources/META-INF/db/schema-42300to2400-cleanup.sql
@@ -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.
+
+--;
+-- Schema upgrade cleanup from 4.23.0.0 to 24.0.0
+--;
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql b/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql
new file mode 100644
index 00000000000..7c11013a17d
--- /dev/null
+++ b/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql
@@ -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.
+
+--;
+-- Schema upgrade from 4.23.0.0 to 24.0.0
+--;
diff --git a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java
index 884398cf410..3810d03161d 100644
--- a/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java
+++ b/engine/schema/src/test/java/com/cloud/upgrade/DatabaseUpgradeCheckerTest.java
@@ -16,20 +16,25 @@
// under the License.
package com.cloud.upgrade;
-import java.sql.SQLException;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.cloudstack.utils.CloudStackVersion;
-import org.junit.Test;
-import org.junit.Before;
import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import org.junit.runner.RunWith;
-
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.Mockito;
@@ -55,15 +60,8 @@ import com.cloud.upgrade.dao.Upgrade470to471;
import com.cloud.upgrade.dao.Upgrade471to480;
import com.cloud.upgrade.dao.Upgrade480to481;
import com.cloud.upgrade.dao.Upgrade490to4910;
-
import com.cloud.utils.db.TransactionLegacy;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertArrayEquals;
-
@RunWith(MockitoJUnitRunner.class)
public class DatabaseUpgradeCheckerTest {
@@ -214,10 +212,10 @@ public class DatabaseUpgradeCheckerTest {
@Test
public void testCalculateUpgradePathUnknownDbVersion() {
- final CloudStackVersion dbVersion = CloudStackVersion.parse("4.99.0.0");
+ final CloudStackVersion dbVersion = CloudStackVersion.parse("99.0.0");
assertNotNull(dbVersion);
- final CloudStackVersion currentVersion = CloudStackVersion.parse("4.99.1.0");
+ final CloudStackVersion currentVersion = CloudStackVersion.parse("99.1.0");
assertNotNull(currentVersion);
final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker();
@@ -234,7 +232,7 @@ public class DatabaseUpgradeCheckerTest {
final CloudStackVersion dbVersion = CloudStackVersion.parse("4.17.0.0");
assertNotNull(dbVersion);
- final CloudStackVersion currentVersion = CloudStackVersion.parse("4.99.1.0");
+ final CloudStackVersion currentVersion = CloudStackVersion.parse("99.1.0");
assertNotNull(currentVersion);
final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker();
@@ -268,10 +266,7 @@ public class DatabaseUpgradeCheckerTest {
final CloudStackVersion dbVersion = checker.getLatestVersion();
assertNotNull(dbVersion);
- final CloudStackVersion currentVersion = CloudStackVersion.parse(dbVersion.getMajorRelease() + "."
- + dbVersion.getMinorRelease() + "."
- + dbVersion.getPatchRelease() + "."
- + (dbVersion.getSecurityRelease() + 1));
+ final CloudStackVersion currentVersion = getNextSecurityRelease(dbVersion);
assertNotNull(currentVersion);
final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion);
@@ -293,10 +288,7 @@ public class DatabaseUpgradeCheckerTest {
final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion);
assertNotNull(upgrades);
- final CloudStackVersion nextSecurityRelease = CloudStackVersion.parse(currentVersion.getMajorRelease() + "."
- + currentVersion.getMinorRelease() + "."
- + currentVersion.getPatchRelease() + "."
- + (currentVersion.getSecurityRelease() + 1));
+ final CloudStackVersion nextSecurityRelease = getNextSecurityRelease(currentVersion);
assertNotNull(nextSecurityRelease);
final DbUpgrade[] upgradesToNext = checker.calculateUpgradePath(dbVersion, nextSecurityRelease);
@@ -306,16 +298,26 @@ public class DatabaseUpgradeCheckerTest {
assertTrue(upgradesToNext[upgradesToNext.length - 1] instanceof NoopDbUpgrade);
}
+ private static CloudStackVersion getNextSecurityRelease(CloudStackVersion version, int increment) {
+ String nextSecurityReleaseVersionStr = version.getMajorRelease() + "."
+ + version.getMinorRelease() + "."
+ + (version.usesNewVersioning() ? "" : version.getPatchRelease() + ".")
+ + (version.getSecurityRelease() + increment);
+
+ return CloudStackVersion.parse(nextSecurityReleaseVersionStr);
+ }
+
+ private static CloudStackVersion getNextSecurityRelease(CloudStackVersion version) {
+ return getNextSecurityRelease(version, 1);
+ }
+
@Test
public void testCalculateUpgradePathFromSecurityReleaseToLatest() {
final CloudStackVersion dbVersion = CloudStackVersion.parse("4.17.2.0"); // a EOL version
assertNotNull(dbVersion);
- final CloudStackVersion oldSecurityRelease = CloudStackVersion.parse(dbVersion.getMajorRelease() + "."
- + dbVersion.getMinorRelease() + "."
- + dbVersion.getPatchRelease() + "."
- + (dbVersion.getSecurityRelease() + 100));
+ final CloudStackVersion oldSecurityRelease = getNextSecurityRelease(dbVersion, 100);
assertNotNull(oldSecurityRelease); // fake security release 4.17.2.100
final DatabaseUpgradeChecker checker = new DatabaseUpgradeChecker();
@@ -347,10 +349,7 @@ public class DatabaseUpgradeCheckerTest {
final CloudStackVersion currentVersion = checker.getLatestVersion();
assertNotNull(currentVersion);
- final CloudStackVersion nextSecurityRelease = CloudStackVersion.parse(currentVersion.getMajorRelease() + "."
- + currentVersion.getMinorRelease() + "."
- + currentVersion.getPatchRelease() + "."
- + (currentVersion.getSecurityRelease() + 1));
+ final CloudStackVersion nextSecurityRelease = getNextSecurityRelease(currentVersion);
assertNotNull(nextSecurityRelease); // fake security release
final DbUpgrade[] upgrades = checker.calculateUpgradePath(dbVersion, currentVersion);
diff --git a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java
index 7b7d80a0f16..2d14443e2d5 100644
--- a/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java
+++ b/plugins/integrations/veeam-control-service/src/main/java/org/apache/cloudstack/veeam/api/dto/Version.java
@@ -87,8 +87,12 @@ public final class Version {
}
version.setMajor(String.valueOf(csVersion.getMajorRelease()));
version.setMinor(String.valueOf(csVersion.getMinorRelease()));
- version.setBuild(String.valueOf(csVersion.getPatchRelease()));
- version.setRevision(String.valueOf(csVersion.getSecurityRelease()));
+ if (csVersion.usesNewVersioning()) {
+ version.setBuild(String.valueOf(csVersion.getSecurityRelease()));
+ } else {
+ version.setBuild(String.valueOf(csVersion.getPatchRelease()));
+ version.setRevision(String.valueOf(csVersion.getSecurityRelease()));
+ }
return version;
}
}
diff --git a/utils/src/main/java/org/apache/cloudstack/utils/CloudStackVersion.java b/utils/src/main/java/org/apache/cloudstack/utils/CloudStackVersion.java
index e29bd9c4e17..8eb4c6ab928 100644
--- a/utils/src/main/java/org/apache/cloudstack/utils/CloudStackVersion.java
+++ b/utils/src/main/java/org/apache/cloudstack/utils/CloudStackVersion.java
@@ -39,22 +39,23 @@ import static com.google.common.base.Preconditions.checkState;
*/
public final class CloudStackVersion implements Comparable<CloudStackVersion> {
- private final static Pattern NUMBER_VERSION_FORMAT = Pattern.compile("(\\d+\\.){2}(\\d+\\.)?\\d+");
- private final static Pattern FULL_VERSION_FORMAT = Pattern.compile("(\\d+\\.){2}(\\d+\\.)?\\d+(-[a-zA-Z]+)?(-\\d+)?(-SNAPSHOT)?");
+ private final static Pattern NUMBER_VERSION_FORMAT = Pattern.compile("\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?");
+ private final static Pattern FULL_VERSION_FORMAT = Pattern.compile("\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?(?:-[a-zA-Z]+)?(?:-\\d+)?(?:-SNAPSHOT)?");
+ private final static int NEW_VERSIONING_CUTOVER_MAJOR_VERSION = 24;
private final int majorRelease;
private final int minorRelease;
- private final int patchRelease;
+ private final Integer patchRelease;
private final Integer securityRelease;
- private CloudStackVersion(final int majorRelease, final int minorRelease, final int patchRelease, final Integer securityRelease) {
+ private CloudStackVersion(final int majorRelease, final int minorRelease, final Integer patchRelease, final Integer securityRelease) {
super();
checkArgument(majorRelease >= 0, CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a majorRelease greater than 0.");
checkArgument(minorRelease >= 0, CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a minorRelease greater than 0.");
- checkArgument(patchRelease >= 0, CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a patchRelease greater than 0.");
- checkArgument((securityRelease != null && securityRelease >= 0) || (securityRelease == null),
+ checkArgument(patchRelease == null || patchRelease >= 0, CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a patchRelease greater than 0.");
+ checkArgument(securityRelease == null || securityRelease >= 0,
CloudStackVersion.class.getName() + "(int, int, int, Integer) requires a null securityRelease or a non-null value greater than 0.");
this.majorRelease = majorRelease;
@@ -69,11 +70,13 @@ public final class CloudStackVersion implements Comparable<CloudStackVersion> {
* Parses a <code>String</code> representation of a version that conforms one of the following
* formats into a <code>CloudStackVersion</code> instance:
* <ul>
- * <li><code><major>.<minor>.<patch>.<security></code></li>
- * <li><code><major>.<minor>.<patch>.<security>.<security></code></li>
- * <li><code><major>.<minor>.<patch>.<security>.<security>-<any string></code></li>
+ * <li><code><major>.<minor>.<patch></code> (legacy, deprecated since 24.0.0, allowed only below major version 24)</li>
+ * <li><code><major>.<minor>.<patch>.<security></code> (legacy, deprecated since 24.0.0, allowed only below major version 24)</li>
+ * <li><code><major>.<minor>.<security release></code> (for versions >= 24.0.0)</li>
* </ul>
*
+ * Legacy patch-based formats remain supported for backward compatibility.
+ *
* If the string contains a suffix that begins with a "-" character, then the "-" and all characters following it
* will be dropped.
*
@@ -91,7 +94,7 @@ public final class CloudStackVersion implements Comparable<CloudStackVersion> {
checkArgument(StringUtils.isNotBlank(trimmedValue), CloudStackVersion.class.getName() + ".parse(String) requires a non-blank value");
checkArgument(NUMBER_VERSION_FORMAT.matcher(trimmedValue).matches(), CloudStackVersion.class.getName() + ".parse(String) passed " +
- value + ", but requires a value in the format of int.int.int(.int)(-<legacy patch>)");
+ value + ", but requires a value in the format of int.int.int(.int)(-<suffix>)");
final String[] components = trimmedValue.split("\\.");
@@ -100,8 +103,26 @@ public final class CloudStackVersion implements Comparable<CloudStackVersion> {
final int majorRelease = Integer.valueOf(components[0]);
final int minorRelease = Integer.valueOf(components[1]);
- final int patchRelease = Integer.valueOf(components[2]);
- final Integer securityRelease = components.length == 3 ? null : Integer.valueOf(components[3]);
+ final int thirdComponent = Integer.valueOf(components[2]);
+
+ final int patchRelease;
+ final Integer securityRelease;
+
+ if (components.length == 4) {
+ checkArgument(isLegacyVersioning(majorRelease), CloudStackVersion.class.getName() + ".parse(String) passed " + value +
+ ", but major versions at or above 24 do not support legacy int.int.int.int format");
+ // Deprecated legacy format: major.minor.patch.security
+ patchRelease = thirdComponent;
+ securityRelease = Integer.valueOf(components[3]);
+ } else if (isNewVersioning(majorRelease)) {
+ // New format: major.minor.securityRelease (patch dropped)
+ patchRelease = 0;
+ securityRelease = thirdComponent;
+ } else {
+ // Deprecated legacy format: major.minor.patch
+ patchRelease = thirdComponent;
+ securityRelease = null;
+ }
return new CloudStackVersion(majorRelease, minorRelease, patchRelease, securityRelease);
@@ -207,6 +228,14 @@ public final class CloudStackVersion implements Comparable<CloudStackVersion> {
}
+ private static boolean isLegacyVersioning(final int majorRelease) {
+ return majorRelease < NEW_VERSIONING_CUTOVER_MAJOR_VERSION;
+ }
+
+ private static boolean isNewVersioning(final int majorRelease) {
+ return majorRelease >= NEW_VERSIONING_CUTOVER_MAJOR_VERSION;
+ }
+
/**
*
* @return The components of this version as an {@link ImmutableList} in order of major release, minor release,
@@ -244,6 +273,10 @@ public final class CloudStackVersion implements Comparable<CloudStackVersion> {
return securityRelease;
}
+ public boolean usesNewVersioning() {
+ return isNewVersioning(majorRelease);
+ }
+
@Override
public boolean equals(final Object thatObject) {
@@ -270,6 +303,11 @@ public final class CloudStackVersion implements Comparable<CloudStackVersion> {
@Override
public String toString() {
+ // Canonicalize cutover-and-later versions to major.minor.securityRelease.
+ if (securityRelease != null && patchRelease == 0 && isNewVersioning(majorRelease)) {
+ return Joiner.on(".").join(ImmutableList.of(majorRelease, minorRelease, securityRelease));
+ }
+
return Joiner.on(".").join(asList());
}
diff --git a/utils/src/test/java/org/apache/cloudstack/utils/CloudStackVersionTest.java b/utils/src/test/java/org/apache/cloudstack/utils/CloudStackVersionTest.java
index dabaf9bc97d..4d0b4cb0439 100644
--- a/utils/src/test/java/org/apache/cloudstack/utils/CloudStackVersionTest.java
+++ b/utils/src/test/java/org/apache/cloudstack/utils/CloudStackVersionTest.java
@@ -36,7 +36,11 @@ public final class CloudStackVersionTest {
"1.2.3, 1.2.3",
"1.2.3.4, 1.2.3.4",
"1.2.3-12, 1.2.3",
- "1.2.3.4-14, 1.2.3.4"
+ "1.2.3.4-14, 1.2.3.4",
+ "23.9.5, 23.9.5",
+ "24.0.0, 24.0.0",
+ "24.0.1, 24.0.1",
+ "25.1.1, 25.1.1"
})
public void testValidParse(final String inputValue, final String expectedVersion) {
final CloudStackVersion version = CloudStackVersion.parse(inputValue);
@@ -44,6 +48,28 @@ public final class CloudStackVersionTest {
assertEquals(expectedVersion, version.toString());
}
+ @Test
+ public void testParseComponentMappingForLegacyAndNewVersioning() {
+ final CloudStackVersion legacyVersion = CloudStackVersion.parse("23.9.5");
+ assertEquals(23, legacyVersion.getMajorRelease());
+ assertEquals(9, legacyVersion.getMinorRelease());
+ assertEquals(5, legacyVersion.getPatchRelease());
+ Assert.assertNull(legacyVersion.getSecurityRelease());
+
+ final CloudStackVersion newVersion = CloudStackVersion.parse("24.0.1");
+ assertEquals(24, newVersion.getMajorRelease());
+ assertEquals(0, newVersion.getMinorRelease());
+ // Patch is retained as 0 to represent "no patch" in the new major.minor.security scheme.
+ assertEquals(0, newVersion.getPatchRelease());
+ assertEquals(Integer.valueOf(1), newVersion.getSecurityRelease());
+
+ final CloudStackVersion futureNewVersion = CloudStackVersion.parse("25.1.1");
+ assertEquals(25, futureNewVersion.getMajorRelease());
+ assertEquals(1, futureNewVersion.getMinorRelease());
+ assertEquals(0, futureNewVersion.getPatchRelease());
+ assertEquals(Integer.valueOf(1), futureNewVersion.getSecurityRelease());
+ }
+
@Test(expected = IllegalArgumentException.class)
@DataProvider({
"1.2",
@@ -52,7 +78,10 @@ public final class CloudStackVersionTest {
"aaaa",
"",
" ",
- "1.2.3.4.5"
+ "1.2.3.4.5",
+ "24.0.0.1",
+ "25.0.0.1",
+ "26.2.3.4"
})
public void testInvalidParse(final String invalidValue) {
CloudStackVersion.parse(invalidValue);
@@ -147,7 +176,9 @@ public final class CloudStackVersionTest {
"1.2.3.4-10, 1.0.0.0-5",
"1.2.3-10, 1.0.0-5",
"1.2.3.4, 1.0.0.0-5",
- "1.2.3.4-10, 1.0.0"
+ "1.2.3.4-10, 1.0.0",
+ "24.0.2, 24.0.1",
+ "24.1.0, 24.0.9"
})
public void testGreaterThanAndLessThanCompareTo(final String value, final String thatValue) {
@@ -178,7 +209,9 @@ public final class CloudStackVersionTest {
"1.2.3.4-10, 1.0.0.0-5",
"1.2.3-10, 1.0.0-5",
"1.2.3.4, 1.0.0.0-5",
- "1.2.3.4-10, 1.0.0"
+ "1.2.3.4-10, 1.0.0",
+ "24.0.2, 24.0.1",
+ "24.1.0, 24.0.9"
})
public void testGreaterThanAndLessThanCompareDirect(final String value, final String thatValue) {
@@ -213,6 +246,7 @@ public final class CloudStackVersionTest {
Assert.assertEquals(CloudStackVersion.getVMwareParentVersion(hypervisorVersion), expectedParentVersion);
}
}
+
@Test
public void testGetParentVersion() {
verifyGetVMwareParentVersion(null, null);
@@ -223,5 +257,6 @@ public final class CloudStackVersionTest {
verifyGetVMwareParentVersion("8.0.0", "8.0");
verifyGetVMwareParentVersion("8.0.0.2", "8.0");
verifyGetVMwareParentVersion("8.0.1.0", "8.0.1");
+ verifyGetVMwareParentVersion("24.1.1", "24.1");
}
}