← Back to team overview

sts-sponsors team mailing list archive

[Merge] ~alexsander-souza/maas:fix_vmfs_layout into maas:master

 

Alexsander de Souza has proposed merging ~alexsander-souza/maas:fix_vmfs_layout into maas:master.

Commit message:
Update ESXi 7+ layout to new disk size

packer-maas template has been updated to use a 32GB image, and as consequence the size of the partitions has grow. This patch reflects this change in the MAAS storage layout.

Requested reviews:
  MAAS Maintainers (maas-maintainers)

For more details, see:
https://code.launchpad.net/~alexsander-souza/maas/+git/maas/+merge/442046

a 32GB disk has always been a requirement of ESXi 7+ storage layout, but only recently the installer started to enforce this limit.
-- 
Your team MAAS Committers is subscribed to branch maas:master.
diff --git a/src/maasserver/models/partition.py b/src/maasserver/models/partition.py
index 84a8fba..76515e5 100644
--- a/src/maasserver/models/partition.py
+++ b/src/maasserver/models/partition.py
@@ -304,44 +304,26 @@ class Partition(CleanSave, TimestampedModel):
                         }
                     )
 
-    def is_vmfs6_partition(self):
-        # Avoid circular imports.
-        from maasserver.storage_layouts import VMFS6StorageLayout
-
-        vmfs_layout = VMFS6StorageLayout(self.get_node())
-        vmfs_bd = vmfs_layout.is_layout()
-        if vmfs_bd is None:
-            return False
-        if vmfs_bd.id != self.partition_table.block_device_id:
-            return False
-        if self.index >= len(vmfs_layout.base_partitions) + 2:
-            # A user may apply the VMFS6 layout and leave space at the end of
-            # the disk for additional VMFS datastores. Those partitions may be
-            # deleted, the base partitions may not as they are part of the DD.
-            # The + 2 is to account for partition 4 being skipped.
-            return False
-        return True
-
-    def is_vmfs7_partition(self):
+    def is_vmfs_partition(self):
         # Avoid circular imports.
-        from maasserver.storage_layouts import VMFS7StorageLayout
-
-        vmfs_layout = VMFS7StorageLayout(self.get_node())
-        vmfs_bd = vmfs_layout.is_layout()
-        if vmfs_bd is None:
-            return False
-        if vmfs_bd.id != self.partition_table.block_device_id:
-            return False
-        if self.index < len(vmfs_layout.base_partitions) + 3:
-            # A user may apply the VMFS7 layout and leave space at the end of
-            # the disk for additional VMFS datastores. Those partitions may be
-            # deleted, the base partitions may not as they are part of the DD.
-            # The + 3 is to account for partition 2-4 being skipped.
-            return True
-        return False
+        from maasserver.storage_layouts import (
+            VMFS6StorageLayout,
+            VMFS7StorageLayout,
+        )
 
