File: abstract.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 (136 lines) | stat: -rw-r--r-- 5,484 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
from abc import ABCMeta
from abc import abstractmethod
from bootstrapvz.common.sectors import Sectors
from bootstrapvz.common.tools import log_check_call
from bootstrapvz.common.fsm_proxy import FSMProxy


class AbstractPartition(FSMProxy):
    """Abstract representation of a partiton
    This class is a finite state machine and represents the state of the real partition
    """

    __metaclass__ = ABCMeta

    # Our states
    events = [{'name': 'create', 'src': 'nonexistent', 'dst': 'created'},
              {'name': 'format', 'src': 'created', 'dst': 'formatted'},
              {'name': 'mount', 'src': 'formatted', 'dst': 'mounted'},
              {'name': 'unmount', 'src': 'mounted', 'dst': 'formatted'},
              ]

    def __init__(self, size, filesystem, format_command, mountopts):
        """
        :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
        """
        self.size           = size
        self.filesystem     = filesystem
        self.format_command = format_command
        # List of mount options
        self.mountopts      = mountopts
        # Initialize the start & end padding to 0 sectors, may be changed later
        self.pad_start = Sectors(0, size.sector_size)
        self.pad_end = Sectors(0, size.sector_size)
        # Path to the partition
        self.device_path    = None
        # Dictionary with mount points as keys and Mount objects as values
        self.mounts         = {}

        # Create the configuration for our state machine
        cfg = {'initial': 'nonexistent', 'events': self.events, 'callbacks': {}}
        super(AbstractPartition, self).__init__(cfg)

    def get_uuid(self):
        """Gets the UUID of the partition

        :return: The UUID of the partition
        :rtype: str
        """
        [uuid] = log_check_call(['blkid', '-s', 'UUID', '-o', 'value', self.device_path])
        return uuid

    @abstractmethod
    def get_start(self):
        pass

    def get_end(self):
        """Gets the end of the partition

        :return: The end of the partition
        :rtype: Sectors
        """
        return self.get_start() + self.pad_start + self.size + self.pad_end

    def _before_format(self, e):
        """Formats the partition
        """
        # If there is no explicit format_command define we simply call mkfs.fstype
        if self.format_command is None:
            format_command = ['mkfs.{fs}', '{device_path}']
        else:
            format_command = self.format_command
        variables = {'fs': self.filesystem,
                     'device_path': self.device_path,
                     'size': self.size,
                     }
        command = map(lambda part: part.format(**variables), format_command)
        # Format the partition
        log_check_call(command)

    def _before_mount(self, e):
        """Mount the partition
        """
        if self.mountopts is None:
            mount_command = ['mount', '--types', self.filesystem, self.device_path, e.destination]
        else:
            mount_command = ['mount', '--options', ",".join(self.mountopts), '--types', self.filesystem, self.device_path, e.destination]
        # Mount the partition
        log_check_call(mount_command)
        self.mount_dir = e.destination

    def _after_mount(self, e):
        """Mount any mounts associated with this partition
        """
        # Make sure we mount in ascending order of mountpoint path length
        # This ensures that we don't mount /dev/pts before we mount /dev
        for destination in sorted(self.mounts.iterkeys(), key=len):
            self.mounts[destination].mount(self.mount_dir)

    def _before_unmount(self, e):
        """Unmount any mounts associated with this partition
        """
        # Unmount the mounts in descending order of mounpoint path length
        # You cannot unmount /dev before you have unmounted /dev/pts
        for destination in sorted(self.mounts.iterkeys(), key=len, reverse=True):
            self.mounts[destination].unmount()
        log_check_call(['umount', self.mount_dir])
        del self.mount_dir

    def add_mount(self, source, destination, opts=[]):
        """Associate a mount with this partition
        Automatically mounts it

        :param str,AbstractPartition source: The source of the mount
        :param str destination: The path to the mountpoint
        :param list opts: Any options that should be passed to the mount command
        """
        # Create a new mount object, mount it if the partition is mounted and put it in the mounts dict
        from mount import Mount
        mount = Mount(source, destination, opts)
        if self.fsm.current == 'mounted':
            mount.mount(self.mount_dir)
        self.mounts[destination] = mount

    def remove_mount(self, destination):
        """Remove a mount from this partition
        Automatically unmounts it

        :param str destination: The mountpoint path of the mount that should be removed
        """
        # Unmount the mount if the partition is mounted and delete it from the mounts dict
        # If the mount is already unmounted and the source is a partition, this will raise an exception
        if self.fsm.current == 'mounted':
            self.mounts[destination].unmount()
        del self.mounts[destination]