Skip to content

Commit 7208510

Browse files
authored
Merge branch 'WEEE-Open:master' into master
2 parents b95d397 + 252261e commit 7208510

File tree

5 files changed

+115
-75
lines changed

5 files changed

+115
-75
lines changed

.github/workflows/lint.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Set up Python
1818
uses: actions/setup-python@v1
1919
with:
20-
python-version: 3.8
20+
python-version: 3.12
2121

2222
- name: Install Python dependencies
2323
run: pip install black

basilico.py

+68-17
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import subprocess
44
import stat
55
import os
6+
import sys
67
import time
78
from collections import deque
89
from typing import Optional, Callable, Dict, Set, List
@@ -96,7 +97,7 @@ def dequeue(self, cmd_runner):
9697
next_in_line.start()
9798
else:
9899
if cmd_runner.get_cmd() != "queued_sleep":
99-
cmd_runner.call_hdparm_for_sleep(self._path)
100+
cmd_runner._call_hdparm_for_sleep(self._path)
100101

101102
def get_path(self):
102103
return self._path
@@ -320,6 +321,7 @@ def dispatch_command(self, cmd: str, args: str) -> (Optional[Callable[[str, str]
320321
"queued_badblocks": self.badblocks,
321322
"queued_cannolo": self.cannolo,
322323
"queued_sleep": self.sleep,
324+
"queued_umount": self.umount,
323325
"upload_to_tarallo": self.upload_to_tarallo,
324326
"queued_upload_to_tarallo": self.queued_upload_to_tarallo,
325327
"get_disks": self.get_disks,
@@ -665,6 +667,47 @@ def cannolo(self, _cmd: str, dev_and_iso: str):
665667
else:
666668
self._queued_command.notify_finish()
667669

670+
def umount(self, _cmd: str, dev: str):
671+
self._queued_command.notify_start("Calling umount")
672+
success = self._umount_internal(dev)
673+
self._queued_command.disk.update_mountpoints()
674+
if success:
675+
self._queued_command.notify_finish(f"Disk {dev} umounted")
676+
else:
677+
self._queued_command.notify_finish_with_error(f"umount failed")
678+
679+
def _umount_internal(self, dev):
680+
try:
681+
result = subprocess.run(["lsblk", "-J", dev], capture_output=True, text=True)
682+
683+
if result.returncode != 0:
684+
return False
685+
686+
lsblk_output = json.loads(result.stdout)
687+
688+
partitions_to_unmount = []
689+
blockdevices = lsblk_output.get("blockdevices", [])
690+
for device in blockdevices:
691+
if "children" in device:
692+
for partition in device["children"]:
693+
if "mountpoints" in partition and len(partition["mountpoints"]) > 0:
694+
partitions_to_unmount.append(f"/dev/{partition['name']}")
695+
break
696+
697+
if not partitions_to_unmount:
698+
return True
699+
700+
for partition in partitions_to_unmount:
701+
rc = self._call_shell_command(("sudo", "umount", partition))
702+
703+
if rc != 0:
704+
return False
705+
706+
except Exception as _:
707+
return False
708+
709+
return True
710+
668711
def _unswap(self) -> bool:
669712
if TEST_MODE:
670713
return True
@@ -694,26 +737,14 @@ def _unswap(self) -> bool:
694737

695738
def sleep(self, _cmd: str, dev: str):
696739
self._queued_command.notify_start("Calling hdparm")
697-
exitcode = self.call_hdparm_for_sleep(dev)
740+
exitcode = self._call_hdparm_for_sleep(dev)
698741
if exitcode == 0:
699742
self._queued_command.notify_finish("Good night!")
700743
else:
701744
self._queued_command.notify_finish_with_error(f"hdparm exited with status {str(exitcode)}")
702745

703-
def call_hdparm_for_sleep(self, dev):
704-
if TEST_MODE:
705-
logging.debug(f"Fake putting {dev} to sleep")
706-
return 0
707-
logging.debug(f"[{self._the_id}] Putting {dev} to sleep")
708-
res = subprocess.Popen(
709-
("sudo", "hdparm", "-Y", dev),
710-
stderr=subprocess.DEVNULL,
711-
stdout=subprocess.DEVNULL,
712-
)
713-
exitcode = res.wait()
714-
if exitcode != 0:
715-
logging.warning(f"[{self._the_id}] hdparm for {dev} returned {str(exitcode)}")
716-
return exitcode
746+
def _call_hdparm_for_sleep(self, dev: str):
747+
return self._call_shell_command(("sudo", "hdparm", "-Y", dev))
717748

718749
def get_smartctl(self, cmd: str, args: str):
719750
params = self._get_smartctl(args, False)
@@ -965,6 +996,26 @@ def stop_process(self, cmd: str, args: str):
965996
thread = find_thread_from_pid(args)
966997
thread.stop_asap()
967998

999+
def _call_shell_command(self, command: tuple):
1000+
if TEST_MODE:
1001+
logging.debug(f"Simulating command: {' '.join(command)}")
1002+
return 0
1003+
logging.debug(f"[{self._the_id}] Running command {' '.join(command)}")
1004+
1005+
try:
1006+
res = subprocess.Popen(
1007+
command,
1008+
stderr=subprocess.DEVNULL,
1009+
stdout=subprocess.DEVNULL,
1010+
)
1011+
except FileNotFoundError as _:
1012+
return sys.maxsize
1013+
1014+
exitcode = res.wait()
1015+
if exitcode != 0:
1016+
logging.warning(f"[{self._the_id}] Command '{' '.join(command)}' returned {str(exitcode)}")
1017+
return exitcode
1018+
9681019

9691020
class QueuedCommand:
9701021
def __init__(self, disk: Disk, command_runner: CommandRunner):
@@ -1274,7 +1325,7 @@ def main():
12741325
ip = os.getenv("IP")
12751326
port = os.getenv("PORT")
12761327
global TEST_MODE
1277-
TEST_MODE = bool(os.getenv("TEST_MODE", False))
1328+
TEST_MODE = bool(int(os.getenv("TEST_MODE", False)))
12781329

12791330
if TEST_MODE:
12801331
logging.warning("Test mode is enabled, no destructive actions will be performed")

pinolo.py

+36-34
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def __init__(self, app: QApplication) -> None:
6565
self.settings = QSettings("WEEE-Open", "PESTO")
6666
self.client = ReactorThread(self.host, self.port, self.remoteMode)
6767

68-
" Utilities Widgets "
68+
""" Utilities Widgets """
6969
self.latest_conf()
7070
self.diff_widgets = {}
7171
self.smart_widgets = {}
@@ -97,6 +97,7 @@ def __init__(self, app: QApplication) -> None:
9797
self.sleep_action = QAction("Sleep", self)
9898
self.uploadToTarallo_action = QAction("Upload to TARALLO", self)
9999
self.showSmartData_Action = QAction("Show SMART data", self)
100+
self.umount_action = QAction("Umount disk", self)
100101
self.stop_action = QAction("Stop", self)
101102
self.remove_action = QAction("Remove", self)
102103
self.remove_all_action = QAction("Remove All", self)
@@ -112,19 +113,15 @@ def __init__(self, app: QApplication) -> None:
112113
self.setup()
113114

114115
def setup(self):
115-
"""This method must be called in the __init__ function of the Ui class
116-
to initialize the pinolo session"""
116+
"""This method must be called in __init__ function of Ui class
117+
to initialize pinolo session"""
117118

118119
# Check if the host and port field are set
119120
if self.host is None and self.port is None:
120121
message = "The host and port combination is not set.\nPlease visit the settings section."
121122
warning_dialog(message, dialog_type="ok")
122123

123-
"""
124-
The client try to connect to the BASILICO. If it can't and the client is in remote mode, then
125-
a critical error is shown and the client goes in idle. If the client is in local mode and it cannot reach a
126-
BASILICO server, a new BASILICO process is instantiated.
127-
"""
124+
# Start client thread
128125
self.client = ReactorThread(self.host, self.port, self.remoteMode)
129126
self.client.updateEvent.connect(self.gui_update)
130127
self.client.start()
@@ -197,6 +194,8 @@ def set_items_functions(self):
197194
self.showSmartData_Action.triggered.connect(self.show_smart_data)
198195
self.diskTable.addAction(self.showSmartData_Action)
199196
self.uploadToTarallo_action.setEnabled(False)
197+
self.diskTable.addAction(self.umount_action)
198+
self.umount_action.triggered.connect(self.umount_disk)
200199

201200
self.diskTable.selectionModel().selectionChanged.connect(self.on_table_select)
202201

@@ -381,18 +380,23 @@ def queue_info(self):
381380
info_dialog(message)
382381
self.deselect()
383382

383+
def umount_disk(self):
384+
dialog = warning_dialog(
385+
"Are you really sure you want to umount this disk?\nThis is generally not a good idea, proceed only if you are really sure of what are you doing.",
386+
"yes_no",
387+
)
388+
389+
if dialog == QMessageBox.Yes:
390+
drives = self.get_multiple_drive_selection()
391+
for drive in drives:
392+
self.client.send("queued_umount " + drive)
393+
384394
def get_multiple_drive_selection(self):
385395
"""This method returns a list with the names of the selected drives on disk_table"""
386396
drives = []
387-
selected_items = self.diskTable.selectedItems()
388-
columns = self.diskTable.columnCount()
389-
for idx in range(len(selected_items) // columns):
390-
drives.append(
391-
[
392-
selected_items[idx * columns].text(),
393-
selected_items[(idx * columns) + 1].text(),
394-
]
395-
)
397+
selected_rows = self.diskTable.selectionModel().selectedRows()
398+
for row in selected_rows:
399+
drives.append(row.data())
396400

397401
return drives
398402

@@ -518,9 +522,7 @@ def show_smart_data(self):
518522
else:
519523
self.selected_drive = self.selected_drive.text()
520524
if self.selected_drive in self.smart_results:
521-
self.smart_widgets[self.selected_drive] = SmartWidget(self.selected_drive,
522-
self.smart_results[self.selected_drive]
523-
)
525+
self.smart_widgets[self.selected_drive] = SmartWidget(self.selected_drive, self.smart_results[self.selected_drive])
524526
self.smart_widgets[self.selected_drive].close_signal.connect(self.remove_smart_widget)
525527

526528
except BaseException as exc:
@@ -565,10 +567,10 @@ def cannolo(self, std=False, drives=None):
565567

566568
def upload_to_tarallo_selection(self, std: bool = False):
567569
# TODO: check if it's really working
568-
#for row in self.get_selected_drive_rows():
569-
#if row[1] == "":
570-
# self.upload_to_tarallo(row[0])
571-
#self.selected_drive = self.selected_drive.text();
570+
# for row in self.get_selected_drive_rows():
571+
# if row[1] == "":
572+
# self.upload_to_tarallo(row[0])
573+
# self.selected_drive = self.selected_drive.text();
572574
self.selected_drive = self.diskTable.item(self.diskTable.currentRow(), 0)
573575

574576
if not std:
@@ -581,7 +583,7 @@ def upload_to_tarallo_selection(self, std: bool = False):
581583
return
582584
elif self.diskTable.item(self.diskTable.currentRow(), 1).text() != "":
583585
return
584-
loc, ok = input_dialog("Location");
586+
loc, ok = input_dialog("Location")
585587

586588
# If no location is provided or cancel is selected,
587589
# cancel the operation
@@ -834,7 +836,7 @@ def gui_update(self, cmd: str, params: str):
834836
print(f"GUI: Ignored exception while parsing {cmd}, expected JSON but this isn't: {params}")
835837

836838
match cmd:
837-
case 'queue_status' | 'get_queue':
839+
case "queue_status" | "get_queue":
838840
if cmd == "queue_status":
839841
params = [params]
840842
for param in params:
@@ -891,7 +893,7 @@ def gui_update(self, cmd: str, params: str):
891893
if "text" in param:
892894
status_cell.setToolTip(param["text"])
893895

894-
case 'get_disks':
896+
case "get_disks":
895897
drives = params
896898
if len(drives) <= 0:
897899
self.diskTable.setRowCount(0)
@@ -903,10 +905,10 @@ def gui_update(self, cmd: str, params: str):
903905
self.diskTable.resizeColumnToContents(0)
904906
self.diskTable.resizeColumnToContents(1)
905907

906-
case 'smartctl' | 'queued_smartctl':
908+
case "smartctl" | "queued_smartctl":
907909
self.smart_results[params["disk"]] = {"output": params["output"], "status": params["status"]}
908910

909-
case ' connection_failed':
911+
case " connection_failed":
910912
message = params["reason"]
911913
if not self.remoteMode:
912914
print("GUI: Connection Failed: Local server not running.")
@@ -917,29 +919,29 @@ def gui_update(self, cmd: str, params: str):
917919
message = "Cannot find BASILICO server.\nCheck if it's running in the " "targeted machine."
918920
warning_dialog(message, dialog_type="ok")
919921

920-
case 'connection_lost':
922+
case "connection_lost":
921923
self.statusBar().showMessage(f"⚠ Connection lost. Press the reload button to reconnect.")
922924
self.queueTable.setRowCount(0)
923925
self.diskTable.setRowCount(0)
924926

925-
case 'connection_made':
927+
case "connection_made":
926928
self.statusBar().showMessage(f"Connected to {params['host']}:{params['port']}")
927929

928-
case 'list_iso':
930+
case "list_iso":
929931
self.dialog = CannoloDialog(self.settingsDialog, PATH, params)
930932
if self.manual_cannolo:
931933
self.dialog.update.connect(self.use_cannolo_img)
932934
self.manual_cannolo = False
933935
else:
934936
self.dialog.update.connect(self.settingsDialog.set_default_cannolo)
935937

936-
case 'error':
938+
case "error":
937939
message = f"{params['message']}"
938940
if "command" in params:
939941
message += f":\n{params['command']}"
940942
critical_dialog(message, dialog_type="ok")
941943

942-
case 'error_that_can_be_manually_fixed':
944+
case "error_that_can_be_manually_fixed":
943945
message = params["message"]
944946
warning_dialog(message, dialog_type="ok")
945947

utilities.py

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import re
21
import subprocess
32
import os
43
import datetime

0 commit comments

Comments
 (0)