~allenap/maas/database-locks-rererevisited

« back to all changes in this revision

Viewing changes to src/maasserver/storage_layouts.py

Merged database-locks-rerevisited into database-locks-rererevisited.

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
    CACHE_MODE_TYPE,
23
23
    CACHE_MODE_TYPE_CHOICES,
24
24
    FILESYSTEM_TYPE,
 
25
    PARTITION_TABLE_TYPE,
25
26
)
26
27
from maasserver.exceptions import MAASAPIValidationError
27
28
from maasserver.fields_storage import (
37
38
 
38
39
EFI_PARTITION_SIZE = 512 * 1024 * 1024  # 512 MiB
39
40
MIN_BOOT_PARTITION_SIZE = 512 * 1024 * 1024  # 512 GiB
40
 
DEFAULT_BOOT_PARTITION_SIZE = 1 * 1024 * 1024 * 1024  # 1 GiB
41
41
MIN_ROOT_PARTITION_SIZE = 3 * 1024 * 1024 * 1024  # 3 GiB
42
42
 
43
43
 
63
63
        super(StorageLayoutBase, self).__init__(data=params)
64
64
        self.node = node
65
65
        self.block_devices = self._load_physical_block_devices()
 
66
        self.boot_disk = node.get_boot_disk()
66
67
        self.setup_root_device_field()
67
68
 
68
69
    def _load_physical_block_devices(self):
81
82
            choices=choices, required=False,
82
83
            error_messages={'invalid_choice': invalid_choice_message})
83
84
 
84
 
    def get_boot_disk(self):
85
 
        """Return the boot disk for the node."""
86
 
        if len(self.block_devices) > 0:
87
 
            return self.block_devices[0]
88
 
        else:
89
 
            return None
90
 
 
91
85
    def _clean_size(self, field, min_size=None, max_size=None):
92
86
        """Clean a size field."""
93
87
        size = self.cleaned_data[field]
96
90
        if is_precentage(size):
97
91
            # Calculate the precentage not counting the EFI partition.
98
92
            size = calculate_size_from_precentage(
99
 
                self.get_boot_disk().size - EFI_PARTITION_SIZE, size)
 
93
                self.boot_disk.size - EFI_PARTITION_SIZE, size)
100
94
        if min_size is not None and size < min_size:
101
95
            raise ValidationError(
102
96
                "Size is too small. Minimum size is %s." % min_size)
107
101
 
108
102
    def clean_boot_size(self):
109
103
        """Clean the boot_size field."""
110
 
        boot_disk = self.get_boot_disk()
111
 
        if boot_disk is not None:
 
104
        if self.boot_disk is not None:
112
105
            return self._clean_size(
113
106
                'boot_size', MIN_BOOT_PARTITION_SIZE, (
114
 
                    boot_disk.size - EFI_PARTITION_SIZE -
 
107
                    self.boot_disk.size - EFI_PARTITION_SIZE -
115
108
                    MIN_ROOT_PARTITION_SIZE))
116
109
        else:
117
110
            return None
118
111
 
119
112
    def clean_root_size(self):
120
113
        """Clean the root_size field."""
121
 
        boot_disk = self.get_boot_disk()
122
 
        if boot_disk is not None:
 
114
        if self.boot_disk is not None:
123
115
            return self._clean_size(
124
116
                'root_size', MIN_ROOT_PARTITION_SIZE, (
125
 
                    boot_disk.size - EFI_PARTITION_SIZE -
 
117
                    self.boot_disk.size - EFI_PARTITION_SIZE -
126
118
                    MIN_BOOT_PARTITION_SIZE))
127
119
        else:
128
120
            return None
133
125
        if len(self.block_devices) == 0:
134
126
            raise StorageLayoutMissingBootDiskError(
135
127
                "Node doesn't have any storage devices to configure.")
136
 
        disk_size = self.get_boot_disk().size
 
128
        disk_size = self.boot_disk.size
137
129
        total_size = (
138
130
            EFI_PARTITION_SIZE + self.get_boot_size())
139
131
        root_size = self.get_root_size()
159
151
        if self.cleaned_data.get('boot_size'):
160
152
            return self.cleaned_data['boot_size']
161
153
        else:
162
 
            return DEFAULT_BOOT_PARTITION_SIZE
 
154
            return 0
163
155
 
164
156
    def get_root_size(self):
