~newell-jensen/maas/fix-1516065-1.9

« back to all changes in this revision

Viewing changes to src/maasserver/models/node.py

  • Committer: Lee Trager
  • Date: 2015-11-18 18:14:30 UTC
  • mto: This revision was merged to the branch mainline in revision 4502.
  • Revision ID: lee.trager@canonical.com-20151118181430-b4zwfr9gei12rmf7
Backport of 4506 from trunk: Validate a node has a boot device and / is mounted before deploying

Show diffs side-by-side

added added

removed removed

Lines of Context:
63
63
from maasserver.enum import (
64
64
    ALLOCATED_NODE_STATUSES,
65
65
    FILESYSTEM_FORMAT_TYPE_CHOICES_DICT,
 
66
    FILESYSTEM_TYPE,
66
67
    INTERFACE_LINK_TYPE,
67
68
    INTERFACE_TYPE,
68
69
    IPADDRESS_FAMILY,
760
761
                event_action=action,
761
762
                event_description=description)
762
763
 
 
764
    def storage_layout_issues(self):
 
765
        """Return any errors with the storage layout.
 
766
 
 
767
        Checks that the node has / mounted. If / is mounted on bcache check
 
768
        that /boot is mounted and is not on bcache."""
 
769
        def on_bcache(obj):
 
770
            if obj.type == "physical":
 
771
                return False
 
772
            elif obj.type == "partition":
 
773
                return on_bcache(obj.partition_table.block_device)
 
774
            for parent in obj.virtualblockdevice.get_parents():
 
775
                if((parent.type != "physical" and on_bcache(parent)) or
 
776
                    (parent.get_effective_filesystem().fstype in
 
777
                     [FILESYSTEM_TYPE.BCACHE_CACHE,
 
778
                      FILESYSTEM_TYPE.BCACHE_BACKING])):
 
779
                    return True
 
780
            return False
 
781
 
 
782
        has_boot = False
 
783
        root_mounted = False
 
784
        root_on_bcache = False
 
785
        boot_mounted = False
 
786
 
 
787
        for block_device in self.blockdevice_set.all():
 
788
            if block_device.is_boot_disk():
 
789
                has_boot = True
 
790
            pt = block_device.get_partitiontable()
 
791
            if pt is not None:
 
792
                for partition in pt.partitions.all():
 
793
                    fs = partition.get_effective_filesystem()
 
794
                    if fs is None:
 
795
                        continue
 
796
                    if fs.mount_point == '/':
 
797
                        root_mounted = True
 
798
                        if on_bcache(block_device):
 
799
                            root_on_bcache = True
 
800
                        else:
 
801
                            # If / is mounted and its not on bcache the system
 
802
                            # is bootable
 
803
                            return []
 
804
                    elif (fs.mount_point == '/boot' and
 
805
                          not on_bcache(block_device)):
 
806
                        boot_mounted = True
 
807
            else:
 
808
                fs = block_device.get_effective_filesystem()
 
809
                if fs is None:
 
810
                    continue
 
811
                if fs.mount_point == '/':
 
812
                    root_mounted = True
 
813
                    if on_bcache(block_device):
 
814
                        root_on_bcache = True
 
815
                    else:
 
816
                        # If / is mounted and its not on bcache the system
 
817
                        # is bootable.
 
818
                        return []
 
819
                elif fs.mount_point == '/boot' and not on_bcache(block_device):
 
820
                    boot_mounted = True
 
821
        issues = []
 
822
        if not has_boot:
 
823
            issues.append("Node must have boot disk.")
 
824
        if not root_mounted:
 
825
            issues.append("Node must have / mounted.")
 
826
        if root_mounted and root_on_bcache and not boot_mounted:
 
827
            issues.append("Because / is on a bcache volume you must create "
 
828
                          "/boot on a non-bcache volume")
 
829
        return issues
 
830
 
763
831
    def on_network(self):
764
832
        """Return true if the node is connected to a managed network."""
765
833
        for interface in self.interface_set.all():
772
840
    def _start_deployment(self):
773
841
        """Mark a node as being deployed."""
774
842
        if self.on_network() is False:
775
 
            raise ValidationError("Node must be configured to use a network")
 
843
            raise ValidationError(
 
844
                {"network":
 
845
                 ["Node must be configured to use a network"]})
 
846
        storage_layout_issues = self.storage_layout_issues()
 
847
        if len(storage_layout_issues) > 0:
 
848
            raise ValidationError({"storage": storage_layout_issues})
776
849
        self.status = NODE_STATUS.DEPLOYING
777
850
        self.save()
778
851