remap-badblocks 0.8__py3-none-any.whl → 0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. remap_badblocks/__init__.py +1 -1
  2. remap_badblocks/cli/commands/update.py +8 -6
  3. remap_badblocks/src/badblocks/_remap_badblocks.py +23 -23
  4. remap_badblocks/src/mapping.py +5 -1
  5. remap_badblocks-0.9.dist-info/METADATA +107 -0
  6. remap_badblocks-0.9.dist-info/RECORD +36 -0
  7. {remap_badblocks-0.8.dist-info → remap_badblocks-0.9.dist-info}/WHEEL +1 -1
  8. __init__.py +0 -0
  9. cli/__init__.py +0 -0
  10. cli/__main__.py +0 -239
  11. cli/commands/__init__.py +0 -8
  12. cli/commands/add.py +0 -91
  13. cli/commands/apply.py +0 -81
  14. cli/commands/get.py +0 -28
  15. cli/commands/remove.py +0 -45
  16. cli/commands/update.py +0 -208
  17. cli/commands/version.py +0 -15
  18. remap_badblocks-0.8.dist-info/METADATA +0 -130
  19. remap_badblocks-0.8.dist-info/RECORD +0 -66
  20. src/badblocks/_compute_good_ranges.py +0 -43
  21. src/badblocks/_find_badblocks.py +0 -76
  22. src/badblocks/_mapping_generation.py +0 -12
  23. src/badblocks/_remap_badblocks.py +0 -114
  24. src/badblocks/badblocks.py +0 -40
  25. src/devices/__init__.py +0 -0
  26. src/devices/device_config.py +0 -62
  27. src/devices/devices_config.py +0 -300
  28. src/devices/exceptions.py +0 -22
  29. src/devices_config_constants.py +0 -3
  30. src/mapping.py +0 -109
  31. src/remappers/_check_applied_devices.py +0 -10
  32. src/remappers/_generate_dm_table.py +0 -27
  33. src/test_utils.py +0 -18
  34. src/utils/__init__.py +0 -0
  35. src/utils/_get_device_info.py +0 -43
  36. src/utils/_iterable_bytes_converter.py +0 -19
  37. src/utils/_parse_inputs.py +0 -84
  38. src/utils/_run_command.py +0 -76
  39. src/utils/_sort_devices.py +0 -26
  40. {remap_badblocks-0.8.dist-info → remap_badblocks-0.9.dist-info}/entry_points.txt +0 -0
  41. {remap_badblocks-0.8.dist-info → remap_badblocks-0.9.dist-info}/licenses/LICENSE +0 -0
  42. {remap_badblocks-0.8.dist-info → remap_badblocks-0.9.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,3 @@
1
1
  from packaging.version import Version
2
2
 
3
- __version__ = Version("0.8")
3
+ __version__ = Version("0.9")
@@ -9,7 +9,7 @@ from remap_badblocks.src.badblocks._find_badblocks import (
9
9
  get_all_badblocks, read_known_badblocks)
10
10
  from remap_badblocks.src.badblocks._mapping_generation import generate_mapping
11
11
  from remap_badblocks.src.badblocks._remap_badblocks import (
12
- count_used_spare_sectors, iter_all_spare_sectors, iter_free_spare_sectors,
12
+ filter_out_used_and_bad_spare_sectors, iter_all_spare_sectors,
13
13
  remap_badblocks, simplify_mapping)
14
14
  from remap_badblocks.src.devices.device_config import DeviceConfig
15
15
  from remap_badblocks.src.devices.devices_config import DevicesConfig
@@ -165,13 +165,15 @@ def update_mapping(
165
165
  good_ranges,
166
166
  )
167
167
  else:
168
+ badblocks_ = set(badblocks)
168
169
  spare_sectors = iter_all_spare_sectors(n_spare_sectors, device.logical_range[0])
169
- n_used_spare_sectors = count_used_spare_sectors(device.mapping, spare_sectors)
170
- current_mapping = map(lambda x: x.to_tuple(), device.mapping)
170
+ spare_sectors = filter_out_used_and_bad_spare_sectors(
171
+ spare_sectors, device.mapping, badblocks_
172
+ )
171
173
  new_mapping = remap_badblocks(
172
- current_mapping,
173
- badblocks,
174
- spare_sectors=iter_free_spare_sectors(spare_sectors, n_used_spare_sectors),
174
+ device.mapping,
175
+ badblocks_,
176
+ spare_sectors,
175
177
  )
176
178
 
177
179
  new_mapping = list(new_mapping)
@@ -1,13 +1,13 @@
1
- import itertools
2
- from typing import Iterable, Iterator, Optional, Union
1
+ from collections.abc import Container, Generator, Iterable, Iterator
2
+ from typing import Optional, Union
3
3
 
4
4
  from remap_badblocks.src.devices.devices_config import Mapping
5
5
 
6
6
 
7
7
  def remap_badblocks(
8
- mapping: Iterable[tuple[int, int, int]],
8
+ mapping: Union[Iterable[tuple[int, int, int]], Mapping],
9
9
  badblocks: Iterable[int],
10
- spare_sectors: Iterator[int],
10
+ usable_spare_sectors: Iterator[int],
11
11
  ) -> Iterable[tuple[int, int, int]]:
12
12
  sorted_badblocks = sorted(badblocks)
13
13
 
@@ -37,10 +37,11 @@ def remap_badblocks(
37
37
  yield current_start_virtual, current_start_real, end_real - current_start_real + 1
38
38
  for virt_to_remap in sorted_to_remap_virtual:
39
39
  try:
40
- while (spare_sector := next(spare_sectors)) in badblocks:
41
- pass
40
+ spare_sector = next(usable_spare_sectors)
42
41
  except StopIteration:
43
- raise RuntimeError
42
+ raise RuntimeError(
43
+ "Not enough spare sectors to remap all new badblocks"
44
+ )
44
45
  # remap
45
46
  yield virt_to_remap, spare_sector, 1
46
47
 
@@ -93,22 +94,21 @@ def iter_all_spare_sectors(
93
94
  return iter(range(first_spare_sector, first_spare_sector + n_spare_sectors))
94
95
 
95
96
 
96
- def iter_free_spare_sectors(
97
- spare_sectors: Iterable[int], n_used_spare_sectors: int
98
- ) -> Iterator[int]:
99
- return itertools.islice(spare_sectors, n_used_spare_sectors, None)
100
-
101
-
102
- def count_used_spare_sectors(
103
- mapping: Union[Iterable[tuple[int, int, int]], Mapping],
104
- spare_sectors: Iterable[int],
105
- ) -> int:
106
- spare_sectors = set(spare_sectors)
107
- used = 0
97
+ def is_sector_used_in_mapping(
98
+ s: int, mapping: Union[Iterable[tuple[int, int, int]], Mapping]
99
+ ):
108
100
  for _, start, length in mapping:
109
101
  end = start + length
110
- for s in spare_sectors:
111
- if start <= s < end:
112
- used += 1
102
+ if start <= s < end:
103
+ return True
104
+ return False
113
105
 
114
- return used
106
+
107
+ def filter_out_used_and_bad_spare_sectors(
108
+ spare_sectors: Iterable[int],
109
+ current_mapping: Mapping,
110
+ badblocks: Container[int],
111
+ ) -> Generator[int]:
112
+ for s in spare_sectors:
113
+ if s not in badblocks and not is_sector_used_in_mapping(s, current_mapping):
114
+ yield s
@@ -1,6 +1,6 @@
1
1
  import sqlite3
2
2
  from dataclasses import dataclass
3
- from typing import Collection, Iterator
3
+ from typing import Collection, Iterable, Iterator
4
4
 
5
5
  from remap_badblocks.src.utils._iterable_bytes_converter import (
6
6
  iterable_from_bytes, iterable_to_bytes)
@@ -107,3 +107,7 @@ class Mapping:
107
107
 
108
108
  def __iter__(self) -> Iterator[MappingElement]:
109
109
  return iter(self.elements)
110
+
111
+ @classmethod
112
+ def from_tuples(cls, mapping_tuples: Iterable[tuple[int, int, int]]) -> "Mapping":
113
+ return cls(set(map(MappingElement.from_tuple, mapping_tuples)))
@@ -0,0 +1,107 @@
1
+ Metadata-Version: 2.4
2
+ Name: remap_badblocks
3
+ Version: 0.9
4
+ Summary: CLI tool for remapping bad sectors on Linux disks using device-mapper
5
+ Author-email: Luigi Privitera <priviteraluigi98@gmail.com>
6
+ License-Expression: GPL-3.0-only
7
+ Project-URL: Repository, https://gitlab.com/Luigi-98/remap_badblocks.git
8
+ Project-URL: Issues, https://gitlab.com/Luigi-98/remap_badblocks/-/issues
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Provides-Extra: dev
13
+ Requires-Dist: mypy; extra == "dev"
14
+ Requires-Dist: flake8; extra == "dev"
15
+ Requires-Dist: ruff; extra == "dev"
16
+ Requires-Dist: pytest>=7.0; extra == "dev"
17
+ Requires-Dist: pre-commit; extra == "dev"
18
+ Requires-Dist: black; extra == "dev"
19
+ Requires-Dist: isort; extra == "dev"
20
+ Dynamic: license-file
21
+
22
+ # remap-badblocks
23
+
24
+ `remap-badblocks` is a Linux CLI tool written in Python that scans a block device for bad sectors and creates a device-mapper target that skips over them. It can also reserve extra space to dynamically remap future badblocks.
25
+
26
+ It's a simple way to extend the usefulness of slightly damaged drives, ideal for anyone who wants to avoid discarding a disk due to a few bad sectors.
27
+
28
+ ## Features
29
+
30
+ * Scans disks using `badblocks` to identify damaged sectors
31
+ * Creates a `device-mapper` target that avoids bad sectors
32
+ * Reserves spare sectors for dynamic remapping
33
+ * Supports using only part of a device (e.g., for hard partitioning or isolation)
34
+
35
+ ## Installation
36
+
37
+ ### From PyPI
38
+
39
+ ```bash
40
+ pip install remap-badblocks
41
+ ```
42
+
43
+ ### From source
44
+
45
+ ```bash
46
+ git clone https://gitlab.com/Luigi-98/remap-badblocks.git
47
+ cd remap-badblocks
48
+ pip install -e .[dev]
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ### Database
54
+
55
+ `remap-badblocks` relies on a single source of stateful storage: a SQLite database. By default, it is stored at `/etc/remap_badblocks/devices_config.db`, but you can specify an alternative location using the `-P` option.
56
+
57
+ ### Device Identification
58
+
59
+ Devices can be referenced by either:
60
+
61
+ * **Name**: set at creation, used as the name of the output device-mapper
62
+ * **ID**: an internal remap-badblocks identifier
63
+
64
+ ### CLI Reference
65
+
66
+ ```bash
67
+ remap-badblocks --help
68
+ ```
69
+
70
+ ### Example Workflow
71
+
72
+ ```bash
73
+ # Add a new device
74
+ remap-badblocks add my_remapped_device --path /dev/sda
75
+
76
+ # Run a read-mode scan and initialize the mapping
77
+ # by reserving spare space
78
+ remap-badblocks update --name my_remapped_device --mode read --spare-space 100MB
79
+
80
+ # Apply changes to generate the device-mapper
81
+ remap-badblocks apply
82
+
83
+ # Resulting device appears under /dev/mapper
84
+ ls /dev/mapper/
85
+ # /dev/mapper/my_remapped_device
86
+ ```
87
+
88
+ ## Contributing
89
+
90
+ We welcome feedback, bug reports, feature ideas, code contributions, and help shaping the roadmap — or even rethinking the project from the ground up.
91
+
92
+ Start with CONTRIBUTING.md, then open an issue or submit a merge request to get involved.
93
+
94
+ ## License
95
+
96
+ GPL v3 License — see LICENSE for details.
97
+
98
+ ## Community
99
+
100
+ * GitLab: [remap-badblocks project page](https://gitlab.com/Luigi-98/remap_badblocks)
101
+ * PyPI: [remap-badblocks](https://pypi.org/project/remap-badblocks/)
102
+ * Issues: [remap-badblocks Gitlab issues](https://gitlab.com/Luigi-98/remap_badblocks/-/issues)
103
+
104
+ ## Acknowledgements & Related Tools
105
+
106
+ * [`badblocks`](https://linux.die.net/man/8/badblocks)
107
+ * [`device-mapper`](https://linux.die.net/man/8/dmsetup)
@@ -0,0 +1,36 @@
1
+ remap_badblocks/__init__.py,sha256=uh9pQzPKdlMD_35fZuzjLFDquAyCl9a9KlBFbGf0sQI,68
2
+ remap_badblocks/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ remap_badblocks/cli/__main__.py,sha256=9p8yZNyu012JYMfAkQVcVkhShaoyre44jcYzK0ow4NY,7132
4
+ remap_badblocks/cli/commands/__init__.py,sha256=tGo7_yWZsDpvIIcA9y-4AlLB6grVlK2zaJ6VUKdpONo,216
5
+ remap_badblocks/cli/commands/add.py,sha256=XRNtzUlwRmFD-5VM2us-odvx5smUcIBM6MBF7786p84,2693
6
+ remap_badblocks/cli/commands/apply.py,sha256=uwLqEGYz4HVV_5V5Fe3dDDpmS9pt6VUmdZBsVyCnwyk,2534
7
+ remap_badblocks/cli/commands/get.py,sha256=qDb_KP4YZA9wiXMlXk2i19TDRGaHasPQKcnkWiVv2dA,740
8
+ remap_badblocks/cli/commands/remove.py,sha256=C3eBGmujtRktBJlGCtvdCRHBVWjRaTDx0o-LQpLEKB8,1186
9
+ remap_badblocks/cli/commands/update.py,sha256=kX_ZGvkm5dx6tOsViLHc_6S9ybuBcBvAW1i9yFjMGrA,7042
10
+ remap_badblocks/cli/commands/version.py,sha256=uJIZLqK8zlNEgXlx-V7-yhhvsZfBKAE1tlF76t_Fg2I,168
11
+ remap_badblocks/src/devices_config_constants.py,sha256=spRtelXdR9kWw-MzFI-CWfmZ6QM0gXvVHbk4J1CeKcM,103
12
+ remap_badblocks/src/mapping.py,sha256=1OI4Si8QTWa3IM9dG5ms7bHNVDjqAsvK8hqF9r1XvGM,3409
13
+ remap_badblocks/src/test_utils.py,sha256=6kzIHJ04VJMQFYLYNw6wLX0Zy-i_aWDRmWc-zFsiw7Y,437
14
+ remap_badblocks/src/badblocks/_compute_good_ranges.py,sha256=hMNiDurMvtxyt9VxJYTqF_i-8S0_zBLvzKybC6rA9Js,1425
15
+ remap_badblocks/src/badblocks/_find_badblocks.py,sha256=aKgCsUpMj0KDPh_WhAXM5-sxTouVgy61ZS2UyPvcdfQ,2263
16
+ remap_badblocks/src/badblocks/_mapping_generation.py,sha256=M9ENd3fol_C0PA2Pi7AcZ9KRPpUqiVAq8-8n7u_I0Ec,381
17
+ remap_badblocks/src/badblocks/_remap_badblocks.py,sha256=XmdlXHucfW6hHitsO8xDxgBgo01uHmEa601bYVYWYcU,4063
18
+ remap_badblocks/src/badblocks/badblocks.py,sha256=AYuh0VRG6vY7of_fqqTCeoe2METVJ-xy7ZvMZw7VqTI,1046
19
+ remap_badblocks/src/devices/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ remap_badblocks/src/devices/device_config.py,sha256=5ZkCxvObgxGf1zVOgzy8GlEX4BfHp8KlD7XzdkJI4zs,1943
21
+ remap_badblocks/src/devices/devices_config.py,sha256=wxOuLfpAHqEHPTZz3LafqZxISGvQWaBlI0kR0Qhl1fk,10380
22
+ remap_badblocks/src/devices/exceptions.py,sha256=2RHEZAHQj_bl4B5KHGuqURZswHOMV0GelXXVN7EugdY,825
23
+ remap_badblocks/src/remappers/_check_applied_devices.py,sha256=3Wv5TEoKtszKhcyNNL2pzcB6BuYLmQIJ16mlpOqlw34,314
24
+ remap_badblocks/src/remappers/_generate_dm_table.py,sha256=7BJnqc7Ijgd8SDqdgZvEz6gDJeFokf0hDtm5UjDHlgg,961
25
+ remap_badblocks/src/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ remap_badblocks/src/utils/_get_device_info.py,sha256=HSERimNgOpi_0VIc9LYWLjWBnLRISo2na-pcxbA2icU,1479
27
+ remap_badblocks/src/utils/_iterable_bytes_converter.py,sha256=H0PDLANQLTaakNBHMD_ZZ0Js3seRgXZgs1lxFrqrYAw,586
28
+ remap_badblocks/src/utils/_parse_inputs.py,sha256=KggQ80y97LIAolbhS1OsBMVdt-XDXDO3w4glJQPd4Tw,2772
29
+ remap_badblocks/src/utils/_run_command.py,sha256=3EXIPcjx7MFsXGSDDfnS2qBwXfV-98Efj_enfQS1C_Q,2435
30
+ remap_badblocks/src/utils/_sort_devices.py,sha256=f9w3DoMbRdkDjjaoSrz8qnmDzfaOvk2I_mq4-wwS65A,876
31
+ remap_badblocks-0.9.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
32
+ remap_badblocks-0.9.dist-info/METADATA,sha256=AZAzZOzzYCrz2CF8d4BMXvdvNpCEpHQia03_ZBX53ww,3293
33
+ remap_badblocks-0.9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
34
+ remap_badblocks-0.9.dist-info/entry_points.txt,sha256=JmLHnqW16bljetWHJrYYAuP0vBrxfO0QY0mFdaWqOKg,70
35
+ remap_badblocks-0.9.dist-info/top_level.txt,sha256=FqRo65stYjF_GyJQCLGIQN1Tj0YQcaiinI5bvzd0OpY,16
36
+ remap_badblocks-0.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
__init__.py DELETED
File without changes
cli/__init__.py DELETED
File without changes
cli/__main__.py DELETED
@@ -1,239 +0,0 @@
1
- from argparse import ArgumentParser, Namespace
2
- from pathlib import Path
3
- from typing import Callable
4
-
5
- from remap_badblocks.cli.commands import (add, apply, get, remove, update,
6
- version)
7
- from remap_badblocks.src.devices.devices_config import DevicesConfig
8
- from remap_badblocks.src.devices_config_constants import \
9
- DEFAULT_DEVICES_CONFIG_PATH
10
-
11
- argparse = ArgumentParser(
12
- description="Badblocks remapper for block devices.",
13
- )
14
-
15
- argparse.add_argument(
16
- "-P",
17
- "--db-path",
18
- type=Path,
19
- default=DEFAULT_DEVICES_CONFIG_PATH,
20
- help="Path to the devices configuration database.",
21
- )
22
-
23
- subparser = argparse.add_subparsers(
24
- title="Actions",
25
- description="Available actions for the badblocks remapper.",
26
- dest="action",
27
- help="Action to perform",
28
- required=True,
29
- )
30
-
31
- add_new_device_parser = subparser.add_parser(
32
- "add",
33
- help="Add a new device to the database.",
34
- )
35
-
36
- add_new_device_parser.add_argument(
37
- "name", type=str, help="The name of the output device."
38
- )
39
- add_new_device_parser.add_argument(
40
- "--wwn",
41
- type=str,
42
- help="The WWN of the device to add.",
43
- )
44
- add_new_device_parser.add_argument(
45
- "--path",
46
- type=Path,
47
- help="The path to the device to add.",
48
- )
49
- add_new_device_parser.add_argument(
50
- "--depends-on",
51
- type=str,
52
- default=[],
53
- action="append",
54
- help=(
55
- "The name of a device that this device depends on. Can be specified multiple times to add multiple"
56
- " dependencies. This is used to ensure that devices are applied in the correct order."
57
- ),
58
- )
59
- add_new_device_parser.add_argument(
60
- "--depends-on-id",
61
- type=int,
62
- default=[],
63
- action="append",
64
- help=(
65
- "The ID of a device that this device depends on. Can be specified multiple times to add multiple"
66
- " dependencies. This is used to ensure that devices are applied in the correct order."
67
- ),
68
- )
69
- add_new_device_parser.add_argument(
70
- "--logical-sector-range",
71
- type=str,
72
- default="-",
73
- help=(
74
- "Logical sector range to assign to the device. Format: 'start-end', where 'end' can be omitted to mean"
75
- " 'till the end of the device'. Both start and end can be specified as sector numbers or as byte offsets."
76
- " In case of byte offsets, they must be multiples of the sector size. If not specified, the whole device"
77
- " will be used. Note that end is not included in the range."
78
- ),
79
- )
80
-
81
-
82
- get_devices_parser = subparser.add_parser(
83
- "get",
84
- help="Get the existing device(s) in the database.",
85
- )
86
- get_devices_parser.add_argument(
87
- "--id",
88
- type=int,
89
- default=None,
90
- help="The ID of the device to get. If neither --id or --name are provided, all devices will be returned.",
91
- )
92
- get_devices_parser.add_argument(
93
- "--name",
94
- type=str,
95
- default=None,
96
- help="The name of the device to get. If neither --id or --name are provided, all devices will be returned.",
97
- )
98
-
99
- remove_devices_parser = subparser.add_parser(
100
- "remove",
101
- help="Remove an existing device in the database.",
102
- )
103
- remove_devices_parser.add_argument(
104
- "--id",
105
- type=int,
106
- default=None,
107
- help="The ID of the device to remove. If neither --id or --name are provided, an error will be raised.",
108
- )
109
- remove_devices_parser.add_argument(
110
- "--name",
111
- type=str,
112
- default=None,
113
- help="The name of the device to remove. If neither --id or --name are provided, an error will be raised.",
114
- )
115
-
116
- update_mapping_parser = subparser.add_parser(
117
- "update",
118
- help=(
119
- "Update the mapping of a device in the database. More specifically: compute/update the device's badblocks and"
120
- " store them in the database, compute the new mapping of a device; if the mapping has changed, the user will"
121
- " be prompted to confirm the changes."
122
- ),
123
- )
124
- update_mapping_parser.add_argument(
125
- "--id",
126
- type=int,
127
- default=None,
128
- help="The ID of the device to update.",
129
- )
130
- update_mapping_parser.add_argument(
131
- "--name",
132
- type=str,
133
- default=None,
134
- help="The name of the device to update.",
135
- )
136
- update_mapping_parser.add_argument(
137
- "--mode",
138
- type=str,
139
- choices=["read", "write", "skip"],
140
- default="read",
141
- help="Mode for computing badblocks",
142
- )
143
- update_mapping_parser.add_argument(
144
- "--known-badblocks-file",
145
- type=Path,
146
- default=None,
147
- help=(
148
- "Path to file with known badblocks (a sector number for each line) that will be merged to those found so far."
149
- " Take care the sector size is the same as configured into remap_badblocks"
150
- ),
151
- )
152
- update_mapping_parser.add_argument(
153
- "--block-range",
154
- type=str,
155
- help=(
156
- "Block range to check (e.g., 0-1000, or 1573-), omitted start and end means the whole logical range. Note"
157
- " that the end is not included in the range"
158
- ),
159
- default="-",
160
- )
161
- update_mapping_parser.add_argument(
162
- "--output",
163
- type=Path,
164
- default=None,
165
- help="Badblocks are stored internally. If provided, they will also be copied to this file.",
166
- )
167
- update_mapping_parser.add_argument(
168
- "--spare-space",
169
- type=str,
170
- default=None,
171
- help="Number of spare sectors to reserve from the good ranges. Cannot be changed after first time",
172
- )
173
- update_mapping_parser.add_argument(
174
- "--reset-mapping",
175
- action="store_true",
176
- help=(
177
- "If specified, will ignore existing mapping and build a new mapping from scratch."
178
- " DANGER: this might lead to data loss, handle with care"
179
- ),
180
- )
181
-
182
- apply_devices_parser = subparser.add_parser(
183
- "apply",
184
- help="Apply a remapping that's already existing in the database.",
185
- )
186
- apply_devices_parser.add_argument(
187
- "--id",
188
- type=int,
189
- default=None,
190
- help="The ID of the device to apply. If neither --id or --name are provided, all devices will be applied.",
191
- )
192
- apply_devices_parser.add_argument(
193
- "--name",
194
- type=str,
195
- default=None,
196
- help="The name of the device to apply. If neither --id or --name are provided, all devices will be applied.",
197
- )
198
- apply_devices_parser.add_argument(
199
- "--method",
200
- type=str,
201
- default="device-mapper",
202
- choices=["device-mapper"],
203
- help="Method to apply the mapping. Only device-mapper is available",
204
- )
205
-
206
- version_parser = subparser.add_parser("version")
207
-
208
-
209
- def main() -> None:
210
- args = argparse.parse_args()
211
- db_path: Path = args.db_path
212
-
213
- actions: dict[str, Callable[[DevicesConfig, Namespace], None]] = {
214
- "add": add,
215
- "apply": apply,
216
- "get": get,
217
- "remove": remove,
218
- "update": update,
219
- }
220
-
221
- static_actions: dict[str, Callable[[], None]] = {
222
- "version": version,
223
- }
224
-
225
- if args.action in actions:
226
- if db_path.is_dir():
227
- raise ValueError("The provided path must not be a directory.")
228
- devices_config = DevicesConfig(db_path)
229
- actions[args.action](devices_config, args)
230
- elif args.action in static_actions:
231
- static_actions[args.action]()
232
- else:
233
- raise ValueError(
234
- f"Unknown action: {args.action}. Please use one of {tuple(actions.keys()) + tuple(static_actions.keys())}."
235
- )
236
-
237
-
238
- if __name__ == "__main__":
239
- main()
cli/commands/__init__.py DELETED
@@ -1,8 +0,0 @@
1
- from .add import add
2
- from .apply import apply
3
- from .get import get
4
- from .remove import remove
5
- from .update import update
6
- from .version import version
7
-
8
- __all__ = ["add", "apply", "get", "remove", "update", "version"]
cli/commands/add.py DELETED
@@ -1,91 +0,0 @@
1
- import os
2
- from argparse import Namespace
3
- from pathlib import Path
4
- from typing import Optional, TypedDict
5
-
6
- from remap_badblocks.src.devices.devices_config import DevicesConfig
7
- from remap_badblocks.src.utils._get_device_info import (
8
- get_disk_block_size, get_disk_number_of_blocks, resolve_device_name)
9
- from remap_badblocks.src.utils._parse_inputs import \
10
- parse_memory_range_to_sectors
11
-
12
-
13
- class AddArgs(TypedDict):
14
- wwn: Optional[str]
15
- path: Optional[Path]
16
- name: str
17
- depends_on_id: set[int]
18
- depends_on: set[str]
19
- logical_sector_range: str
20
-
21
-
22
- def parse_args(args: Namespace) -> AddArgs:
23
- return {
24
- "wwn": args.wwn,
25
- "path": args.path,
26
- "name": args.name,
27
- "depends_on_id": set(args.depends_on_id),
28
- "depends_on": set(args.depends_on),
29
- "logical_sector_range": args.logical_sector_range.strip(),
30
- }
31
-
32
-
33
- def get_path_from_args(args: AddArgs) -> Path:
34
- wwn = args["wwn"]
35
- path = args["path"]
36
-
37
- if wwn is not None and path is not None:
38
- raise ValueError("Only one of --wwn or --path can be provided, not both.")
39
-
40
- if path is not None:
41
- if isinstance(path, str):
42
- path = Path(path)
43
- return path
44
- elif wwn is not None:
45
- if not wwn.startswith("/dev/disk/by-id/"):
46
- wwn = os.path.join("/dev/disk/by-id/", wwn)
47
- return Path(wwn)
48
- else:
49
- raise ValueError("Either --wwn or --path must be provided.")
50
-
51
-
52
- def get_device_dependencies_from_args(dc: DevicesConfig, args: AddArgs) -> set[int]:
53
- return args["depends_on_id"] | {
54
- dc.get_device(name=dep).id for dep in args["depends_on"] if dep
55
- }
56
-
57
-
58
- def get_logical_block_range_from_args(
59
- args: AddArgs, sector_size: int, total_sectors: int
60
- ) -> tuple[int, int]:
61
- logical_start_block, logical_end_block = parse_memory_range_to_sectors(
62
- args["logical_sector_range"], sector_size
63
- )
64
-
65
- if logical_start_block is None:
66
- logical_start_block = 0
67
- if logical_end_block is None:
68
- logical_end_block = total_sectors
69
-
70
- return logical_start_block, logical_end_block
71
-
72
-
73
- def add(dc: DevicesConfig, _args: Namespace) -> None:
74
- """
75
- Add a new device to the database.
76
- """
77
- args = parse_args(_args)
78
- path: Path = get_path_from_args(args)
79
-
80
- sector_size = get_disk_block_size(resolve_device_name(path))
81
- total_sectors = get_disk_number_of_blocks(resolve_device_name(path))
82
-
83
- dc.add_device(
84
- path=path,
85
- name=args["name"],
86
- sector_size=sector_size,
87
- depends_on=get_device_dependencies_from_args(dc, args),
88
- logical_range=get_logical_block_range_from_args(
89
- args, sector_size, total_sectors
90
- ),
91
- )