-    def is_vmfs_partition(self):
-        return self.is_vmfs6_partition() or self.is_vmfs7_partition()
+        part_blk_dev_id = self.partition_table.block_device_id
+
+        for layout_class in (VMFS7StorageLayout, VMFS6StorageLayout):
+            vmfs_layout = layout_class(self.get_node())
+            vmfs_bd = vmfs_layout.is_layout()
+            if vmfs_bd is None or vmfs_bd.id != part_blk_dev_id:
+                continue
+            if self.index <= vmfs_layout.last_base_partition_index:
+                # A user may apply the VMFS layout and leave space at the end of
+                # the disk for additional VMFS datastores. Those partitions may be
+                # deleted, the base partitions may not as they are part of the DD.
+                return True
+        return False
 
     def delete(self):
         """Delete the partition.
diff --git a/src/maasserver/models/tests/test_partition.py b/src/maasserver/models/tests/test_partition.py
index 4956d8f..2c9f3bb 100644
--- a/src/maasserver/models/tests/test_partition.py
+++ b/src/maasserver/models/tests/test_partition.py
@@ -544,45 +544,26 @@ class TestPartition(MAASServerTestCase):
             [partition.index for partition in partitions],
         )
 
-    def test_is_vmfs6_partition(self):
-        node = factory.make_Node(with_boot_disk=False)
-        bd = factory.make_PhysicalBlockDevice(
-            node=node, size=LARGE_BLOCK_DEVICE
-        )
-        layout = VMFS6StorageLayout(node)
-        layout.configure()
-        pt = bd.get_partitiontable()
-        for partition in pt.partitions.all():
-            self.assertTrue(partition.is_vmfs6_partition())
-
-    def test_is_vmfs7_partition(self):
-        node = factory.make_Node(with_boot_disk=False)
-        bd = factory.make_PhysicalBlockDevice(
-            node=node, size=LARGE_BLOCK_DEVICE
-        )
-        layout = VMFS7StorageLayout(node)
-        layout.configure()
-        pt = bd.get_partitiontable()
-        for partition in pt.partitions.all():
-            if partition.index >= 8:
-                self.assertFalse(partition.is_vmfs7_partition())
-            else:
-                self.assertTrue(partition.is_vmfs7_partition())
-
     def test_is_vmfs_partition(self):
         node = factory.make_Node(with_boot_disk=False)
         bd = factory.make_PhysicalBlockDevice(
             node=node, size=LARGE_BLOCK_DEVICE
         )
-        vmfs_layout = random.choice([VMFS6StorageLayout, VMFS7StorageLayout])
-        layout = vmfs_layout(node)
-        layout_name = layout.configure()
-        pt = bd.get_partitiontable()
-        for partition in pt.partitions.all():
-            if layout_name == "vmfs7" and partition.index >= 8:
-                self.assertFalse(partition.is_vmfs_partition())
-            else:
-                self.assertTrue(partition.is_vmfs_partition())
+        for vmfs_layout in [VMFS6StorageLayout, VMFS7StorageLayout]:
+            layout = vmfs_layout(node)
+            layout_name = layout.configure()
+            pt = bd.get_partitiontable()
+            for partition in pt.partitions.all():
+                if layout_name == "vmfs7" and partition.index >= 8:
+                    self.assertFalse(
+                        partition.is_vmfs_partition(),
+                        f"{layout_name} index {partition.index} is VMFS",
+                    )
+                else:
+                    self.assertTrue(
+                        partition.is_vmfs_partition(),
+                        f"{layout_name} index {partition.index} is VMFS",
+                    )
 
     def test_is_vmfs_partition_false_no_vmfs(self):
         partition = factory.make_Partition()
diff --git a/src/maasserver/storage_layouts.py b/src/maasserver/storage_layouts.py
index 8e1b1e8..4056191 100644
--- a/src/maasserver/storage_layouts.py
+++ b/src/maasserver/storage_layouts.py
@@ -798,25 +798,29 @@ class VMFS6StorageLayout(StorageLayoutBase):
     name = "vmfs6"
     title = "VMFS6 layout"
 
-    base_partitions = [
-        # EFI System
-        {"index": 1, "size": 3 * 1024**2, "bootable": True},
-        # Basic Data
-        {"index": 2, "size": 4 * 1024**3},
-        # VMFS Datastore, size is 0 so the partition order is correct, its
-        # fixed after everything is applied.
-        {"index": 3, "size": 0},
-        # Basic Data
-        {"index": 5, "size": 249 * 1024**2},
-        # Basic Data
-        {"index": 6, "size": 249 * 1024**2},
-        # VMKCore Diagnostic
-        {"index": 7, "size": 109 * 1024**2},
-        # Basic Data
-        {"index": 8, "size": 285 * 1024**2},
-        # VMKCore Diagnostic
-        {"index": 9, "size": 2560 * 1024**2},
-    ]
+    _default_layout = "default"
+
+    base_partitions = {
+        "default": [
+            # EFI System
+            {"index": 1, "size": 3 * 1024**2, "bootable": True},
+            # Basic Data
+            {"index": 2, "size": 4 * 1024**3},
+            # VMFS Datastore, size is 0 so the partition order is correct, its
+            # fixed after everything is applied.
+            {"index": 3, "size": 0},
+            # Basic Data
+            {"index": 5, "size": 249 * 1024**2},
+            # Basic Data
+            {"index": 6, "size": 249 * 1024**2},
+            # VMKCore Diagnostic
+            {"index": 7, "size": 109 * 1024**2},
+            # Basic Data
+            {"index": 8, "size": 285 * 1024**2},
+            # VMKCore Diagnostic
+            {"index": 9, "size": 2560 * 1024**2},
+        ],
+    }
 
     def _clean_boot_disk(self):
         if self.boot_disk.size < (10 * 1024**3):
@@ -854,7 +858,7 @@ class VMFS6StorageLayout(StorageLayoutBase):
                     updated=now,
                     **partition,
                 )
-                for partition in self.base_partitions
+                for partition in self.base_partitions[self._default_layout]
             ]
         )
         vmfs_part = boot_partition_table.partitions.get(size=0)
@@ -877,54 +881,77 @@ class VMFS6StorageLayout(StorageLayoutBase):
             if pt.table_type != PARTITION_TABLE_TYPE.GPT:
                 continue
             partitions = sorted(pt.partitions.all(), key=attrgetter("id"))
-            if len(partitions) < len(self.base_partitions):
-                continue
-            for i, (partition, base_partition) in enumerate(
-                zip(partitions, self.base_partitions)
-            ):
-                if (i + 1) == len(self.base_partitions):
-                    return bd
-                if partition.bootable != base_partition.get("bootable", False):
-                    break
-                # Skip checking the size of the Datastore partition as that
-                # changes based on available disk size/user input.
-                if base_partition["size"] == 0:
+            for layout in self.base_partitions.values():
+                if len(partitions) < len(layout):
                     continue
-                if partition.size != base_partition["size"]:
-                    break
+                for i, (partition, base_partition) in enumerate(
+                    zip(partitions, layout)
+                ):
+                    if (i + 1) == len(layout):
+                        return bd
+                    if partition.bootable != base_partition.get(
+                        "bootable", False
+                    ):
+                        break
+                    # Skip checking the size of the Datastore partition as that
+                    # changes based on available disk size/user input.
+                    if base_partition["size"] == 0:
+                        continue
+                    if partition.size != base_partition["size"]:
+                        break
         return None
 
+    @property
+    def last_base_partition_index(self):
+        return self.base_partitions[self._default_layout][-1]["index"]
+
 
 class VMFS7StorageLayout(VMFS6StorageLayout):
     """VMFS7 layout.
 
     The VMware ESXi 7+ image is a DD. The image has 5 partitions which are
     in order but not linear. Users may only change the last partition which
