atomicshop 2.15.11__py3-none-any.whl → 3.10.5__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.
- atomicshop/__init__.py +1 -1
- atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
- atomicshop/a_mains/dns_gateway_setting.py +11 -0
- atomicshop/a_mains/get_local_tcp_ports.py +85 -0
- atomicshop/a_mains/github_wrapper.py +11 -0
- atomicshop/a_mains/install_ca_certificate.py +172 -0
- atomicshop/a_mains/process_from_port.py +119 -0
- atomicshop/a_mains/set_default_dns_gateway.py +90 -0
- atomicshop/a_mains/update_config_toml.py +38 -0
- atomicshop/basics/ansi_escape_codes.py +3 -1
- atomicshop/basics/argparse_template.py +2 -0
- atomicshop/basics/booleans.py +27 -30
- atomicshop/basics/bytes_arrays.py +43 -0
- atomicshop/basics/classes.py +149 -1
- atomicshop/basics/enums.py +2 -2
- atomicshop/basics/exceptions.py +5 -1
- atomicshop/basics/list_of_classes.py +29 -0
- atomicshop/basics/multiprocesses.py +374 -50
- atomicshop/basics/strings.py +72 -3
- atomicshop/basics/threads.py +14 -0
- atomicshop/basics/tracebacks.py +13 -3
- atomicshop/certificates.py +153 -52
- atomicshop/config_init.py +11 -6
- atomicshop/console_user_response.py +7 -14
- atomicshop/consoles.py +9 -0
- atomicshop/datetimes.py +1 -1
- atomicshop/diff_check.py +3 -3
- atomicshop/dns.py +128 -3
- atomicshop/etws/_pywintrace_fix.py +17 -0
- atomicshop/etws/trace.py +40 -42
- atomicshop/etws/traces/trace_dns.py +56 -44
- atomicshop/etws/traces/trace_tcp.py +130 -0
- atomicshop/file_io/csvs.py +27 -5
- atomicshop/file_io/docxs.py +34 -17
- atomicshop/file_io/file_io.py +31 -17
- atomicshop/file_io/jsons.py +49 -0
- atomicshop/file_io/tomls.py +139 -0
- atomicshop/filesystem.py +616 -291
- atomicshop/get_process_list.py +3 -3
- atomicshop/http_parse.py +149 -93
- atomicshop/ip_addresses.py +6 -1
- atomicshop/mitm/centered_settings.py +132 -0
- atomicshop/mitm/config_static.py +207 -0
- atomicshop/mitm/config_toml_editor.py +55 -0
- atomicshop/mitm/connection_thread_worker.py +875 -357
- atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
- atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
- atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
- atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
- atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
- atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
- atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
- atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
- atomicshop/mitm/engines/create_module_template.py +58 -14
- atomicshop/mitm/import_config.py +359 -139
- atomicshop/mitm/initialize_engines.py +160 -80
- atomicshop/mitm/message.py +64 -23
- atomicshop/mitm/mitm_main.py +892 -0
- atomicshop/mitm/recs_files.py +183 -0
- atomicshop/mitm/shared_functions.py +4 -10
- atomicshop/mitm/ssh_tester.py +82 -0
- atomicshop/mitm/statistic_analyzer.py +136 -40
- atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
- atomicshop/monitor/checks/dns.py +1 -1
- atomicshop/networks.py +671 -0
- atomicshop/on_exit.py +39 -9
- atomicshop/package_mains_processor.py +84 -0
- atomicshop/permissions/permissions.py +22 -0
- atomicshop/permissions/ubuntu_permissions.py +239 -0
- atomicshop/permissions/win_permissions.py +33 -0
- atomicshop/print_api.py +24 -42
- atomicshop/process.py +24 -6
- atomicshop/process_poller/process_pool.py +0 -1
- atomicshop/process_poller/simple_process_pool.py +204 -5
- atomicshop/python_file_patcher.py +1 -1
- atomicshop/python_functions.py +27 -75
- atomicshop/speech_recognize.py +8 -0
- atomicshop/ssh_remote.py +158 -172
- atomicshop/system_resource_monitor.py +61 -47
- atomicshop/system_resources.py +8 -8
- atomicshop/tempfiles.py +1 -2
- atomicshop/urls.py +6 -0
- atomicshop/venvs.py +28 -0
- atomicshop/versioning.py +27 -0
- atomicshop/web.py +98 -27
- atomicshop/web_apis/google_custom_search.py +44 -0
- atomicshop/web_apis/google_llm.py +188 -0
- atomicshop/websocket_parse.py +450 -0
- atomicshop/wrappers/certauthw/certauth.py +1 -0
- atomicshop/wrappers/cryptographyw.py +29 -8
- atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
- atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
- atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
- atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
- atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
- atomicshop/wrappers/ctyping/setup_device.py +466 -0
- atomicshop/wrappers/ctyping/win_console.py +39 -0
- atomicshop/wrappers/dockerw/dockerw.py +113 -2
- atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
- atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
- atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
- atomicshop/wrappers/factw/get_file_data.py +12 -5
- atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
- atomicshop/wrappers/githubw.py +537 -54
- atomicshop/wrappers/loggingw/consts.py +1 -1
- atomicshop/wrappers/loggingw/filters.py +23 -0
- atomicshop/wrappers/loggingw/formatters.py +12 -0
- atomicshop/wrappers/loggingw/handlers.py +214 -107
- atomicshop/wrappers/loggingw/loggers.py +19 -0
- atomicshop/wrappers/loggingw/loggingw.py +860 -22
- atomicshop/wrappers/loggingw/reading.py +134 -112
- atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
- atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
- atomicshop/wrappers/netshw.py +271 -0
- atomicshop/wrappers/playwrightw/engine.py +34 -19
- atomicshop/wrappers/playwrightw/infra.py +5 -0
- atomicshop/wrappers/playwrightw/javascript.py +7 -3
- atomicshop/wrappers/playwrightw/keyboard.py +14 -0
- atomicshop/wrappers/playwrightw/scenarios.py +172 -5
- atomicshop/wrappers/playwrightw/waits.py +9 -7
- atomicshop/wrappers/powershell_networking.py +80 -0
- atomicshop/wrappers/psutilw/processes.py +37 -1
- atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
- atomicshop/wrappers/pyopensslw.py +9 -2
- atomicshop/wrappers/pywin32w/cert_store.py +116 -0
- atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
- atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
- atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
- atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
- atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
- atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
- atomicshop/wrappers/socketw/accepter.py +21 -7
- atomicshop/wrappers/socketw/certificator.py +216 -150
- atomicshop/wrappers/socketw/creator.py +190 -50
- atomicshop/wrappers/socketw/dns_server.py +491 -182
- atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
- atomicshop/wrappers/socketw/process_getter.py +86 -0
- atomicshop/wrappers/socketw/receiver.py +144 -102
- atomicshop/wrappers/socketw/sender.py +65 -35
- atomicshop/wrappers/socketw/sni.py +334 -165
- atomicshop/wrappers/socketw/socket_base.py +134 -0
- atomicshop/wrappers/socketw/socket_client.py +137 -95
- atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
- atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
- atomicshop/wrappers/socketw/ssl_base.py +15 -14
- atomicshop/wrappers/socketw/statistics_csv.py +148 -17
- atomicshop/wrappers/sysmonw.py +1 -1
- atomicshop/wrappers/ubuntu_terminal.py +65 -26
- atomicshop/wrappers/win_auditw.py +189 -0
- atomicshop/wrappers/winregw/__init__.py +0 -0
- atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
- atomicshop/wrappers/winregw/winreg_network.py +232 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
- atomicshop-3.10.5.dist-info/RECORD +306 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
- atomicshop/_basics_temp.py +0 -101
- atomicshop/a_installs/win/fibratus.py +0 -9
- atomicshop/a_installs/win/mongodb.py +0 -9
- atomicshop/a_installs/win/pycharm.py +0 -9
- atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
- atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
- atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
- atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
- atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
- atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
- atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
- atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
- atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
- atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
- atomicshop/addons/package_setup/Setup.cmd +0 -7
- atomicshop/archiver/_search_in_zip.py +0 -189
- atomicshop/archiver/archiver.py +0 -34
- atomicshop/archiver/search_in_archive.py +0 -250
- atomicshop/archiver/sevenz_app_w.py +0 -86
- atomicshop/archiver/sevenzs.py +0 -44
- atomicshop/archiver/zips.py +0 -293
- atomicshop/file_types.py +0 -24
- atomicshop/mitm/config_editor.py +0 -37
- atomicshop/mitm/engines/create_module_template_example.py +0 -13
- atomicshop/mitm/initialize_mitm_server.py +0 -268
- atomicshop/pbtkmultifile_argparse.py +0 -88
- atomicshop/permissions.py +0 -151
- atomicshop/script_as_string_processor.py +0 -38
- atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
- atomicshop/ssh_scripts/process_from_port.py +0 -27
- atomicshop/wrappers/_process_wrapper_curl.py +0 -27
- atomicshop/wrappers/_process_wrapper_tar.py +0 -21
- atomicshop/wrappers/dockerw/install_docker.py +0 -209
- atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
- atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
- atomicshop/wrappers/ffmpegw.py +0 -125
- atomicshop/wrappers/fibratusw/install.py +0 -81
- atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
- atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
- atomicshop/wrappers/msiw.py +0 -149
- atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
- atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
- atomicshop/wrappers/psutilw/networks.py +0 -45
- atomicshop/wrappers/pycharmw.py +0 -81
- atomicshop/wrappers/socketw/base.py +0 -59
- atomicshop/wrappers/socketw/get_process.py +0 -107
- atomicshop/wrappers/wslw.py +0 -191
- atomicshop-2.15.11.dist-info/RECORD +0 -302
- /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
- /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
- /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
- /atomicshop/{archiver → permissions}/__init__.py +0 -0
- /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
- /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
- /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
- {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
atomicshop/ssh_remote.py
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import sys
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import shlex
|
|
4
5
|
|
|
5
|
-
from .print_api import print_api
|
|
6
|
-
from .wrappers.loggingw import loggingw
|
|
7
|
-
from .wrappers.socketw import base
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# External Libraries
|
|
11
6
|
try:
|
|
12
7
|
import paramiko
|
|
13
8
|
except ImportError as exception_object:
|
|
14
9
|
print(f"Library missing: {exception_object.name}. Install by executing: pip install paramiko")
|
|
15
10
|
sys.exit()
|
|
16
11
|
|
|
12
|
+
from .print_api import print_api
|
|
13
|
+
from .wrappers.loggingw import loggingw
|
|
14
|
+
from .wrappers.socketw import socket_base
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SSHRemoteWrapperNoPythonFound(Exception):
|
|
18
|
+
"""Raised when no usable Python 3 interpreter found on remote host."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
17
21
|
|
|
18
22
|
class SSHRemote:
|
|
19
23
|
"""
|
|
@@ -87,9 +91,13 @@ class SSHRemote:
|
|
|
87
91
|
sys.exit(main())
|
|
88
92
|
|
|
89
93
|
"""
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
ip_address: str,
|
|
97
|
+
username: str,
|
|
98
|
+
password: str,
|
|
99
|
+
logger: logging.Logger = None
|
|
100
|
+
):
|
|
93
101
|
self.ip_address: str = ip_address
|
|
94
102
|
self.username: str = username
|
|
95
103
|
self.password: str = password
|
|
@@ -97,11 +105,23 @@ class SSHRemote:
|
|
|
97
105
|
# Initializing paramiko SSHClient class
|
|
98
106
|
self.ssh_client = paramiko.SSHClient()
|
|
99
107
|
|
|
100
|
-
|
|
108
|
+
# Variable to store detected python command on remote (python3 / python).
|
|
109
|
+
self.python_cmd: str | None = None
|
|
110
|
+
|
|
111
|
+
if logger:
|
|
112
|
+
# Create child logger for the provided logger with the module's name.
|
|
113
|
+
self.logger: logging.Logger = loggingw.get_logger_with_level(f'{logger.name}.{Path(__file__).stem}')
|
|
114
|
+
else:
|
|
115
|
+
self.logger: logging.Logger = logger
|
|
116
|
+
|
|
117
|
+
def connect(
|
|
118
|
+
self,
|
|
119
|
+
timeout: int = 60
|
|
120
|
+
):
|
|
101
121
|
error: str = str()
|
|
102
122
|
|
|
103
123
|
# Get all local interfaces IPv4 addresses.
|
|
104
|
-
local_interfaces_ipv4 =
|
|
124
|
+
local_interfaces_ipv4 = socket_base.get_local_network_interfaces_ip_address("ipv4", True)
|
|
105
125
|
# Check if the target IP address is in the list of local interfaces.
|
|
106
126
|
if self.ip_address in local_interfaces_ipv4:
|
|
107
127
|
# If it is, we don't need to connect to it via SSH, it means that we want to connect to ourselves.
|
|
@@ -119,75 +139,15 @@ class SSHRemote:
|
|
|
119
139
|
# with description of
|
|
120
140
|
# Server 'address_goes_here' not found in known_hosts
|
|
121
141
|
self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# When port 22 is unreachable on the client.
|
|
126
|
-
except paramiko.ssh_exception.NoValidConnectionsError as e:
|
|
127
|
-
error = str(e)
|
|
128
|
-
# Logging the error also. Since the process name isn't critical, we'll continue script execution.
|
|
129
|
-
print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
130
|
-
pass
|
|
131
|
-
except paramiko.ssh_exception.SSHException as e:
|
|
132
|
-
error = str(e)
|
|
133
|
-
# Logging the error also. Since the process name isn't critical, we'll continue script execution.
|
|
134
|
-
print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
135
|
-
pass
|
|
136
|
-
except ConnectionResetError:
|
|
137
|
-
# Returning the error.
|
|
138
|
-
error = "An existing connection was forcibly closed by the remote host."
|
|
139
|
-
# Logging the error also. Since the process name isn't critical, we'll continue script execution.
|
|
140
|
-
print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
141
|
-
pass
|
|
142
|
-
except TimeoutError:
|
|
143
|
-
# Returning the error.
|
|
144
|
-
error = "Connection timed out."
|
|
145
|
-
# Logging the error also. Since the process name isn't critical, we'll continue script execution.
|
|
146
|
-
print_api(error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
147
|
-
pass
|
|
142
|
+
|
|
143
|
+
# Executing SSH connection to client.
|
|
144
|
+
self.ssh_client.connect(self.ip_address, username=self.username, password=self.password, timeout=timeout)
|
|
148
145
|
|
|
149
146
|
return error
|
|
150
147
|
|
|
151
148
|
def close(self):
|
|
152
149
|
self.ssh_client.close()
|
|
153
150
|
|
|
154
|
-
def exec_command_with_error_handling(self, script_string: str):
|
|
155
|
-
# Defining variables.
|
|
156
|
-
stdin = None
|
|
157
|
-
stdout = None
|
|
158
|
-
stderr = None
|
|
159
|
-
result_exception = None
|
|
160
|
-
|
|
161
|
-
# Don't put debugging break point over the next line in PyCharm. For some reason it gets stuck.
|
|
162
|
-
# Put the point right after that.
|
|
163
|
-
try:
|
|
164
|
-
stdin, stdout, stderr = self.ssh_client.exec_command(command=script_string, timeout=30)
|
|
165
|
-
except AttributeError as function_exception_object:
|
|
166
|
-
if function_exception_object.name == "open_session":
|
|
167
|
-
result_exception = "'SSHRemote().connect' wasn't executed."
|
|
168
|
-
print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
169
|
-
|
|
170
|
-
# Since getting Process name is not the main feature of the server, we can pass the exception
|
|
171
|
-
pass
|
|
172
|
-
else:
|
|
173
|
-
result_exception = f"Couldn't execute script over SSH. Unknown yet exception with 'AttributeError' " \
|
|
174
|
-
f"and name: {function_exception_object.name}"
|
|
175
|
-
print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
176
|
-
# Since getting Process name is not the main feature of the server, we can pass the exception
|
|
177
|
-
pass
|
|
178
|
-
except socket.error:
|
|
179
|
-
result_exception = "Couldn't execute script over SSH. SSH socket closed."
|
|
180
|
-
print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
181
|
-
# Since getting Process name is not the main feature of the server, we can pass the exception
|
|
182
|
-
pass
|
|
183
|
-
except Exception:
|
|
184
|
-
result_exception = "Couldn't execute script over SSH. Unknown yet exception."
|
|
185
|
-
print_api(result_exception, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
186
|
-
# Since getting Process name is not the main feature of the server, we can pass the exception
|
|
187
|
-
pass
|
|
188
|
-
|
|
189
|
-
return stdin, stdout, stderr, result_exception
|
|
190
|
-
|
|
191
151
|
@staticmethod
|
|
192
152
|
def check_console_output_for_errors(console_output_string: str):
|
|
193
153
|
# Defining variables.
|
|
@@ -202,104 +162,132 @@ class SSHRemote:
|
|
|
202
162
|
if "ModuleNotFoundError: No module named" in line:
|
|
203
163
|
function_result = f"Python library is not installed - {line}"
|
|
204
164
|
break
|
|
165
|
+
else:
|
|
166
|
+
function_result = console_output_string
|
|
205
167
|
|
|
206
168
|
return function_result
|
|
207
169
|
|
|
208
|
-
def remote_execution(
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
170
|
+
def remote_execution(
|
|
171
|
+
self,
|
|
172
|
+
command: str,
|
|
173
|
+
script_string: str = None
|
|
174
|
+
) -> tuple[str, str]:
|
|
175
|
+
"""
|
|
176
|
+
Function to execute any command over SSH.
|
|
177
|
+
|
|
178
|
+
:param command: command to execute over SSH.
|
|
179
|
+
:param script_string: string representation of script to execute as input.
|
|
180
|
+
Example:
|
|
181
|
+
command = "python - 56734"
|
|
182
|
+
script_string = "import sys;print(f'sys.argv[0]')"
|
|
183
|
+
|
|
184
|
+
Under ssh in terminal it would execute like this:
|
|
185
|
+
ssh User@HostIpv4
|
|
186
|
+
|
|
187
|
+
python - 56734 << EOF
|
|
188
|
+
import sys;print(f'sys.argv[0]')
|
|
189
|
+
EOF
|
|
190
|
+
|
|
191
|
+
Or as onliner:
|
|
192
|
+
ssh User@HostIpv4 "python - 56734 << EOF import sys;print(f'sys.argv[0]') EOF"
|
|
193
|
+
|
|
194
|
+
or using a specific file path:
|
|
195
|
+
ssh User@HostIpv4 "python - 56734" < /path/to/script.py
|
|
196
|
+
|
|
197
|
+
:return: SSH console output, Error output
|
|
198
|
+
"""
|
|
199
|
+
output_result: str = str()
|
|
200
|
+
error_result: str = str()
|
|
216
201
|
|
|
217
202
|
# Execute the command over SSH remotely.
|
|
218
|
-
stdin, stdout, stderr
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
# And connect again.
|
|
225
|
-
self.connect()
|
|
226
|
-
self.logger.info("Reconnected. Trying to send the command one more time.")
|
|
227
|
-
# Try to execute the command over SSH remotely again.
|
|
228
|
-
stdin, stdout, stderr, exec_exception = self.exec_command_with_error_handling(script_string)
|
|
229
|
-
# If there was an exception again.
|
|
230
|
-
if exec_exception:
|
|
231
|
-
# Populate the function_error variable that will be returned outside.
|
|
232
|
-
function_error = exec_exception
|
|
233
|
-
|
|
234
|
-
# If there was no exception executing the remote command.
|
|
235
|
-
if not function_error:
|
|
236
|
-
# Reading the buffer of stdout.
|
|
237
|
-
output_lines = stdout.readlines()
|
|
238
|
-
# Reading the buffer of stderr.
|
|
239
|
-
function_error = stderr.readlines()
|
|
240
|
-
|
|
241
|
-
# Joining error lines list to string if not empty.
|
|
242
|
-
if function_error:
|
|
243
|
-
function_error = ''.join(function_error)
|
|
244
|
-
# Else, joining output lines to string.
|
|
245
|
-
else:
|
|
246
|
-
output_lines = ''.join(output_lines)
|
|
203
|
+
stdin, stdout, stderr = self.ssh_client.exec_command(command=command, timeout=30)
|
|
204
|
+
|
|
205
|
+
# Writing the script string into stdin buffer.
|
|
206
|
+
if script_string:
|
|
207
|
+
stdin.write(script_string)
|
|
208
|
+
stdin.channel.shutdown_write()
|
|
247
209
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
210
|
+
# Reading the buffer of stdout.
|
|
211
|
+
output_lines: list = stdout.readlines()
|
|
212
|
+
# Reading the buffer of stderr.
|
|
213
|
+
error_lines: list = stderr.readlines()
|
|
252
214
|
|
|
253
|
-
|
|
215
|
+
# Joining error lines list to string if not empty.
|
|
216
|
+
if error_lines:
|
|
217
|
+
error_result: str = ''.join(error_lines)
|
|
218
|
+
# Else, joining output lines to string.
|
|
219
|
+
else:
|
|
220
|
+
output_result = ''.join(output_lines)
|
|
254
221
|
|
|
255
|
-
|
|
222
|
+
# Since they're "file-like" objects we need to close them after we finished using.
|
|
223
|
+
stdin.close()
|
|
224
|
+
stdout.close()
|
|
225
|
+
stderr.close()
|
|
226
|
+
|
|
227
|
+
return output_result, error_result
|
|
228
|
+
|
|
229
|
+
def _detect_remote_python_cmd_name(self) -> str:
|
|
230
|
+
"""
|
|
231
|
+
Try 'python3' then 'python' on the remote, return the one that is Python 3.
|
|
232
|
+
Raises if neither works.
|
|
233
|
+
"""
|
|
234
|
+
for candidate in ("python3", "python"):
|
|
235
|
+
# Use a simple version check that works on both Windows and Linux
|
|
236
|
+
cmd = f'{candidate} -c "import sys; print(sys.version_info[0])"'
|
|
237
|
+
stdin, stdout, stderr = self.ssh_client.exec_command(cmd, timeout=5)
|
|
238
|
+
|
|
239
|
+
out = stdout.read().decode().strip()
|
|
240
|
+
exit_status = stdout.channel.recv_exit_status()
|
|
241
|
+
|
|
242
|
+
if exit_status == 0 and out == "3":
|
|
243
|
+
print_api(f"Detected remote Python 3 interpreter (once per client port): {candidate}", logger=self.logger)
|
|
244
|
+
return candidate
|
|
245
|
+
|
|
246
|
+
raise SSHRemoteWrapperNoPythonFound("No usable Python 3 interpreter found on remote host")
|
|
247
|
+
|
|
248
|
+
def _get_python_cmd(self) -> str:
|
|
249
|
+
if self.python_cmd is None:
|
|
250
|
+
self.python_cmd = self._detect_remote_python_cmd_name()
|
|
251
|
+
return self.python_cmd
|
|
252
|
+
|
|
253
|
+
def remote_execution_python(
|
|
254
|
+
self,
|
|
255
|
+
script_string: str,
|
|
256
|
+
script_arg_values: tuple = None,
|
|
257
|
+
script_kwargs: dict = None,
|
|
258
|
+
):
|
|
256
259
|
"""
|
|
257
260
|
Function to execute python script over SSH.
|
|
258
261
|
|
|
259
|
-
Example:
|
|
260
|
-
network.logger.info("Initializing SSH connection to get the calling process.")
|
|
261
|
-
|
|
262
|
-
# Initializing SSHRemote class.
|
|
263
|
-
ssh_client = SSHRemote(ip_address=client_message.client_ip, username=username, password=password)
|
|
264
|
-
# Making actual SSH Connection to the computer.
|
|
265
|
-
ssh_connection_error = ssh_client.connect()
|
|
266
|
-
# If there's an exception / error during connection.
|
|
267
|
-
if ssh_connection_error:
|
|
268
|
-
# Put the error in the process name value.
|
|
269
|
-
client_message.process_name = ssh_connection_error
|
|
270
|
-
else:
|
|
271
|
-
# If no error, then initialize the variables for python script execution over SSH.
|
|
272
|
-
remote_output = remote_error = None
|
|
273
|
-
|
|
274
|
-
# Put source port variable inside the string script.
|
|
275
|
-
script_string: str = \
|
|
276
|
-
put_variable_into_string_script(ssh_script_port_by_process_string, client_message.source_port)
|
|
277
|
-
|
|
278
|
-
# Execute the python script on remote computer over SSH.
|
|
279
|
-
remote_output, remote_error = ssh_client.remote_execution_python(script_string)
|
|
280
|
-
|
|
281
|
-
# If there was an error during execution, put it in process name.
|
|
282
|
-
if remote_error:
|
|
283
|
-
client_message.process_name = remote_error
|
|
284
|
-
# If there was no error during execution, put the output of the ssh to process name.
|
|
285
|
-
else:
|
|
286
|
-
client_message.process_name = remote_output
|
|
287
|
-
network.logger.info(f"Remote SSH: Client executing Command Line: {client_message.process_name}")
|
|
288
|
-
|
|
289
262
|
:param script_string: string representation of python script.
|
|
263
|
+
:param script_arg_values: values arguments to pass to the script. Example for first argument: 56734
|
|
264
|
+
:param script_kwargs: keyword arguments to pass to the script.
|
|
265
|
+
Example: {'-r': None}
|
|
266
|
+
Interpreted as: -r
|
|
267
|
+
Example: {'-f': 'value'}
|
|
268
|
+
Interpreted as: -f value
|
|
269
|
+
Example: {'--arg': value}
|
|
270
|
+
Interpreted as: --arg value
|
|
271
|
+
|
|
290
272
|
:return: SSH console output, Error output
|
|
291
273
|
"""
|
|
292
274
|
# Defining variables.
|
|
293
|
-
|
|
294
|
-
|
|
275
|
+
error_result: str | None = None
|
|
276
|
+
|
|
277
|
+
python_cmd = self._get_python_cmd()
|
|
278
|
+
command: str = f"{python_cmd} -"
|
|
295
279
|
|
|
296
|
-
|
|
297
|
-
|
|
280
|
+
if script_arg_values:
|
|
281
|
+
for arg in script_arg_values:
|
|
282
|
+
command += " " + shlex.quote(str(arg))
|
|
298
283
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
284
|
+
if script_kwargs:
|
|
285
|
+
for key, value in script_kwargs.items():
|
|
286
|
+
command += f" {shlex.quote(str(key))}"
|
|
287
|
+
if value is not None:
|
|
288
|
+
command += " " + shlex.quote(str(value))
|
|
289
|
+
|
|
290
|
+
remote_output, remote_error = self.remote_execution(command=command, script_string=script_string)
|
|
303
291
|
|
|
304
292
|
# If there was an error during remote execution
|
|
305
293
|
if remote_error:
|
|
@@ -308,37 +296,35 @@ class SSHRemote:
|
|
|
308
296
|
# If the message is known and didn't return empty.
|
|
309
297
|
if console_check:
|
|
310
298
|
# 'execution_error' variable will be that full error.
|
|
311
|
-
|
|
299
|
+
error_result = console_check
|
|
300
|
+
else:
|
|
301
|
+
error_result = remote_error
|
|
312
302
|
|
|
313
|
-
return remote_output,
|
|
303
|
+
return remote_output, error_result
|
|
314
304
|
|
|
315
|
-
def connect_get_client_commandline(
|
|
305
|
+
def connect_get_client_commandline(
|
|
306
|
+
self,
|
|
307
|
+
port: int,
|
|
308
|
+
script_string: str):
|
|
316
309
|
# Defining locals.
|
|
317
|
-
execution_output = None
|
|
318
|
-
execution_error = None
|
|
310
|
+
execution_output: str | None = None
|
|
319
311
|
|
|
320
312
|
# Making actual SSH Connection to the computer.
|
|
321
313
|
execution_error = self.connect()
|
|
322
314
|
# if there was an error, try to connect again.
|
|
323
315
|
if execution_error:
|
|
324
|
-
|
|
316
|
+
print_api("Retrying SSH Connection Initialization.", logger=self.logger, logger_method='info')
|
|
325
317
|
execution_error = self.connect()
|
|
326
318
|
|
|
327
319
|
# If there was still an error, we won't be executing the script. And the error will be passed to
|
|
328
320
|
# 'process_name'.
|
|
329
321
|
if not execution_error:
|
|
330
|
-
|
|
322
|
+
print_api("Executing SSH command to acquire the calling process.", logger=self.logger, logger_method='info')
|
|
331
323
|
|
|
332
|
-
|
|
333
|
-
execution_output, execution_error = self.remote_execution_python(script_string)
|
|
334
|
-
# Basically we don't care much about SSH exceptions. Just log them and pass to record.
|
|
335
|
-
except Exception as function_exception_object:
|
|
336
|
-
execution_error = function_exception_object
|
|
337
|
-
print_api(execution_error, logger=self.logger, logger_method='error', traceback_string=True, oneline=True)
|
|
338
|
-
pass
|
|
324
|
+
execution_output, execution_error = self.remote_execution_python(script_string=script_string, script_arg_values=(str(port),))
|
|
339
325
|
|
|
340
326
|
# Closing SSH connection at this stage.
|
|
341
327
|
self.close()
|
|
342
|
-
|
|
328
|
+
print_api("Acquired. Closed SSH connection.", logger=self.logger, logger_method='info')
|
|
343
329
|
|
|
344
330
|
return execution_output, execution_error
|
|
@@ -2,8 +2,7 @@ from typing import Union
|
|
|
2
2
|
import threading
|
|
3
3
|
import multiprocessing.managers
|
|
4
4
|
|
|
5
|
-
from .
|
|
6
|
-
from . import system_resources
|
|
5
|
+
from . import system_resources, print_api
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
class SystemResourceMonitor:
|
|
@@ -109,67 +108,77 @@ class SystemResourceMonitor:
|
|
|
109
108
|
self.thread: Union[threading.Thread, None] = None
|
|
110
109
|
# Sets the running state of the monitoring process. Needed to stop the monitoring and queue threads.
|
|
111
110
|
self.running: bool = False
|
|
112
|
-
# The shared results dictionary.
|
|
111
|
+
# The shared results' dictionary.
|
|
113
112
|
self.results: dict = {}
|
|
114
113
|
|
|
115
|
-
def start(
|
|
114
|
+
def start(
|
|
115
|
+
self,
|
|
116
|
+
print_kwargs: dict = None,
|
|
117
|
+
thread_as_daemon: bool = True
|
|
118
|
+
):
|
|
116
119
|
"""
|
|
117
120
|
Start the monitoring process.
|
|
118
121
|
:param print_kwargs:
|
|
122
|
+
:param thread_as_daemon: bool, set the thread as daemon. If you're running the monitoring process in the main
|
|
123
|
+
process, set it to True. If you're running the monitoring process in a separate process, set it to False.
|
|
124
|
+
In child processes created by multiprocessing.Process, the thread works differently. You might not
|
|
125
|
+
get the desired result.
|
|
119
126
|
:return:
|
|
120
127
|
"""
|
|
121
128
|
|
|
122
|
-
def run_check_system_resources(
|
|
123
|
-
interval, get_cpu, get_memory, get_disk_io_bytes, get_disk_files_count, get_disk_busy_time,
|
|
124
|
-
get_disk_used_percent, calculate_maximum_changed_disk_io, maximum_disk_io, queue_list, manager_dict):
|
|
125
|
-
"""
|
|
126
|
-
Continuously update the system resources in the shared results dictionary.
|
|
127
|
-
This function runs in a separate process.
|
|
128
|
-
"""
|
|
129
|
-
|
|
130
|
-
while self.running:
|
|
131
|
-
# Get the results of the system resources check function and store them in temporary results dictionary.
|
|
132
|
-
results = system_resources.check_system_resources(
|
|
133
|
-
interval=interval, get_cpu=get_cpu, get_memory=get_memory,
|
|
134
|
-
get_disk_io_bytes=get_disk_io_bytes, get_disk_files_count=get_disk_files_count,
|
|
135
|
-
get_disk_busy_time=get_disk_busy_time, get_disk_used_percent=get_disk_used_percent)
|
|
136
|
-
|
|
137
|
-
if calculate_maximum_changed_disk_io:
|
|
138
|
-
if results['disk_io_read'] > maximum_disk_io['read_bytes_per_sec']:
|
|
139
|
-
maximum_disk_io['read_bytes_per_sec'] = results['disk_io_read']
|
|
140
|
-
if results['disk_io_write'] > maximum_disk_io['write_bytes_per_sec']:
|
|
141
|
-
maximum_disk_io['write_bytes_per_sec'] = results['disk_io_write']
|
|
142
|
-
if results['disk_files_count_read'] > maximum_disk_io['read_files_count_per_sec']:
|
|
143
|
-
maximum_disk_io['read_files_count_per_sec'] = results['disk_files_count_read']
|
|
144
|
-
if results['disk_files_count_write'] > maximum_disk_io['write_files_count_per_sec']:
|
|
145
|
-
maximum_disk_io['write_files_count_per_sec'] = results['disk_files_count_write']
|
|
146
|
-
results['maximum_disk_io'] = maximum_disk_io
|
|
147
|
-
|
|
148
|
-
if queue_list is not None:
|
|
149
|
-
for queue in queue_list:
|
|
150
|
-
queue.put(results)
|
|
151
|
-
|
|
152
|
-
# Update the shared results dictionary with the temporary results dictionary.
|
|
153
|
-
# This is done in separate steps to avoid overwriting the special 'multiprocessing.Manager.dict' object.
|
|
154
|
-
# So we update the shared results dictionary with the temporary results dictionary.
|
|
155
|
-
if manager_dict is not None:
|
|
156
|
-
manager_dict.update(results)
|
|
157
|
-
|
|
158
|
-
self.results = results
|
|
159
|
-
|
|
160
129
|
if print_kwargs is None:
|
|
161
130
|
print_kwargs = {}
|
|
162
131
|
|
|
163
132
|
if self.thread is None:
|
|
164
133
|
self.running = True
|
|
165
|
-
self.thread = threading.Thread(target=run_check_system_resources, args=(
|
|
134
|
+
self.thread = threading.Thread(target=self.run_check_system_resources, args=(
|
|
166
135
|
self.interval, self.get_cpu, self.get_memory, self.get_disk_io_bytes, self.get_disk_files_count,
|
|
167
136
|
self.get_disk_busy_time, self.get_disk_used_percent, self.calculate_maximum_changed_disk_io,
|
|
168
137
|
self.maximum_disk_io, self.queue_list, self.manager_dict))
|
|
169
|
-
self.thread.daemon =
|
|
138
|
+
self.thread.daemon = thread_as_daemon
|
|
170
139
|
self.thread.start()
|
|
171
140
|
else:
|
|
172
|
-
print_api("Monitoring is already running.", color='yellow', **print_kwargs)
|
|
141
|
+
print_api.print_api("Monitoring is already running.", color='yellow', **print_kwargs)
|
|
142
|
+
|
|
143
|
+
def run_check_system_resources(
|
|
144
|
+
self,
|
|
145
|
+
interval, get_cpu, get_memory, get_disk_io_bytes, get_disk_files_count, get_disk_busy_time,
|
|
146
|
+
get_disk_used_percent, calculate_maximum_changed_disk_io, maximum_disk_io, queue_list, manager_dict):
|
|
147
|
+
"""
|
|
148
|
+
Continuously update the system resources in the shared results dictionary.
|
|
149
|
+
This function runs in a separate process.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
while self.running:
|
|
153
|
+
# Get the results of the system resources check function and store them in
|
|
154
|
+
# temporary results' dictionary.
|
|
155
|
+
results = system_resources.check_system_resources(
|
|
156
|
+
interval=interval, get_cpu=get_cpu, get_memory=get_memory,
|
|
157
|
+
get_disk_io_bytes=get_disk_io_bytes, get_disk_files_count=get_disk_files_count,
|
|
158
|
+
get_disk_busy_time=get_disk_busy_time, get_disk_used_percent=get_disk_used_percent)
|
|
159
|
+
|
|
160
|
+
if calculate_maximum_changed_disk_io:
|
|
161
|
+
if results['disk_io_read'] > maximum_disk_io['read_bytes_per_sec']:
|
|
162
|
+
maximum_disk_io['read_bytes_per_sec'] = results['disk_io_read']
|
|
163
|
+
if results['disk_io_write'] > maximum_disk_io['write_bytes_per_sec']:
|
|
164
|
+
maximum_disk_io['write_bytes_per_sec'] = results['disk_io_write']
|
|
165
|
+
if results['disk_files_count_read'] > maximum_disk_io['read_files_count_per_sec']:
|
|
166
|
+
maximum_disk_io['read_files_count_per_sec'] = results['disk_files_count_read']
|
|
167
|
+
if results['disk_files_count_write'] > maximum_disk_io['write_files_count_per_sec']:
|
|
168
|
+
maximum_disk_io['write_files_count_per_sec'] = results['disk_files_count_write']
|
|
169
|
+
results['maximum_disk_io'] = maximum_disk_io
|
|
170
|
+
|
|
171
|
+
if queue_list is not None:
|
|
172
|
+
for queue in queue_list:
|
|
173
|
+
queue.put(results)
|
|
174
|
+
|
|
175
|
+
# Update the shared results dictionary with the temporary results' dictionary.
|
|
176
|
+
# This is done in separate steps to avoid overwriting the special 'multiprocessing.Manager.dict' object.
|
|
177
|
+
# So we update the shared results dictionary with the temporary results' dictionary.
|
|
178
|
+
if manager_dict is not None:
|
|
179
|
+
manager_dict.update(results)
|
|
180
|
+
|
|
181
|
+
self.results = results
|
|
173
182
|
|
|
174
183
|
def get_results(self) -> dict:
|
|
175
184
|
"""
|
|
@@ -204,6 +213,7 @@ def start_monitoring(
|
|
|
204
213
|
calculate_maximum_changed_disk_io: bool = False,
|
|
205
214
|
queue_list: list = None,
|
|
206
215
|
manager_dict: multiprocessing.managers.DictProxy = None, # multiprocessing.Manager().dict()
|
|
216
|
+
get_results_thread_as_daemon: bool = True,
|
|
207
217
|
print_kwargs: dict = None
|
|
208
218
|
):
|
|
209
219
|
"""
|
|
@@ -234,6 +244,10 @@ def start_monitoring(
|
|
|
234
244
|
multiprocessing.Process(
|
|
235
245
|
target=system_resource_monitor.start_monitoring, kwargs={'manager_dict': shared_dict}).start()
|
|
236
246
|
|
|
247
|
+
:param get_results_thread_as_daemon: bool, set the thread as daemon. If you're running the monitoring process in the
|
|
248
|
+
main process, set it to True. If you're running the monitoring process in a separate process, set it to False.
|
|
249
|
+
In child processes created by multiprocessing.Process, the thread works differently.
|
|
250
|
+
You might not get the desired result.
|
|
237
251
|
:param print_kwargs: dict, print kwargs.
|
|
238
252
|
:return:
|
|
239
253
|
"""
|
|
@@ -256,9 +270,9 @@ def start_monitoring(
|
|
|
256
270
|
queue_list=queue_list,
|
|
257
271
|
manager_dict=manager_dict
|
|
258
272
|
)
|
|
259
|
-
SYSTEM_RESOURCES_MONITOR.start()
|
|
273
|
+
SYSTEM_RESOURCES_MONITOR.start(thread_as_daemon=get_results_thread_as_daemon)
|
|
260
274
|
else:
|
|
261
|
-
print_api("System resources monitoring is already running.", color='yellow', **(print_kwargs or {}))
|
|
275
|
+
print_api.print_api("System resources monitoring is already running.", color='yellow', **(print_kwargs or {}))
|
|
262
276
|
|
|
263
277
|
|
|
264
278
|
def stop_monitoring():
|
atomicshop/system_resources.py
CHANGED
|
@@ -5,9 +5,8 @@ import shutil
|
|
|
5
5
|
import threading
|
|
6
6
|
import multiprocessing.managers
|
|
7
7
|
|
|
8
|
-
from .print_api import print_api
|
|
9
8
|
from .wrappers.psutilw import cpus, memories, disks
|
|
10
|
-
from . import system_resource_monitor
|
|
9
|
+
from . import system_resource_monitor, print_api
|
|
11
10
|
|
|
12
11
|
|
|
13
12
|
def check_system_resources(
|
|
@@ -129,13 +128,13 @@ def wait_for_resource_availability(
|
|
|
129
128
|
|
|
130
129
|
if result['cpu_usage'] < cpu_percent_max and result['memory_usage'] < memory_percent_max:
|
|
131
130
|
break
|
|
132
|
-
print_api(
|
|
131
|
+
print_api.print_api(
|
|
133
132
|
f"Waiting for resources to be available... "
|
|
134
133
|
f"CPU: {result['cpu_usage']}%, Memory: {result['memory_usage']}%", color='yellow')
|
|
135
134
|
time.sleep(wait_time) # Wait for 'wait_time' seconds before checking again
|
|
136
135
|
|
|
137
136
|
|
|
138
|
-
def
|
|
137
|
+
def _test_disk_speed_with_monitoring(
|
|
139
138
|
file_settings: list[dict],
|
|
140
139
|
remove_file_after_each_copy: bool = False,
|
|
141
140
|
target_directory=None,
|
|
@@ -144,6 +143,7 @@ def test_disk_speed_with_monitoring(
|
|
|
144
143
|
print_kwargs: dict = None
|
|
145
144
|
):
|
|
146
145
|
"""
|
|
146
|
+
THIS IS NOT TESTED.
|
|
147
147
|
Generates files and performs write and read operations in the specified target directory,
|
|
148
148
|
while monitoring disk I/O speeds in a separate thread. Returns the maximum read and write rates,
|
|
149
149
|
and the total operation time.
|
|
@@ -171,7 +171,7 @@ def test_disk_speed_with_monitoring(
|
|
|
171
171
|
if monitoring:
|
|
172
172
|
system_resource_monitor.start_monitoring(
|
|
173
173
|
interval=1, get_cpu=False, get_memory=False, get_disk_io_bytes=True, get_disk_used_percent=False,
|
|
174
|
-
get_disk_files_count=True, calculate_maximum_changed_disk_io=True
|
|
174
|
+
get_disk_files_count=True, calculate_maximum_changed_disk_io=True)
|
|
175
175
|
|
|
176
176
|
if target_directory is None:
|
|
177
177
|
target_directory = tempfile.mkdtemp()
|
|
@@ -196,7 +196,7 @@ def test_disk_speed_with_monitoring(
|
|
|
196
196
|
shutil.copy(src_file_path, dest_directory)
|
|
197
197
|
|
|
198
198
|
target_file_path = os.path.join(dest_directory, os.path.basename(src_file_path))
|
|
199
|
-
print_api(f"Copied: {target_file_path}", **(print_kwargs or {}))
|
|
199
|
+
print_api.print_api(f"Copied: {target_file_path}", **(print_kwargs or {}))
|
|
200
200
|
|
|
201
201
|
# Measure read speed.
|
|
202
202
|
with open(target_file_path, "rb") as file:
|
|
@@ -212,7 +212,7 @@ def test_disk_speed_with_monitoring(
|
|
|
212
212
|
|
|
213
213
|
overall_end_time = time.time()
|
|
214
214
|
total_execution_time = overall_end_time - overall_start_time
|
|
215
|
-
print_api(f"Total execution time: {total_execution_time}", **(print_kwargs or {}))
|
|
215
|
+
print_api.print_api(f"Total execution time: {total_execution_time}", **(print_kwargs or {}))
|
|
216
216
|
|
|
217
217
|
# Cleanup. Remove all created files and directories.
|
|
218
218
|
shutil.rmtree(source_directory)
|
|
@@ -220,7 +220,7 @@ def test_disk_speed_with_monitoring(
|
|
|
220
220
|
|
|
221
221
|
if monitoring:
|
|
222
222
|
# Stop the I/O monitoring.
|
|
223
|
-
max_io_changes = system_resource_monitor.
|
|
223
|
+
max_io_changes = system_resource_monitor.get_results()['maximum_disk_io']
|
|
224
224
|
system_resource_monitor.stop_monitoring()
|
|
225
225
|
|
|
226
226
|
return total_execution_time, max_io_changes
|
atomicshop/tempfiles.py
CHANGED