165
157
        """Get the size of the root partition.
179
171
        # Circular imports.
180
172
        from maasserver.models.filesystem import Filesystem
181
173
        from maasserver.models.partitiontable import PartitionTable
182
 
        boot_disk = self.get_boot_disk()
183
174
        boot_partition_table = PartitionTable.objects.create(
184
 
            block_device=boot_disk)
185
 
        efi_partition = boot_partition_table.add_partition(
186
 
            size=EFI_PARTITION_SIZE, bootable=True)
187
 
        boot_partition = boot_partition_table.add_partition(
188
 
            size=self.get_boot_size(), bootable=True)
 
175
            block_device=self.boot_disk)
 
176
        if boot_partition_table.table_type == PARTITION_TABLE_TYPE.GPT:
 
177
            efi_partition = boot_partition_table.add_partition(
 
178
                size=EFI_PARTITION_SIZE, bootable=True)
 
179
            Filesystem.objects.create(
 
180
                partition=efi_partition,
 
181
                fstype=FILESYSTEM_TYPE.FAT32,
 
182
                label="efi",
 
183
                mount_point="/boot/efi")
 
184
        boot_size = self.get_boot_size()
 
185
        if boot_size > 0:
 
186
            boot_partition = boot_partition_table.add_partition(
 
187
                size=self.get_boot_size(), bootable=True)
 
188
            Filesystem.objects.create(
 
189
                partition=boot_partition,
 
190
                fstype=FILESYSTEM_TYPE.EXT4,
 
191
                label="boot",
 
192
                mount_point="/boot")
189
193
        root_device = self.get_root_device()
190
 
        if root_device is None or root_device == boot_disk:
 
194
        if root_device is None or root_device == self.boot_disk:
191
195
            root_partition = boot_partition_table.add_partition(
192
196
                size=self.get_root_size())
193
197
        else:
195
199
                block_device=root_device)
196
200
            root_partition = root_partition_table.add_partition(
197
201
                size=self.get_root_size())
198
 
        Filesystem.objects.create(
199
 
            partition=efi_partition,
200
 
            fstype=FILESYSTEM_TYPE.FAT32,
201
 
            label="efi",
202
 
            mount_point="/boot/efi")
203
 
        Filesystem.objects.create(
204
 
            partition=boot_partition,
205
 
            fstype=FILESYSTEM_TYPE.EXT4,
206
 
            label="boot",
207
 
            mount_point="/boot")
208
202
        return root_partition
209
203
 
210
204
    def configure(self, allow_fallback=True):
227
221
 
228
222
    NAME        SIZE        TYPE    FSTYPE         MOUNTPOINT
229
223
    sda         100G        disk
230
 
      sda15     512M        part    fat32          /boot/efi
231
 
      sda1      1G          part    ext4           /boot
232
 
      sda2      98.5G       part    ext4           /
 
224
      sda1      512M        part    fat32          /boot/efi
 
225
      sda2      99.5G       part    ext4           /
233
226
    """
234
227
 
235
228
    def configure_storage(self, allow_fallback):
250
243
 
251
244
    NAME        SIZE        TYPE    FSTYPE         MOUNTPOINT
252
245
    sda         100G        disk
253
 
      sda15     512M        part    fat32          /boot/efi
254
 
      sda1      1G          part    ext4           /boot
255
 
      sda2      98.5G       part    lvm-pv(vgroot)
256
 
    vgroot      98.5G       lvm
257
 
      lvroot    98.5G       lvm     ext4           /
 
246
      sda1      512M        part    fat32          /boot/efi
 
247
      sda2      99.5G       part    lvm-pv(vgroot)
 
248
    vgroot      99.5G       lvm
 
249
      lvroot    99.5G       lvm     ext4           /
258
250
    """
259
251
 
260
252
    DEFAULT_VG_NAME = "vgroot"
304
296
            root_size = self.get_root_size()
305
297
            if root_size is None:
306
298
                root_size = (
307
 
                    self.get_boot_disk().size - EFI_PARTITION_SIZE -
 
299
                    self.boot_disk.size - EFI_PARTITION_SIZE -
308
300
                    self.get_boot_size())
309
301
            if is_precentage(lv_size):
310
302
                lv_size = calculate_size_from_precentage(
354
346
 
355
347
    def setup_cache_device_field(self):
356
348
        """Setup the possible cache devices."""
357
 
        boot_disk = self.get_boot_disk()
358
 
        if boot_disk is None:
 
349
        if self.boot_disk is None:
359
350
            return
360
351
        choices = [
361
352
            (block_device.id, block_device.id)
362
353
            for block_device in self.block_devices
363
 
            if block_device != boot_disk
 
354
            if block_device != self.boot_disk
364
355
        ]
365
356
        invalid_choice_message = compose_invalid_choice_text(
366
357
            'cache_device', choices)
370
361
 
371
362
    def _find_best_cache_device(self):
372
363
        """Return the best possible cache device on the node."""
373
 
        boot_disk = self.get_boot_disk()
374
 
        if boot_disk is None:
 
364
        if self.boot_disk is None:
375
365
            return None
376
366
        block_devices = self.node.physicalblockdevice_set.exclude(
377
 
            id__in=[boot_disk.id]).order_by('size')
 
367
            id__in=[self.boot_disk.id]).order_by('size')
378
368
        for block_device in block_devices:
379
369
            if "ssd" in block_device.tags:
380
370
                return block_device
464
454
 
465
455
    NAME        SIZE        TYPE    FSTYPE         MOUNTPOINT
466
456
    sda         100G        disk
467
 
      sda15     512M        part    fat32          /boot/efi
468
 
      sda1      1G          part    ext4           /boot
469
 
      sda2      98.5G       part    bc-backing
 
457
      sda1      512M        part    fat32          /boot/efi
 
458
      sda2      99.5G       part    bc-backing
470
459
    sdb         50G         disk
471
460
      sdb1      50G         part    bc-cache
472
 
    bcache0     98.5G       disk    ext4           /
 
461
    bcache0     99.5G       disk    ext4           /
473
462
    """