-    is partition 8 and stored at the end of the disk.
-
-    NAME                PARTITION   SIZE      START BLOCK   END BLOCK
-    EFI System          1           105MB     0             105
-    Basic Data          5           1074MB    106           1180
-    Basic Data          6           1074MB    1181          2255
-    VMFSL               7           8.5GB     2256          10959
-    VMFS                8           Remaining 10960         End of disk
+    is partition 8 and stored at the end of the disk. In recent ESXi 7 ISOs
+    and version 8 onwards, the 32GB minimum disk size is being enforced,
+    so the Packer template has been updated.
+
+    NAME                PARTITION   SIZE
+    EFI System          1           100MB
+    Basic Data          5           4GB
+    Basic Data          6           4GB
+    VMFSL               7           23.8GB
+    VMFS                8           Remaining
     """
 
     name = "vmfs7"
     title = "VMFS7 layout"
 
-    base_partitions = [
-        # EFI System
-        {"index": 1, "size": 105 * 1024**2, "bootable": True},
-        # Basic Data
-        {"index": 5, "size": 1074 * 1024**2},
-        # Basic Data
-        {"index": 6, "size": 1074 * 1024**2},
-        # VMFSL
-        {"index": 7, "size": 8704 * 1024**2},
-        # VMFS
-        {"index": 8, "size": 0},
-    ]
+    base_partitions = {
+        "default": [
+            # EFI System
+            {"index": 1, "size": 100 * 1024**2, "bootable": True},
+            # Basic Data
+            {"index": 5, "size": 4 * 1024**3},
+            # Basic Data
+            {"index": 6, "size": 4 * 1024**3},
+            # VMFSL
+            {"index": 7, "size": 24320 * 1024**2},
+            # VMFS
+            {"index": 8, "size": 0},
+        ],
+        "legacy": [
+            # EFI System
+            {"index": 1, "size": 105 * 1024**2, "bootable": True},
+            # Basic Data
+            {"index": 5, "size": 1074 * 1024**2},
+            # Basic Data
+            {"index": 6, "size": 1074 * 1024**2},
+            # VMFSL
+            {"index": 7, "size": 8704 * 1024**2},
+            # VMFS
+            {"index": 8, "size": 0},
+        ],
+    }
 
     def _clean_boot_disk(self):
         """https://docs.vmware.com/en/VMware-vSphere/7.0/com.vmware.esxi.install.doc/GUID-DEB8086A-306B-4239-BF76-E354679202FC.html
@@ -939,9 +966,10 @@ class VMFS7StorageLayout(VMFS6StorageLayout):
                 self, "boot_size", "Boot disk must be at least 32Gb."
             )
 
-    def configure_storage(self, allow_fallback):
-        super().configure_storage(allow_fallback)
-        return self.name
+    @property
+    def last_base_partition_index(self):
+        # VMFS partition can be modified by the user
+        return self.base_partitions[self._default_layout][-2]["index"]
 
 
 class CustomStorageLayout(StorageLayoutBase):
