File: base.py

package info (click to toggle)
bootstrap-vz 0.9.11%2B20180121git-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 2,244 kB
  • sloc: python: 8,800; sh: 813; makefile: 16
file content (138 lines) | stat: -rw-r--r-- 5,925 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import os
from abstract import AbstractPartition
from bootstrapvz.common.sectors import Sectors


class BasePartition(AbstractPartition):
    """Represents a partition that is actually a partition (and not a virtual one like 'Single')
    """

    # Override the states of the abstract partition
    # A real partition can be mapped and unmapped
    events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'unmapped'},
              {'name': 'map', 'src': 'unmapped', 'dst': 'mapped'},
              {'name': 'format', 'src': 'mapped', 'dst': 'formatted'},
              {'name': 'mount', 'src': 'formatted', 'dst': 'mounted'},
              {'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'},
              {'name': 'unmap', 'src': 'formatted', 'dst': 'unmapped_fmt'},

              {'name': 'map', 'src': 'unmapped_fmt', 'dst': 'formatted'},
              {'name': 'unmap', 'src': 'mapped', 'dst': 'unmapped'},
              ]

    def __init__(self, size, filesystem, format_command, mountopts, previous):
        """
        :param Bytes size: Size of the partition
        :param str filesystem: Filesystem the partition should be formatted with
        :param list format_command: Optional format command, valid variables are fs, device_path and size
        :param BasePartition previous: The partition that preceeds this one
        """
        # By saving the previous partition we have a linked list
        # that partitions can go backwards in to find the first partition.
        self.previous = previous
        # List of flags that parted should put on the partition
        self.flags = []
        # Path to symlink in /dev/disk/by-uuid (manually maintained by this class)
        self.disk_by_uuid_path = None
        super(BasePartition, self).__init__(size, filesystem, format_command, mountopts)

    def create(self, volume):
        """Creates the partition

        :param Volume volume: The volume to create the partition on
        """
        self.fsm.create(volume=volume)

    def get_index(self):
        """Gets the index of this partition in the partition map

        :return: The index of the partition in the partition map
        :rtype: int
        """
        if self.previous is None:
            # Partitions are 1 indexed
            return 1
        else:
            # Recursive call to the previous partition, walking up the chain...
            return self.previous.get_index() + 1

    def get_start(self):
        """Gets the starting byte of this partition

        :return: The starting byte of this partition
        :rtype: Sectors
        """
        if self.previous is None:
            return Sectors(0, self.size.sector_size)
        else:
            return self.previous.get_end()

    def map(self, device_path):
        """Maps the partition to a device_path

        :param str device_path: The device path this partition should be mapped to
        """
        self.fsm.map(device_path=device_path)

    def link_uuid(self):
        # /lib/udev/rules.d/60-kpartx.rules does not create symlinks in /dev/disk/by-{uuid,label}
        # This patch would fix that: http://www.redhat.com/archives/dm-devel/2013-July/msg00080.html
        # For now we just do the uuid part ourselves.
        # This is mainly to fix a problem in update-grub where /etc/grub.d/10_linux
        # checks if the $GRUB_DEVICE_UUID exists in /dev/disk/by-uuid and falls
        # back to $GRUB_DEVICE if it doesn't.
        # $GRUB_DEVICE is /dev/mapper/xvd{f,g...}# (on ec2), opposed to /dev/xvda# when booting.
        # Creating the symlink ensures that grub consistently uses
        # $GRUB_DEVICE_UUID when creating /boot/grub/grub.cfg
        self.disk_by_uuid_path = os.path.join('/dev/disk/by-uuid', self.get_uuid())
        if not os.path.exists(self.disk_by_uuid_path):
            os.symlink(self.device_path, self.disk_by_uuid_path)

    def unlink_uuid(self):
        if os.path.isfile(self.disk_by_uuid_path):
            os.remove(self.disk_by_uuid_path)
        self.disk_by_uuid_path = None

    def _before_create(self, e):
        """Creates the partition
        """
        from bootstrapvz.common.tools import log_check_call
        # The create command is fairly simple:
        # - fs_type is the partition filesystem, as defined by parted:
        #   fs-type can be one of "fat16", "fat32", "ext2", "HFS", "linux-swap",
        #   "NTFS", "reiserfs", or "ufs".
        # - start and end are just Bytes objects coerced into strings
        if self.filesystem == 'swap':
            fs_type = 'linux-swap'
        else:
            fs_type = 'ext2'
        create_command = ('mkpart primary {fs_type} {start} {end}'
                          .format(fs_type=fs_type,
                                  start=str(self.get_start() + self.pad_start),
                                  end=str(self.get_end() - self.pad_end)))
        # Create the partition
        log_check_call(['parted', '--script', '--align', 'none', e.volume.device_path,
                        '--', create_command])

        # Set any flags on the partition
        for flag in self.flags:
            log_check_call(['parted', '--script', e.volume.device_path,
                            '--', ('set {idx} {flag} on'
                                   .format(idx=str(self.get_index()), flag=flag))])

    def _before_map(self, e):
        # Set the device path
        self.device_path = e.device_path
        if e.src == 'unmapped_fmt':
            # Only link the uuid if the partition is formatted
            self.link_uuid()

    def _after_format(self, e):
        # We do this after formatting because there otherwise would be no UUID
        self.link_uuid()

    def _before_unmap(self, e):
        # When unmapped, the device_path information becomes invalid, so we delete it
        self.device_path = None
        if e.src == 'formatted':
            self.unlink_uuid()