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 <[email protected]>
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>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;.&lt;security&gt;</code></li>
-     *     
<li><code>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;.&lt;security&gt;.&lt;security&gt;</code></li>
-     *     
<li><code>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;.&lt;security&gt;.&lt;security&gt;-&lt;any
 string&gt;</code></li>
+     *     <li><code>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;</code> (legacy, 
deprecated since 24.0.0, allowed only below major version 24)</li>
+     *     
<li><code>&lt;major&gt;.&lt;minor&gt;.&lt;patch&gt;.&lt;security&gt;</code> 
(legacy, deprecated since 24.0.0, allowed only below major version 24)</li>
+     *     <li><code>&lt;major&gt;.&lt;minor&gt;.&lt;security 
release&gt;</code> (for versions &gt;= 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");
     }
 }

Reply via email to