474
463
 
475
464
    def __init__(self, node, params={}):
511
500
        return "bcache"
512
501
 
513
502
 
514
 
class BcacheLVMStorageLayout(LVMStorageLayout, BcacheStorageLayoutBase):
515
 
    """Bcache+LVM layout.
516
 
 
517
 
    NAME        SIZE        TYPE    FSTYPE         MOUNTPOINT
518
 
    sda         100G        disk
519
 
      sda15     512M        part    fat32          /boot/efi
520
 
      sda1      1G          part    ext4           /boot
521
 
      sda2      98.5G       part    lvm-pv(vgroot)
522
 
    sdb         50G         disk
523
 
      sdb1      50G         part    bc-cache
524
 
    vgroot      98.5G       lvm
525
 
      lvroot    98.5G       lvm     bc-backing
526
 
    bcache0     98.5G       disk    ext4           /
527
 
    """
528
 
 
529
 
    def __init__(self, node, params={}):
530
 
        super(BcacheLVMStorageLayout, self).__init__(node, params=params)
531
 
        self.setup_cache_device_field()
532
 
 
533
 
    def configure_storage(self, allow_fallback):
534
 
        """Create the Bcache+LVM configuration."""
535
 
        # Circular imports.
536
 
        from maasserver.models.filesystem import Filesystem
537
 
        from maasserver.models.filesystemgroup import (
538
 
            Bcache,
539
 
            VolumeGroup,
540
 
            )
541
 
        cache_block_device = self.get_cache_device()
542
 
        if cache_block_device is None:
543
 
            if allow_fallback:
544
 
                # No cache device so just configure using the lvm layout.
545
 
                return super(BcacheLVMStorageLayout, self).configure_storage(
546
 
                    allow_fallback)
547
 
            else:
548
 
                raise StorageLayoutError(
549
 
                    "Node doesn't have an available cache device to "
550
 
                    "setup bcache.")
551
 
 
552
 
        root_partition = self.create_basic_layout()
553
 
        volume_group = VolumeGroup.objects.create_volume_group(
554
 
            self.get_vg_name(), block_devices=[], partitions=[root_partition])
555
 
        logical_volume = volume_group.create_logical_volume(
556
 
            self.get_lv_name(), self.get_calculated_lv_size(volume_group))
557
 
        cache_device = self.create_cache_device()
558
 
        create_kwargs = {
559
 
            "backing_device": logical_volume,
560
 
            "cache_mode": self.get_cache_mode(),
561
 
        }
562
 
        if cache_device.type == "partition":
563
 
            create_kwargs['cache_partition'] = cache_device
564
 
        else:
565
 
            create_kwargs['cache_device'] = cache_device
566
 
        bcache = Bcache.objects.create_bcache(**create_kwargs)
567
 
        Filesystem.objects.create(
568
 
            block_device=bcache.virtual_device,
569
 
            fstype=FILESYSTEM_TYPE.EXT4,
570
 
            label="root",
571
 
            mount_point="/")
572
 
        return "bcache+lvm"
573
 
 
574
 
 
575
503
# Holds all the storage layouts that can be used.
576
504
STORAGE_LAYOUTS = {
577
505
    "flat": ("Flat layout", FlatStorageLayout),
578
506
    "lvm": ("LVM layout", LVMStorageLayout),
579
507
    "bcache": ("Bcache layout", BcacheStorageLayout),
580
 
    "bcache+lvm": ("Bcache+LVM layout", BcacheLVMStorageLayout),
581
508
    }
582
509
 
583
510