From e91acb1b32d0b9d78aa55176f98893a9e254a82e Mon Sep 17 00:00:00 2001 From: Al3cr1s <153731375+Al3cr1s@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:46:24 +0100 Subject: [PATCH] Added blkdiscard --- basilico.py | 243 ++++++++++++++++++++++++++++++--------------------- constants.py | 2 +- pinolo.py | 6 +- 3 files changed, 149 insertions(+), 102 deletions(-) diff --git a/basilico.py b/basilico.py index ce6d9fb..0fad695 100755 --- a/basilico.py +++ b/basilico.py @@ -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, @@ -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): @@ -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) diff --git a/constants.py b/constants.py index d2ba363..4a01fb7 100644 --- a/constants.py +++ b/constants.py @@ -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", diff --git a/pinolo.py b/pinolo.py index 32c4202..a184f61 100644 --- a/pinolo.py +++ b/pinolo.py @@ -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 @@ -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() @@ -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.