]>
Commit | Line | Data |
---|---|---|
9f95a23c TL |
1 | import logging |
2 | ||
3 | try: | |
4 | from typing import List, Optional | |
5 | except ImportError: | |
6 | pass | |
7 | ||
e306af50 | 8 | from ..inventory import Device |
9f95a23c TL |
9 | from ..drive_group import DriveGroupSpec, DeviceSelection |
10 | ||
11 | from .filter import FilterGenerator | |
12 | ||
13 | logger = logging.getLogger(__name__) | |
14 | ||
15 | ||
16 | class DriveSelection(object): | |
17 | def __init__(self, | |
18 | spec, # type: DriveGroupSpec | |
e306af50 | 19 | disks, # type: List[Device] |
f91f0fd5 | 20 | existing_daemons=None, # type: Optional[int] |
9f95a23c TL |
21 | ): |
22 | self.disks = disks.copy() | |
23 | self.spec = spec | |
f91f0fd5 | 24 | self.existing_daemons = existing_daemons or 0 |
9f95a23c | 25 | |
f67539c2 TL |
26 | self._data = self.assign_devices(self.spec.data_devices) |
27 | self._wal = self.assign_devices(self.spec.wal_devices) | |
28 | self._db = self.assign_devices(self.spec.db_devices) | |
29 | self._journal = self.assign_devices(self.spec.journal_devices) | |
9f95a23c TL |
30 | |
31 | def data_devices(self): | |
32 | # type: () -> List[Device] | |
33 | return self._data | |
34 | ||
35 | def wal_devices(self): | |
36 | # type: () -> List[Device] | |
37 | return self._wal | |
38 | ||
39 | def db_devices(self): | |
40 | # type: () -> List[Device] | |
41 | return self._db | |
42 | ||
43 | def journal_devices(self): | |
44 | # type: () -> List[Device] | |
45 | return self._journal | |
46 | ||
f91f0fd5 | 47 | def _limit_reached(self, device_filter, len_devices, |
9f95a23c TL |
48 | disk_path): |
49 | # type: (DeviceSelection, int, str) -> bool | |
50 | """ Check for the <limit> property and apply logic | |
51 | ||
52 | If a limit is set in 'device_attrs' we have to stop adding | |
53 | disks at some point. | |
54 | ||
55 | If limit is set (>0) and len(devices) >= limit | |
56 | ||
57 | :param int len_devices: Length of the already populated device set/list | |
58 | :param str disk_path: The disk identifier (for logging purposes) | |
59 | :return: True/False if the device should be added to the list of devices | |
60 | :rtype: bool | |
61 | """ | |
62 | limit = device_filter.limit or 0 | |
63 | ||
f91f0fd5 | 64 | if limit > 0 and (len_devices + self.existing_daemons >= limit): |
9f95a23c TL |
65 | logger.info("Refuse to add {} due to limit policy of <{}>".format( |
66 | disk_path, limit)) | |
67 | return True | |
68 | return False | |
69 | ||
70 | @staticmethod | |
71 | def _has_mandatory_idents(disk): | |
72 | # type: (Device) -> bool | |
73 | """ Check for mandatory identification fields | |
74 | """ | |
75 | if disk.path: | |
76 | logger.debug("Found matching disk: {}".format(disk.path)) | |
77 | return True | |
78 | else: | |
79 | raise Exception( | |
80 | "Disk {} doesn't have a 'path' identifier".format(disk)) | |
81 | ||
82 | def assign_devices(self, device_filter): | |
83 | # type: (Optional[DeviceSelection]) -> List[Device] | |
84 | """ Assign drives based on used filters | |
85 | ||
86 | Do not add disks when: | |
87 | ||
88 | 1) Filter didn't match | |
89 | 2) Disk doesn't have a mandatory identification item (path) | |
90 | 3) The set :limit was reached | |
91 | ||
92 | After the disk was added we make sure not to re-assign this disk | |
93 | for another defined type[wal/db/journal devices] | |
94 | ||
95 | return a sorted(by path) list of devices | |
96 | """ | |
97 | ||
98 | if not device_filter: | |
99 | logger.debug('device_filter is None') | |
100 | return [] | |
101 | ||
102 | if not self.spec.data_devices: | |
103 | logger.debug('data_devices is None') | |
104 | return [] | |
105 | ||
f67539c2 TL |
106 | if device_filter.paths: |
107 | logger.debug('device filter is using explicit paths') | |
108 | return device_filter.paths | |
109 | ||
9f95a23c TL |
110 | devices = list() # type: List[Device] |
111 | for disk in self.disks: | |
112 | logger.debug("Processing disk {}".format(disk.path)) | |
113 | ||
9f95a23c TL |
114 | if not self._has_mandatory_idents(disk): |
115 | logger.debug( | |
116 | "Ignoring disk {}. Missing mandatory idents".format( | |
117 | disk.path)) | |
118 | continue | |
119 | ||
120 | # break on this condition. | |
121 | if self._limit_reached(device_filter, len(devices), disk.path): | |
122 | logger.debug("Ignoring disk {}. Limit reached".format( | |
123 | disk.path)) | |
124 | break | |
125 | ||
126 | if disk in devices: | |
127 | continue | |
128 | ||
f6b5b4d7 TL |
129 | if self.spec.filter_logic == 'AND': |
130 | if not all(m.compare(disk) for m in FilterGenerator(device_filter)): | |
131 | logger.debug( | |
132 | "Ignoring disk {}. Not all filter did match the disk".format( | |
133 | disk.path)) | |
134 | continue | |
135 | ||
136 | if self.spec.filter_logic == 'OR': | |
137 | if not any(m.compare(disk) for m in FilterGenerator(device_filter)): | |
138 | logger.debug( | |
139 | "Ignoring disk {}. No filter matched the disk".format( | |
140 | disk.path)) | |
141 | continue | |
9f95a23c TL |
142 | |
143 | logger.debug('Adding disk {}'.format(disk.path)) | |
144 | devices.append(disk) | |
145 | ||
146 | # This disk is already taken and must not be re-assigned. | |
147 | for taken_device in devices: | |
148 | if taken_device in self.disks: | |
149 | self.disks.remove(taken_device) | |
150 | ||
151 | return sorted([x for x in devices], key=lambda dev: dev.path) |