Skip to content

Commit

Permalink
Added blkdiscard
Browse files Browse the repository at this point in the history
  • Loading branch information
Al3cr1s committed Dec 5, 2024
1 parent 7253f49 commit e91acb1
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 102 deletions.
243 changes: 145 additions & 98 deletions basilico.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ def dispatch_command(self, cmd: str, args: str) -> (Optional[Callable[[str, str]
"sudo_password": self.sudo_password,
"smartctl": self.get_smartctl,
"queued_smartctl": self.queued_get_smartctl,
"queued_badblocks": self.badblocks,
"queued_erase": self.erase,
"queued_cannolo": self.cannolo,
"queued_sleep": self.sleep,
"queued_umount": self.umount,
Expand Down Expand Up @@ -443,12 +443,10 @@ def remove_one_from_queue(self, _cmd: str, queue_id: str):
break
return None

def badblocks(self, _cmd: str, dev: str):
def erase(self, _cmd: str, dev: str):
go_ahead = self._unswap()
if not go_ahead:
return

self._queued_command.notify_start("Running badblocks")
if TEST_MODE:
final_message = ""
for progress in range(0, 100, 10):
Expand All @@ -463,104 +461,153 @@ def badblocks(self, _cmd: str, dev: str):
completed = True
all_ok = False
else:
custom_env = os.environ.copy()
custom_env["LC_ALL"] = "C"

pipe = subprocess.Popen(
(
"sudo",
"-n",
"badblocks",
"-w",
"-s",
"-p",
"0",
"-t",
"0x00",
"-b",
"4096",
dev,
),
stderr=subprocess.PIPE,
env=custom_env,
) # , stdout=subprocess.PIPE)

percent = 0.0
reading_and_comparing = False
errors = -1
deleting = False
buffer = bytearray()
for char in iter(lambda: pipe.stderr.read(1), b""):
if not self._go:
pipe.kill()
pipe.wait()
print(f"Killed badblocks process {self.get_queued_command().id()}")
self._queued_command.notify_finish_with_error("Process terminated by user.")
return
if char == b"":
if pipe.poll() is not None:
break
elif char == b"\b":
if not deleting:
result = buffer.decode("utf-8")
errors_print = "?"

reading_and_comparing = reading_and_comparing or ("Reading and comparing" in result)

# If other messages are printed, ignore them
i = result.index("% done")
if i >= 0:
# /2 due to the 0x00 test + read & compare
percent = float(result[i - 6 : i]) / 2
if reading_and_comparing:
percent += 50
i = result.index("(", i)
if i >= 0:
# errors_str = result[i+1:].split(")", 1)[0]
errors_str = result[i + 1 :].split(" ", 1)[0]
# The errors are read, write and corruption
errors_str = errors_str.split("/")
errors = 0 # badblocks prints the 3 totals every time
for error in errors_str:
errors += int(error)
errors_print = str(errors)
self._queued_command.notify_percentage(percent, f"{errors_print} errors")
buffer.clear()
deleting = True
# elif char == b'\n':
# # Skip the first lines (total number of blocks)
# buffer.clear()
else:
if deleting:
deleting = False
buffer += char

# TODO: was this needed? Why were we doing it twice?
# pipe.wait()
exitcode = pipe.wait()

if errors <= -1:
all_ok = None
errors_print = "an unknown amount of"
elif errors == 0:
all_ok = True
errors_print = "no"
is_ssd = subprocess.run(f"lsblk -o ROTA {dev}", shell=True).stdout.split("\n")[1].strip() == "1"
if is_ssd:
return self.blkdiscard(_cmd, dev)
else:
all_ok = False
errors_print = str(errors)
final_message = f"Finished with {errors_print} errors"
return self.badblocks(_cmd, dev)

def blkdiscard(self, _cmd: str, dev: str):
custom_env = os.environ.copy()
custom_env["LC_ALL"] = "C"
pipe = subprocess.Popen(
(
"sudo",
"-n",
"blkdiscard",
"-f",
dev,
),
stderr=subprocess.PIPE,
env=custom_env,
),
stderr = pipe.stderr.read().decode("utf-8")
exitcode = pipe.wait()
if exitcode == 0:
completed = True
all_ok = True
else:
self._queued_command.notify_error()
self._queued_command.notify_finish_with_error(f"blkdiscard exited with status {exitcode}")
completed = False
all_ok = False

with disks_lock:
update_disks_if_needed(self)
disk_ref = disks[dev]

if exitcode == 0:
# self._queued_command.notify_finish(final_message)
completed = True
# noinspection PyBroadException
try:
disk_ref.update_erase(completed, all_ok)
except Exception as e:
final_message = f"Error during upload. {final_message}"
self._queued_command.notify_error(final_message)
logging.warning(
f"[{self._the_id}] Can't update blkdiscard results of {dev} on tarallo",
exc_info=e,
)
self._queued_command.notify_finish(final_message)

def badblocks(self, _cmd: str, dev: str):
self._queued_command.notify_start("Running badblocks")
custom_env = os.environ.copy()
custom_env["LC_ALL"] = "C"

pipe = subprocess.Popen(
(
"sudo",
"-n",
"badblocks",
"-w",
"-s",
"-p",
"0",
"-t",
"0x00",
"-b",
"4096",
dev,
),
stderr=subprocess.PIPE,
env=custom_env,
) # , stdout=subprocess.PIPE)

percent = 0.0
reading_and_comparing = False
errors = -1
deleting = False
buffer = bytearray()
for char in iter(lambda: pipe.stderr.read(1), b""):
if not self._go:
pipe.kill()
pipe.wait()
print(f"Killed badblocks process {self.get_queued_command().id()}")
self._queued_command.notify_finish_with_error("Process terminated by user.")
return
if char == b"":
if pipe.poll() is not None:
break
elif char == b"\b":
if not deleting:
result = buffer.decode("utf-8")
errors_print = "?"

reading_and_comparing = reading_and_comparing or ("Reading and comparing" in result)

# If other messages are printed, ignore them
i = result.index("% done")
if i >= 0:
# /2 due to the 0x00 test + read & compare
percent = float(result[i - 6 : i]) / 2
if reading_and_comparing:
percent += 50
i = result.index("(", i)
if i >= 0:
# errors_str = result[i+1:].split(")", 1)[0]
errors_str = result[i + 1 :].split(" ", 1)[0]
# The errors are read, write and corruption
errors_str = errors_str.split("/")
errors = 0 # badblocks prints the 3 totals every time
for error in errors_str:
errors += int(error)
errors_print = str(errors)
self._queued_command.notify_percentage(percent, f"{errors_print} errors")
buffer.clear()
deleting = True
# elif char == b'\n':
# # Skip the first lines (total number of blocks)
# buffer.clear()
else:
self._queued_command.notify_error()
final_message += f" and badblocks exited with status {exitcode}"
# self._queued_command.notify_finish(final_message)
completed = False
if deleting:
deleting = False
buffer += char

# TODO: was this needed? Why were we doing it twice?
# pipe.wait()
exitcode = pipe.wait()

if errors <= -1:
all_ok = None
errors_print = "an unknown amount of"
elif errors == 0:
all_ok = True
errors_print = "no"
else:
all_ok = False
errors_print = str(errors)
final_message = f"Finished with {errors_print} errors"

if exitcode == 0:
# self._queued_command.notify_finish(final_message)
completed = True
else:
self._queued_command.notify_error()
final_message += f" and badblocks exited with status {exitcode}"
# self._queued_command.notify_finish(final_message)
completed = False

# print(pipe.stdout.readline().decode('utf-8'))
# print(pipe.stderr.readline().decode('utf-8'))
# print(pipe.stdout.readline().decode('utf-8'))
# print(pipe.stderr.readline().decode('utf-8'))

with disks_lock:
update_disks_if_needed(self)
Expand Down
2 changes: 1 addition & 1 deletion constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
QUEUE_TABLE_PROGRESS = 4

QUEUE_LABELS = {
"queued_badblocks": "Erase",
"queued_erase": "Erase",
"queued_smartctl": "Smart Check",
"smartctl": "Smart Check",
"queued_cannolo": "Load System",
Expand Down
6 changes: 3 additions & 3 deletions pinolo.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ def refresh(self):

def standard_procedure(self):
"""This function send to the server a sequence of commands:
- queued_badblocks
- queued_erase
- queued_smartctl
- queued_cannolo (if the cannolo flag on the dialog is checked)
- queued_sleep
Expand All @@ -401,7 +401,7 @@ def standard_procedure(self):
self.load_system(standard_procedure=True)

def erase(self, standard_procedure=False):
"""This function send to the server a queued_badblocks command.
"""This function send to the server a queued_erase command.
If "std" is True it will skip the confirm dialog."""

rows = self.drivesTableView.selectionModel().selectedRows()
Expand All @@ -415,7 +415,7 @@ def erase(self, standard_procedure=False):
if critical_dialog(message, dialog_type="yes_no") != QMessageBox.Yes:
return
for drive in drives:
self.send_command(f"queued_badblocks {drive.name}")
self.send_command(f"queued_erase {drive.name}")

def smart_check(self):
"""This function send to the server a queued_smartctl command.
Expand Down

0 comments on commit e91acb1

Please sign in to comment.