-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript_manager.py
More file actions
356 lines (293 loc) · 12.3 KB
/
Copy pathscript_manager.py
File metadata and controls
356 lines (293 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# script_manager.py
"""
Script Manager GUI for HelpIT application.
This module provides a graphical interface to browse and execute scripts
on remote machines using PsExec.
Features:
- List available scripts (.ps1, .bat, .cmd) from a specified directory
- Select and launch scripts on remote machines
- Visual feedback for script execution status
"""
import tkinter as tk
from tkinter import ttk, messagebox
import logging
from pathlib import Path
from constants import *
class ScriptManager:
"""
A GUI window to manage and execute scripts on remote machines.
This class creates a Toplevel window that displays available scripts
from a specified directory and allows the user to execute them on
a remote machine via PsExec.
Attributes:
scripts_path (Path): Directory containing the scripts.
psexec_manager (PsExecManager): Instance to execute remote commands.
current_ip (str): IP address of the target machine.
current_hostname (str): Hostname of the target machine.
window (tk.Toplevel): The main window for the script manager.
script_listbox (tk.Listbox): Widget displaying available scripts.
launch_button (tk.Button): Button to execute the selected script.
"""
# Supported script extensions
SUPPORTED_EXTENSIONS = ['.ps1', '.bat', '.cmd']
def __init__(self, scripts_path, psexec_manager, current_ip, current_hostname):
"""
Initialize the ScriptManager window.
Args:
scripts_path (str or Path): Path to the directory containing scripts.
psexec_manager (PsExecManager): Instance of PsExecManager for remote execution.
current_ip (str): IP address of the remote machine.
current_hostname (str): Hostname of the remote machine.
"""
self.scripts_path = Path(scripts_path)
self.psexec_manager = psexec_manager
self.current_ip = current_ip
self.current_hostname = current_hostname
# List to store script file paths
self.script_files = []
# Create the main window
self.window = tk.Toplevel()
self.window.title(f"Script Manager - {self.current_hostname} ({self.current_ip})")
self.window.geometry(SCRIPT_MANAGER_SIZE)
self.window.resizable(True, True)
# Keep window on top
self.window.attributes('-topmost', True)
# Create the interface
self._create_widgets()
# Load scripts from the directory
self._load_scripts()
# Update the launch button state based on selection
self._update_launch_button_state()
def _create_widgets(self):
"""
Create all GUI widgets for the script manager window.
This method builds the complete user interface including:
- Title label
- Scrollable list of scripts
- Launch and Close buttons
"""
# Main frame with padding
main_frame = ttk.Frame(self.window, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)
# Title label
title_label = ttk.Label(
main_frame,
text=f"Available Scripts",
font=('Arial', 12, 'bold')
)
title_label.pack(pady=(0, 10))
# Info label showing the scripts directory
info_label = ttk.Label(
main_frame,
text=f"Directory: {self.scripts_path}",
font=('Arial', 8),
foreground='gray'
)
info_label.pack(pady=(0, 5))
# Frame for the listbox and scrollbar
list_frame = ttk.Frame(main_frame)
list_frame.pack(fill=tk.BOTH, expand=True, pady=5)
# Scrollbar for the listbox
scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# Listbox to display scripts
self.script_listbox = tk.Listbox(
list_frame,
yscrollcommand=scrollbar.set,
selectmode=tk.SINGLE,
font=('Arial', 10),
height=10
)
self.script_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
# Configure scrollbar
scrollbar.config(command=self.script_listbox.yview)
# Bind selection event to update button state
self.script_listbox.bind('<<ListboxSelect>>', self._on_selection_change)
# Bind double-click to launch script
self.script_listbox.bind('<Double-1>', lambda e: self._launch_script())
# Frame for buttons at the bottom
button_frame = ttk.Frame(main_frame)
button_frame.pack(fill=tk.X, pady=(10, 0))
# Launch button (initially disabled)
self.launch_button = tk.Button(
button_frame,
text="Launch",
bg="#4CAF50", # Green color
fg="white",
width=12,
font=('Arial', 10, 'bold'),
command=self._launch_script,
state=tk.DISABLED # Disabled by default
)
self.launch_button.pack(side=tk.LEFT, padx=5)
# Refresh button
refresh_button = ttk.Button(
button_frame,
text="Refresh",
width=10,
command=self._refresh_scripts
)
refresh_button.pack(side=tk.LEFT, padx=5)
# Close button
close_button = ttk.Button(
button_frame,
text="Close",
width=10,
command=self.close
)
close_button.pack(side=tk.RIGHT, padx=5)
# Status label at the bottom
self.status_var = tk.StringVar(value="Select a script to launch")
status_label = ttk.Label(
main_frame,
textvariable=self.status_var,
font=('Arial', 9),
foreground='gray'
)
status_label.pack(pady=(5, 0))
def _load_scripts(self):
"""
Load script files from the scripts directory.
This method scans the scripts_path directory for files with
supported extensions (.ps1, .bat, .cmd) and populates the listbox.
"""
# Clear the current list
self.script_listbox.delete(0, tk.END)
self.script_files.clear()
# Check if the scripts directory exists
if not self.scripts_path.exists():
logging.warning(f"Scripts directory does not exist: {self.scripts_path}")
self.status_var.set("Scripts directory not found")
return
# Find all script files with supported extensions
try:
for extension in self.SUPPORTED_EXTENSIONS:
# Use glob to find files with the current extension
for script_file in self.scripts_path.glob(f"*{extension}"):
if script_file.is_file():
self.script_files.append(script_file)
# Sort scripts alphabetically by name
self.script_files.sort(key=lambda x: x.name.lower())
# Populate the listbox
for script_file in self.script_files:
# Display the filename with its extension
self.script_listbox.insert(tk.END, script_file.name)
# Update status based on number of scripts found
script_count = len(self.script_files)
if script_count == 0:
self.status_var.set("No scripts found in directory")
logging.info(f"No scripts found in {self.scripts_path}")
else:
self.status_var.set(f"{script_count} script(s) available")
logging.info(f"Loaded {script_count} scripts from {self.scripts_path}")
except Exception as e:
logging.error(f"Error loading scripts: {e}")
self.status_var.set(f"Error loading scripts")
messagebox.showerror("Error", f"Failed to load scripts: {e}", parent=self.window)
def _on_selection_change(self, event=None):
"""
Handle listbox selection change event.
Args:
event: The selection change event (optional).
"""
self._update_launch_button_state()
def _update_launch_button_state(self):
"""
Update the Launch button state based on current selection.
The button is enabled only when a script is selected in the listbox.
"""
# Get current selection
selection = self.script_listbox.curselection()
if selection and len(self.script_files) > 0:
# A script is selected - enable the button
self.launch_button.config(state=tk.NORMAL)
else:
# No selection - disable the button
self.launch_button.config(state=tk.DISABLED)
def _launch_script(self):
"""
Launch the selected script on the remote machine.
This method gets the selected script from the listbox and uses
PsExecManager to execute it on the remote machine.
"""
# Get the current selection
selection = self.script_listbox.curselection()
if not selection:
messagebox.showwarning("Warning", "Please select a script to launch.", parent=self.window)
return
# Get the selected script index
selected_index = selection[0]
# Get the corresponding script file path
script_file = self.script_files[selected_index]
# Confirm execution with the user
confirm = messagebox.askyesno(
"Confirm Execution",
f"Are you sure you want to execute:\n\n"
f"{script_file.name}\n\n"
f"on {self.current_hostname} ({self.current_ip})?",
parent=self.window
)
if not confirm:
return
# Update status to show execution in progress
self.status_var.set(f"Executing {script_file.name}...")
self.launch_button.config(state=tk.DISABLED)
self.window.update()
try:
# Execute the script using PsExecManager
result = self.psexec_manager.run_script(script_file)
if result:
# Script executed successfully
self.status_var.set(f"Script executed successfully")
messagebox.showinfo(
"Success",
f"Script '{script_file.name}' executed successfully on {self.current_hostname}.",
parent=self.window
)
logging.info(f"Script {script_file.name} executed on {self.current_hostname}")
else:
# Script execution failed
self.status_var.set(f"Script execution failed")
messagebox.showerror(
"Error",
f"Failed to execute script '{script_file.name}' on {self.current_hostname}.",
parent=self.window
)
logging.error(f"Failed to execute {script_file.name} on {self.current_hostname}")
except FileNotFoundError as e:
self.status_var.set("Script file not found")
messagebox.showerror("Error", f"Script file not found: {e}", parent=self.window)
logging.error(f"Script file not found: {e}")
except ValueError as e:
self.status_var.set("Unsupported script type")
messagebox.showerror("Error", f"Unsupported script type: {e}", parent=self.window)
logging.error(f"Unsupported script type: {e}")
except Exception as e:
self.status_var.set("Error during execution")
messagebox.showerror("Error", f"Error executing script: {e}", parent=self.window)
logging.error(f"Error executing script {script_file.name}: {e}")
finally:
# Re-enable the launch button
self._update_launch_button_state()
def _refresh_scripts(self):
"""
Refresh the list of available scripts.
This method reloads the scripts from the directory and updates
the listbox display.
"""
self._load_scripts()
self._update_launch_button_state()
logging.info("Script list refreshed")
def close(self):
"""
Close the script manager window.
"""
if self.window and self.window.winfo_exists():
self.window.destroy()
logging.info("ScriptManager window closed")
def show(self):
"""
Show the script manager window and start its event loop.
Note: As a Toplevel window, it shares the mainloop with the parent.
"""
self.window.focus_set()