diff --git a/src/maasserver/tests/test_storage_layouts.py b/src/maasserver/tests/test_storage_layouts.py
index 0fd7a94..84128ad 100644
--- a/src/maasserver/tests/test_storage_layouts.py
+++ b/src/maasserver/tests/test_storage_layouts.py
@@ -1992,18 +1992,18 @@ class TestVMFS7StorageLayout(MAASServerTestCase):
         pt = node.boot_disk.get_partitiontable()
         self.assertEqual(
             {
-                "%s-part1" % node.boot_disk.name: 105 * 1024**2,
-                "%s-part5" % node.boot_disk.name: 1074 * 1024**2,
-                "%s-part6" % node.boot_disk.name: 1074 * 1024**2,
-                "%s-part7" % node.boot_disk.name: 8704 * 1024**2,
+                "%s-part1" % node.boot_disk.name: 100 * 1024**2,
+                "%s-part5" % node.boot_disk.name: 4 * 1024**3,
+                "%s-part6" % node.boot_disk.name: 4 * 1024**3,
+                "%s-part7" % node.boot_disk.name: 24320 * 1024**2,
                 "%s-part8"
                 % node.boot_disk.name: (
                     node.boot_disk.size
-                    - 105 * 1024**2
-                    - 1074 * 1024**2
-                    - 1074 * 1024**2
-                    - 8704 * 1024**2
-                    - 7 * 1024**2
+                    - 100 * 1024**2
+                    - 4 * 1024**3
+                    - 4 * 1024**3
+                    - 24320 * 1024**2
+                    - 8 * 1024**2  # rounding
                 ),
             },
             {part.name: part.size for part in pt.partitions.all()},
@@ -2035,18 +2035,18 @@ class TestVMFS7StorageLayout(MAASServerTestCase):
         pt = root_disk.get_partitiontable()
         self.assertEqual(
             {
-                "%s-part1" % root_disk.name: 105 * 1024**2,
-                "%s-part5" % root_disk.name: 1074 * 1024**2,
-                "%s-part6" % root_disk.name: 1074 * 1024**2,
-                "%s-part7" % root_disk.name: 8704 * 1024**2,
+                "%s-part1" % root_disk.name: 100 * 1024**2,
+                "%s-part5" % root_disk.name: 4 * 1024**3,
+                "%s-part6" % root_disk.name: 4 * 1024**3,
+                "%s-part7" % root_disk.name: 24320 * 1024**2,
                 "%s-part8"
                 % root_disk.name: (
                     root_disk.size
-                    - 105 * 1024**2
-                    - 1074 * 1024**2
-                    - 1074 * 1024**2
-                    - 8704 * 1024**2
-                    - 7 * 1024**2
+                    - 100 * 1024**2
+                    - 4 * 1024**3
+                    - 4 * 1024**3
+                    - 24320 * 1024**2
+                    - 8 * 1024**2
                 ),
             },
             {part.name: part.size for part in pt.partitions.all()},
@@ -2062,10 +2062,10 @@ class TestVMFS7StorageLayout(MAASServerTestCase):
         pt = node.boot_disk.get_partitiontable()
         self.assertEqual(
             {
-                "%s-part1" % node.boot_disk.name: 105 * 1024**2,
-                "%s-part5" % node.boot_disk.name: 1074 * 1024**2,
-                "%s-part6" % node.boot_disk.name: 1074 * 1024**2,
-                "%s-part7" % node.boot_disk.name: 8704 * 1024**2,
+                "%s-part1" % node.boot_disk.name: 100 * 1024**2,
+                "%s-part5" % node.boot_disk.name: 4 * 1024**3,
+                "%s-part6" % node.boot_disk.name: 4 * 1024**3,
+                "%s-part7" % node.boot_disk.name: 24320 * 1024**2,
                 "%s-part8" % node.boot_disk.name: 10 * 1024**3,
             },
             {part.name: part.size for part in pt.partitions.all()},
@@ -2080,6 +2080,16 @@ class TestVMFS7StorageLayout(MAASServerTestCase):
         layout.configure()
         self.assertEqual(bd, layout.is_layout())
 
+    def test_is_layout_legacy(self):
+        node = make_Node_with_uefi_boot_method()
+        bd = factory.make_PhysicalBlockDevice(
+            node=node, size=LARGE_BLOCK_DEVICE
+        )
+        layout = VMFS7StorageLayout(node)
+        layout._default_layout = "legacy"
+        layout.configure()
+        self.assertEqual(bd, layout.is_layout())
+
     def test_is_layout_without_datastore(self):
         node = make_Node_with_uefi_boot_method()
         bd = factory.make_PhysicalBlockDevice(

Follow ups