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.
Files changed (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,183 @@
1
+ import datetime
2
+ import os
3
+ import multiprocessing
4
+ import logging
5
+ import zipfile
6
+ import shutil
7
+
8
+ from .. import filesystem, print_api
9
+ from .. wrappers.loggingw import consts, loggingw
10
+
11
+
12
+ REC_FILE_DATE_TIME_MILLISECONDS_FORMAT: str = f'{consts.DEFAULT_ROTATING_SUFFIXES_FROM_WHEN["S"]}_%f'
13
+ REC_FILE_DATE_TIME_FORMAT: str = f'{consts.DEFAULT_ROTATING_SUFFIXES_FROM_WHEN["S"]}'
14
+ REC_FILE_DATE_FORMAT: str = REC_FILE_DATE_TIME_FORMAT.split('_')[0]
15
+
16
+
17
+ def archive(
18
+ directory_path: str,
19
+ include_root_directory: bool = True,
20
+ ) -> str:
21
+ """
22
+ Function archives the directory.
23
+ :param directory_path: string, full path to the directory.
24
+ :param include_root_directory: boolean, default is 'True'.
25
+ 'True': The root directory will be included in the archive.
26
+ 'False': The root directory will not be included in the archive.
27
+ True is usually the case in most archiving utilities.
28
+ :return: string, full path to the archived file.
29
+ """
30
+
31
+ # This is commonly used and supported by most ZIP utilities.
32
+ compression_method = zipfile.ZIP_DEFLATED
33
+
34
+ archive_path: str = directory_path + '.zip'
35
+ with zipfile.ZipFile(archive_path, 'w', compression_method) as zip_object:
36
+ for root, _, files in os.walk(directory_path):
37
+ for file in files:
38
+ file_path = os.path.join(root, file)
39
+
40
+ # If including the root directory, use the relative path from the parent directory of the root
41
+ if include_root_directory:
42
+ arcname = os.path.relpath(file_path, os.path.dirname(directory_path))
43
+ else:
44
+ arcname = os.path.relpath(file_path, directory_path)
45
+
46
+ zip_object.write(file_path, arcname)
47
+
48
+ return archive_path
49
+
50
+
51
+ def recs_archiver(
52
+ recs_directory: str,
53
+ logging_queue: multiprocessing.Queue,
54
+ logger_name: str,
55
+ finalize_output_queue: multiprocessing.Queue
56
+ ) -> list | None:
57
+ """
58
+ Find recs files in a directory for each day.
59
+ Each day of recordings will have separate archive.
60
+
61
+ :param recs_directory: The directory where recordings are stored.
62
+ :param logging_queue: The queue for logging messages.
63
+ :param logger_name: The name of the logger to use for logging.
64
+ This is the base name that '.rec_packer' will be added to it.
65
+ :param finalize_output_queue: output queue for results/exceptions.
66
+ """
67
+
68
+ logger_name = f"{logger_name}.rec_packer"
69
+
70
+ rec_packer_logger_with_queue_handler: logging.Logger = loggingw.create_logger(
71
+ logger_name=logger_name,
72
+ add_queue_handler=True,
73
+ log_queue=logging_queue)
74
+
75
+ print_api.print_api(
76
+ 'Starting recs archiver process.', color='blue',
77
+ logger=rec_packer_logger_with_queue_handler
78
+ )
79
+
80
+ today_date_string = datetime.datetime.now().strftime(REC_FILE_DATE_FORMAT)
81
+
82
+ # There should not be recording json files in recs root.
83
+ files_in_recs_root: list = filesystem.get_paths_from_directory(
84
+ recs_directory, get_file=True, file_name_check_pattern='*\\.json', recursive=False)
85
+ if files_in_recs_root:
86
+ raise NotImplementedError("The files in recs root directory are not implemented yet.")
87
+
88
+ # Each engine should have its own directory inside recordings. We will find all the directories inside recs folder.
89
+ directory_paths_in_recs: list = filesystem.get_paths_from_directory(
90
+ recs_directory, get_directory=True, recursive=False)
91
+
92
+ file_list_per_directory: list = list()
93
+ for directory_path in directory_paths_in_recs:
94
+ all_recs_files = filesystem.get_paths_from_directory(
95
+ directory_path=directory_path.path,
96
+ get_file=True,
97
+ file_name_check_pattern='*.json',
98
+ datetime_format=REC_FILE_DATE_FORMAT,
99
+ recursive=False
100
+ )
101
+ file_list_per_directory.append((directory_path, all_recs_files))
102
+
103
+
104
+ try:
105
+ archived_files: list = list()
106
+ for directory_path, all_recs_files in file_list_per_directory:
107
+ print_api.print_api(f"Archiving recs files in directory: {directory_path.path}",
108
+ logger=rec_packer_logger_with_queue_handler, color='blue')
109
+ for recs_atomic_path in all_recs_files:
110
+ # We don't need to archive today's files.
111
+ if today_date_string == recs_atomic_path.datetime_string:
112
+ continue
113
+
114
+ target_directory_path: str = f"{directory_path.path}{os.sep}{recs_atomic_path.datetime_string}"
115
+ filesystem.create_directory(target_directory_path)
116
+ filesystem.move_file(
117
+ recs_atomic_path.path, target_directory_path)
118
+
119
+ # Archive directories.
120
+ archive_directories: list = filesystem.get_paths_from_directory(
121
+ directory_path.path, get_directory=True, recursive=False)
122
+
123
+ if not archive_directories:
124
+ print_api.print_api(
125
+ f"No directories to archive in: {directory_path.path}",
126
+ color='blue',
127
+ logger=rec_packer_logger_with_queue_handler
128
+ )
129
+ else:
130
+ total_archived_files: int = 0
131
+ for archive_directory in archive_directories:
132
+ files_to_archive: list = filesystem.get_paths_from_directory(
133
+ directory_path=archive_directory.path, get_file=True, recursive=False)
134
+ total_archived_files += len(files_to_archive)
135
+ archived_file: str = archive(archive_directory.path, include_root_directory=True)
136
+ # Remove the original directory after archiving.
137
+ shutil.rmtree(archive_directory.path, ignore_errors=True)
138
+ archived_files.append(archived_file)
139
+
140
+ print_api.print_api(
141
+ f'Archived: 'f'Directories: {len(archive_directories)} | '
142
+ f'Total Files: {total_archived_files} | In: {directory_path.path}',
143
+ logger=rec_packer_logger_with_queue_handler, color='blue')
144
+ print_api.print_api(f'Archived files: {archived_files}', logger=rec_packer_logger_with_queue_handler)
145
+
146
+ finalize_output_queue.put(None)
147
+
148
+ print_api.print_api(
149
+ 'Finished recs archiver process.', color='blue',
150
+ logger=rec_packer_logger_with_queue_handler
151
+ )
152
+
153
+ return archived_files
154
+ except Exception as e:
155
+ print_api.print_api(
156
+ f"Error while archiving recs files: {e}",
157
+ color='red',
158
+ logger=rec_packer_logger_with_queue_handler
159
+ )
160
+
161
+ finalize_output_queue.put(e)
162
+ return None
163
+
164
+
165
+ def recs_archiver_in_process(
166
+ recs_directory: str,
167
+ logging_queue: multiprocessing.Queue,
168
+ logger_name: str,
169
+ finalize_output_queue: multiprocessing.Queue
170
+ ) -> multiprocessing.Process:
171
+ """
172
+ Archive recs files in a directory for each day in a separate process.
173
+
174
+ :param recs_directory: The directory where recordings are stored.
175
+ :param logging_queue: The queue for logging messages.
176
+ :param logger_name: The name of the logger to use for logging.
177
+ :param finalize_output_queue: output queue for results/exceptions.
178
+ """
179
+
180
+ process = multiprocessing.Process(
181
+ target=recs_archiver, args=(recs_directory, logging_queue, logger_name, finalize_output_queue))
182
+ process.start()
183
+ return process
@@ -1,9 +1,8 @@
1
- import json
2
- # Needed to get the function caller module.
3
1
  import inspect
4
2
 
5
3
  from ..wrappers.loggingw import loggingw
6
- from ..basics import dicts
4
+
5
+ from . import config_static
7
6
 
8
7
 
9
8
  # If the string has several dot characters (".") - return the most right string after the last dot.
@@ -33,12 +32,7 @@ def create_custom_logger():
33
32
  # 'f_globals' is a dictionary of all the global variables of the calling initiated class.
34
33
  class_name = calling_frame.f_globals['__name__']
35
34
  # Get the logger name only.
36
- logger_name = build_module_names(class_name)[0]
35
+ engine_logger_part = build_module_names(class_name)[0]
36
+ logger_name = f'{config_static.MainConfig.LOGGER_NAME}.{engine_logger_part}'
37
37
 
38
38
  return loggingw.get_logger_with_level(logger_name)
39
-
40
-
41
- def get_json(obj):
42
- """ Convert any nested object to json / dict and values to string as is """
43
-
44
- return json.dumps(obj, default=dicts.convert_complex_object_to_dict)
@@ -0,0 +1,82 @@
1
+ import socket
2
+
3
+ import paramiko
4
+
5
+ from .. import package_mains_processor, ssh_remote, config_init
6
+ from ..wrappers.socketw import process_getter
7
+ from ..print_api import print_api
8
+
9
+
10
+ PORT_TO_CMD_FILE: str = 'process_from_port'
11
+ TCP_PORTS_FILE: str = 'get_local_tcp_ports'
12
+
13
+
14
+ def test_ssh_main(config: dict) -> int:
15
+ hosts: list = config['main']['hosts_or_ips']
16
+
17
+ for host in hosts:
18
+ print("-----------------------------------")
19
+ print_api(f"Testing cmd for host: {host}", color='blue')
20
+
21
+ if host in config['main']:
22
+ print("Using host-specific credentials")
23
+ username = config[host]['user']
24
+ password = config[host]['pass']
25
+ else:
26
+ print("Didn't find host-specific credential, using defaults")
27
+ username = config['all_hosts']['user']
28
+ password = config['all_hosts']['pass']
29
+
30
+ ssh_client = ssh_remote.SSHRemote(ip_address=host, username=username, password=password)
31
+
32
+ try:
33
+ ssh_client.connect()
34
+ except socket.gaierror as e:
35
+ if e.errno == 11001:
36
+ print_api(f"Couldn't resolve IP to {host}: {str(e)}\n"
37
+ f"Try providing IP address instead of hostname", color='red')
38
+ continue
39
+ else:
40
+ raise e
41
+ except paramiko.ssh_exception.NoValidConnectionsError as e:
42
+ print_api(f"Couldn't connect to {host}: {str(e)}", color='red')
43
+ continue
44
+
45
+ # Read the TCP ports file to string.
46
+ tcp_ports_package_processor: package_mains_processor.PackageMainsProcessor = package_mains_processor.PackageMainsProcessor(
47
+ script_file_stem=TCP_PORTS_FILE)
48
+ tcp_ports_script_string: str = tcp_ports_package_processor.read_script_file_to_string()
49
+
50
+ # Execute the TCP ports script remotely via SSH to get the list of open TCP ports.
51
+ tcp_ports_output, tcp_ports_error = ssh_client.remote_execution_python(script_string=tcp_ports_script_string)
52
+ if tcp_ports_error:
53
+ print_api(f"Error getting TCP ports from host {host}: {tcp_ports_error}", color='red')
54
+ continue
55
+
56
+ tcp_ports_list: list = tcp_ports_output.strip().splitlines()
57
+ if not tcp_ports_list:
58
+ print_api(f"No TCP ports found on host {host}", color='red')
59
+ continue
60
+
61
+ last_port: int = int(tcp_ports_list[-1])
62
+
63
+ port_to_cmd_package_processor: package_mains_processor.PackageMainsProcessor = package_mains_processor.PackageMainsProcessor(
64
+ script_file_stem=PORT_TO_CMD_FILE)
65
+ get_command_instance = process_getter.GetCommandLine(
66
+ client_ip=host,
67
+ client_port=last_port,
68
+ package_processor=port_to_cmd_package_processor,
69
+ ssh_client=ssh_client)
70
+ process_name = get_command_instance.get_process_name()
71
+ print(f"Process for port {last_port} on host {host}: {process_name}")
72
+
73
+ print("Closing SSH connection")
74
+ ssh_client.close()
75
+
76
+ if not process_name:
77
+ print_api(f"Failed to get process name for port {last_port} on host {host}", color='red')
78
+ continue
79
+
80
+ print_api(f"SSH test success!", color='green')
81
+
82
+ return 0
@@ -2,6 +2,7 @@ import os
2
2
  import datetime
3
3
  import json
4
4
  from typing import Union, Literal
5
+ import argparse
5
6
 
6
7
  from .statistic_analyzer_helper import analyzer_helper, moving_average_helper
7
8
  from .. import filesystem, domains, datetimes, urls
@@ -28,9 +29,9 @@ def analyze(main_file_path: str):
28
29
  summary_path: str = filesystem.check_absolute_path___add_full(config['report_file_path'], script_directory)
29
30
 
30
31
  # Get the content from statistics files.
32
+ log_file_path_pattern: str = f"{config['statistic_files_path']}{os.sep}statistics.csv"
31
33
  statistics_content: list = reading.get_all_log_files_into_list(
32
- config['statistic_files_path'],
33
- file_name_pattern='statistics*.csv',
34
+ log_file_path=log_file_path_pattern,
34
35
  log_type='csv'
35
36
  )
36
37
 
@@ -344,21 +345,33 @@ def analyze(main_file_path: str):
344
345
  # ======================================================================================================================
345
346
 
346
347
 
347
- def deviation_calculator_by_moving_average_main(
348
- statistics_file_directory: str,
349
- moving_average_window_days: int,
350
- top_bottom_deviation_percentage: float,
348
+ def deviation_calculator_by_moving_average(
349
+ statistics_file_directory: str = None,
350
+ statistics_content: dict = None,
351
+ by_type: Literal['host', 'url'] = 'url',
352
+ moving_average_window_days: int = 5,
353
+ top_bottom_deviation_percentage: float = 0.25,
351
354
  get_deviation_for_last_day_only: bool = False,
355
+ get_deviation_for_date: str = None,
352
356
  summary: bool = False,
353
357
  output_file_path: str = None,
354
- output_file_type: Literal['json', 'csv'] = 'json'
358
+ output_file_type: Literal['json', 'csv'] = 'json',
359
+ convert_sizes_lists_and_ma_data_to_string: bool = False,
360
+ skip_total_count_less_than: int = None
355
361
  ) -> Union[list, None]:
356
362
  """
357
363
  This function is the main function for the moving average calculator.
358
364
 
359
- :param statistics_file_directory: string, the directory where 'statistics.csv' file resides.
365
+ :param statistics_file_directory: string, can be either providing the directory where 'statistics.csv' file resides
366
+ or None. If None, the 'statistics_content' must be provided.
367
+ The directory where 'statistics.csv' file resides.
360
368
  Also, all the rotated files like: statistics_2021-01-01.csv, statistics_2021-01-02.csv, etc.
361
369
  These will be analyzed in the order of the date in the file name.
370
+ :param statistics_content: dict, if specified, this will be used instead of reading the files from the directory.
371
+ The dict should be a result of the 'atomicshop.mitm.statistic_analyzer_helper.moving_average_helper.get_all_files_content'.
372
+ :param by_type: string, 'host' or 'url'. The type of the deviation calculation.
373
+ 'host' will calculate the deviation by the host name. Example: maps.google.com, yahoo.com, etc.
374
+ 'url' will calculate the deviation by the URL. Example: maps.google.com/maps, yahoo.com/news, etc.
362
375
  :param moving_average_window_days: integer, the moving average window days.
363
376
  :param top_bottom_deviation_percentage: float, the top bottom deviation percentage. Example: 0.1 for 10%.
364
377
  :param get_deviation_for_last_day_only: bool, if True, only the last day will be analyzed.
@@ -374,10 +387,20 @@ def deviation_calculator_by_moving_average_main(
374
387
  Files 01 to 05 will be used for moving average and the file 06 for deviation.
375
388
  Meaning the average calculated for 2021-01-06 will be compared to the values moving average of 2021-01-01
376
389
  to 2021-01-05.
390
+ :param get_deviation_for_date: string, if specified, only the specified date will be analyzed.
391
+ The date must be in the format of 'YYYY-MM-DD'. Example: '2021-01-06'.
377
392
  :param summary: bool, if True, Only the summary will be generated without all the numbers that were used
378
393
  to calculate the averages and the moving average data.
379
394
  :param output_file_path: string, if None, no file will be written.
380
395
  :param output_file_type: string, the type of the output file. 'json' or 'csv'.
396
+ :param convert_sizes_lists_and_ma_data_to_string: bool, if True, the 'request_sizes', 'response_sizes' and 'ma_data'
397
+ will be converted to string. This is useful when writing to files, so the view will be more readable.
398
+ :param skip_total_count_less_than: integer, if specified, the deviation calculation will be skipped
399
+ if the total count is less than this number.
400
+ This means that if we have moving average window of 7 days, on the 8th day, the deviation will be calculated
401
+ only if the total count of the checked type
402
+ (request average size, response average, request count, response count)
403
+ is greater or equal to this number.
381
404
  -----------------------------
382
405
  :return: the deviation list of dicts.
383
406
 
@@ -399,11 +422,6 @@ def deviation_calculator_by_moving_average_main(
399
422
  sys.exit(main())
400
423
  """
401
424
 
402
- if output_file_type not in ['json', 'csv']:
403
- raise ValueError(f'output_file_type must be "json" or "csv", not [{output_file_type}]')
404
-
405
- statistics_file_path: str = f'{statistics_file_directory}{os.sep}{STATISTICS_FILE_NAME}'
406
-
407
425
  def convert_data_value_to_string(value_key: str, list_index: int) -> None:
408
426
  deviation_list[list_index]['data'][value_key] = json.dumps(deviation_list[list_index]['data'][value_key])
409
427
 
@@ -411,43 +429,53 @@ def deviation_calculator_by_moving_average_main(
411
429
  if value_key in deviation_list[list_index]:
412
430
  deviation_list[list_index][value_key] = json.dumps(deviation_list[list_index][value_key])
413
431
 
432
+ if not statistics_file_directory and not statistics_content:
433
+ raise ValueError('Either [statistics_file_directory] or [statistics_content] must be provided.')
434
+ if statistics_file_directory and statistics_content:
435
+ raise ValueError('Either [statistics_file_directory] or [statistics_content] must be provided, not both.')
436
+
437
+ if output_file_type not in ['json', 'csv']:
438
+ raise ValueError(f'output_file_type must be "json" or "csv", not [{output_file_type}]')
439
+
440
+ if by_type not in ['host', 'url']:
441
+ raise ValueError(f'by_type must be "host" or "url", not [{by_type}]')
442
+
443
+ if get_deviation_for_last_day_only and get_deviation_for_date:
444
+ raise ValueError('Either [get_deviation_for_last_day_only] or [get_deviation_for_date] can be provided, not both.')
445
+
446
+ if statistics_file_directory:
447
+ statistics_file_path: str | None = f'{statistics_file_directory}{os.sep}{STATISTICS_FILE_NAME}'
448
+ else:
449
+ statistics_file_path: str | None = None
450
+
414
451
  deviation_list = moving_average_helper.calculate_moving_average(
415
452
  statistics_file_path,
453
+ statistics_content,
454
+ by_type,
416
455
  moving_average_window_days,
417
456
  top_bottom_deviation_percentage,
418
- get_deviation_for_last_day_only
457
+ get_deviation_for_last_day_only,
458
+ get_deviation_for_date,
459
+ skip_total_count_less_than
419
460
  )
420
461
 
421
462
  if deviation_list:
463
+ if convert_sizes_lists_and_ma_data_to_string:
464
+ for deviation_list_index, deviation in enumerate(deviation_list):
465
+ convert_data_value_to_string('request_sizes', deviation_list_index)
466
+ convert_data_value_to_string('response_sizes', deviation_list_index)
467
+ convert_value_to_string('ma_data', deviation_list_index)
468
+
422
469
  if summary:
423
- summary_deviation_list: list = []
424
470
  for deviation in deviation_list:
425
- value = deviation.get('value', None)
426
- ma_value = deviation.get('ma_value', None)
427
- if not value or not ma_value:
428
- total_entries_averaged = None
429
- else:
430
- total_entries_averaged = deviation['data']['count']
431
-
432
- summary_deviation_list.append({
433
- 'day': deviation['day'],
434
- 'host': deviation['host'],
435
- 'message': deviation['message'],
436
- 'value': deviation.get('value', None),
437
- 'ma_value': deviation.get('ma_value', None),
438
- 'deviation_percentage': deviation.get('deviation_percentage', None),
439
- 'total_entries_averaged': total_entries_averaged
440
- })
441
-
442
- deviation_list = summary_deviation_list
471
+ _ = deviation.pop('check_type')
472
+ _ = deviation.pop('percentage')
473
+ _ = deviation.pop('ma_value_checked')
474
+ _ = deviation.pop('deviation_type')
475
+ _ = deviation.pop('data')
476
+ _ = deviation.pop('ma_data')
443
477
 
444
478
  if output_file_path:
445
- if not summary:
446
- for deviation_list_index, deviation in enumerate(deviation_list):
447
- convert_data_value_to_string('request_sizes', deviation_list_index)
448
- convert_data_value_to_string('response_sizes', deviation_list_index)
449
- convert_value_to_string('ma_data', deviation_list_index)
450
-
451
479
  print_api(f'Deviation Found, saving to file: {output_file_path}', color='blue')
452
480
 
453
481
  if output_file_type == 'csv':
@@ -457,4 +485,72 @@ def deviation_calculator_by_moving_average_main(
457
485
 
458
486
  return deviation_list
459
487
 
460
- return None
488
+ return []
489
+
490
+
491
+ def deviation_calculator_by_moving_average_main():
492
+ def parse_args():
493
+ description_full: str = (
494
+ f'Description: Calculate deviation by moving average.')
495
+
496
+ parser = argparse.ArgumentParser(
497
+ description=description_full)
498
+
499
+ parser.add_argument(
500
+ '-d', '--directory', type=str, required=True,
501
+ help='Provide full path to directory with statistics.csv files.')
502
+ parser.add_argument(
503
+ '-of', '--output_file', type=str, required=True, help='Provide full path to output file.')
504
+ parser.add_argument(
505
+ '-ot', '--output_type', type=str, required=True, help='Provide output type: [json] or [csv].')
506
+ parser.add_argument(
507
+ '-by', '--by_type', type=str, required=True, help='Calculate by [host] or [url].')
508
+ parser.add_argument(
509
+ '-f', '--full_details', action='store_true', required=False,
510
+ help='(OPTIONAL) Output full processing details instead of summary.')
511
+ parser.add_argument(
512
+ '-w', '--window', type=int, required=True, help='Moving average window in days.')
513
+ parser.add_argument(
514
+ '-p', '--percentage', type=float, required=True,
515
+ help='Percentage of deviation from moving average. Example: 0.1 for 10%%.')
516
+ parser.add_argument(
517
+ '-slt', '--skip_total_count_less_than', type=int, required=False,
518
+ help='An integer to skip the deviation calculation if the total count is less than this number.')
519
+
520
+ return parser.parse_args()
521
+
522
+ args = parse_args()
523
+
524
+ if args.output_type == 'csv' and args.full_details:
525
+ print_api("Full details output is not supported for csv files.", color='red')
526
+ return 1
527
+
528
+ if not os.path.isdir(args.directory):
529
+ print_api(f"Directory does not exist: {args.directory}", color='red')
530
+ return 1
531
+
532
+ if not filesystem.get_paths_from_directory(
533
+ directory_path=args.directory, get_file=True, simple_list=True, file_name_check_pattern='*statistics*.csv'):
534
+ print_api(f"No statistics files found in: {args.directory}", color='red')
535
+ return 1
536
+
537
+ if args.full_details:
538
+ summary = False
539
+ convert_sizes_lists_and_ma_data_to_string = True
540
+ else:
541
+ summary = True
542
+ convert_sizes_lists_and_ma_data_to_string = False
543
+
544
+ _ = deviation_calculator_by_moving_average(
545
+ statistics_file_directory=args.directory,
546
+ by_type=args.by_type,
547
+ moving_average_window_days=args.window,
548
+ top_bottom_deviation_percentage=args.percentage,
549
+ summary=summary,
550
+ output_file_path=args.output_file,
551
+ output_file_type=args.output_type,
552
+ convert_sizes_lists_and_ma_data_to_string=convert_sizes_lists_and_ma_data_to_string,
553
+ skip_total_count_less_than=args.skip_total_count_less_than
554
+ )
555
+
556
+ return 0