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
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
from ctypes import wintypes
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Constants and functions for creating root-enumerated device nodes
|
|
7
|
+
# ---------------------------------------------------------------------------
|
|
8
|
+
# Compatibility shim: wintypes.ULONG_PTR is missing on some builds
|
|
9
|
+
# ---------------------------------------------------------------------------
|
|
10
|
+
if not hasattr(wintypes, "ULONG_PTR"):
|
|
11
|
+
if ctypes.sizeof(ctypes.c_void_p) == 8: # 64-bit Python
|
|
12
|
+
wintypes.ULONG_PTR = ctypes.c_uint64
|
|
13
|
+
else: # 32-bit Python
|
|
14
|
+
wintypes.ULONG_PTR = ctypes.c_uint32
|
|
15
|
+
|
|
16
|
+
# ------------------------------------------------------------------
|
|
17
|
+
# SetupDi* “device‑registry property” indices (SPDRP_…)
|
|
18
|
+
# From Microsoft’s setupapi.h – keep them as ints.
|
|
19
|
+
# ------------------------------------------------------------------
|
|
20
|
+
SPDRP_DEVICEDESC = 0 # REG_SZ – Device description (friendly name)
|
|
21
|
+
SPDRP_HARDWAREID = 1 # REG_MULTI_SZ – Hardware‑ID list
|
|
22
|
+
SPDRP_COMPATIBLEIDS = 2 # REG_MULTI_SZ – Compatible‑ID list
|
|
23
|
+
SPDRP_SERVICE = 4 # REG_SZ – Service/miniport to load
|
|
24
|
+
SPDRP_CLASS = 7 # REG_SZ – Class name (e.g. "Net")
|
|
25
|
+
SPDRP_CLASSGUID = 8 # REG_SZ – Class GUID in string form
|
|
26
|
+
|
|
27
|
+
# newdev.h (Windows SDK) / SetupAPI
|
|
28
|
+
DIF_REGISTERDEVICE = 0x00000019
|
|
29
|
+
DIF_REMOVE = 0x00000005
|
|
30
|
+
DICD_GENERATE_ID = 0x00000001
|
|
31
|
+
INSTALLFLAG_FORCE = 0x00000001 # install even if “better” driver exists
|
|
32
|
+
INSTALLFLAG_READONLY = 0x00000002 # don’t write driver to driver store
|
|
33
|
+
INSTALLFLAG_NONINTERACTIVE = 0x00000004 # never display UI (silent mode)
|
|
34
|
+
|
|
35
|
+
DIGCF_PRESENT = 0x00000002
|
|
36
|
+
ERROR_NO_MORE_ITEMS = 259
|
|
37
|
+
|
|
38
|
+
setupapi = ctypes.WinDLL("setupapi", use_last_error=True)
|
|
39
|
+
newdev = ctypes.WinDLL("newdev", use_last_error=True)
|
|
40
|
+
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Structures & prototypes
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
class SP_DEVINFO_DATA(ctypes.Structure):
|
|
45
|
+
_fields_ = [
|
|
46
|
+
("cbSize", wintypes.DWORD),
|
|
47
|
+
("ClassGuid", ctypes.c_byte * 16),
|
|
48
|
+
("DevInst", wintypes.DWORD),
|
|
49
|
+
("Reserved", wintypes.ULONG_PTR),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
# --- creation helpers ------------------------------------------------------
|
|
53
|
+
SetupDiCreateDeviceInfoList = setupapi.SetupDiCreateDeviceInfoList
|
|
54
|
+
SetupDiCreateDeviceInfoList.argtypes = [ctypes.POINTER(ctypes.c_byte * 16), wintypes.HWND]
|
|
55
|
+
SetupDiCreateDeviceInfoList.restype = wintypes.HANDLE
|
|
56
|
+
|
|
57
|
+
SetupDiCreateDeviceInfoW = setupapi.SetupDiCreateDeviceInfoW
|
|
58
|
+
SetupDiCreateDeviceInfoW.argtypes = [
|
|
59
|
+
wintypes.HANDLE, wintypes.LPCWSTR,
|
|
60
|
+
ctypes.POINTER(ctypes.c_byte * 16),
|
|
61
|
+
wintypes.LPCWSTR, wintypes.HWND, wintypes.DWORD,
|
|
62
|
+
ctypes.POINTER(SP_DEVINFO_DATA)
|
|
63
|
+
]
|
|
64
|
+
SetupDiCreateDeviceInfoW.restype = wintypes.BOOL
|
|
65
|
+
|
|
66
|
+
SetupDiSetDeviceRegistryPropertyW = setupapi.SetupDiSetDeviceRegistryPropertyW
|
|
67
|
+
SetupDiSetDeviceRegistryPropertyW.argtypes = [
|
|
68
|
+
wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA), wintypes.DWORD,
|
|
69
|
+
wintypes.LPBYTE, wintypes.DWORD
|
|
70
|
+
]
|
|
71
|
+
SetupDiSetDeviceRegistryPropertyW.restype = wintypes.BOOL
|
|
72
|
+
|
|
73
|
+
SetupDiCallClassInstaller = setupapi.SetupDiCallClassInstaller
|
|
74
|
+
SetupDiCallClassInstaller.argtypes = [
|
|
75
|
+
wintypes.DWORD, wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA)
|
|
76
|
+
]
|
|
77
|
+
SetupDiCallClassInstaller.restype = wintypes.BOOL
|
|
78
|
+
|
|
79
|
+
# --- enumeration / removal -------------------------------------------------
|
|
80
|
+
SetupDiGetClassDevsW = setupapi.SetupDiGetClassDevsW
|
|
81
|
+
SetupDiGetClassDevsW.argtypes = [ctypes.POINTER(ctypes.c_byte * 16),
|
|
82
|
+
wintypes.LPCWSTR, wintypes.HWND, wintypes.DWORD]
|
|
83
|
+
SetupDiGetClassDevsW.restype = wintypes.HANDLE
|
|
84
|
+
|
|
85
|
+
SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
|
|
86
|
+
SetupDiEnumDeviceInfo.argtypes = [wintypes.HANDLE, wintypes.DWORD,
|
|
87
|
+
ctypes.POINTER(SP_DEVINFO_DATA)]
|
|
88
|
+
SetupDiEnumDeviceInfo.restype = wintypes.BOOL
|
|
89
|
+
|
|
90
|
+
SetupDiGetDeviceRegistryPropertyW = setupapi.SetupDiGetDeviceRegistryPropertyW
|
|
91
|
+
SetupDiGetDeviceRegistryPropertyW.argtypes = [
|
|
92
|
+
wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA), wintypes.DWORD,
|
|
93
|
+
ctypes.POINTER(wintypes.DWORD), wintypes.PBYTE, wintypes.DWORD,
|
|
94
|
+
ctypes.POINTER(wintypes.DWORD)
|
|
95
|
+
]
|
|
96
|
+
SetupDiGetDeviceRegistryPropertyW.restype = wintypes.BOOL
|
|
97
|
+
|
|
98
|
+
SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
|
|
99
|
+
SetupDiDestroyDeviceInfoList.argtypes = [wintypes.HANDLE]
|
|
100
|
+
SetupDiDestroyDeviceInfoList.restype = wintypes.BOOL
|
|
101
|
+
|
|
102
|
+
UpdateDriverForPlugAndPlayDevicesW = newdev.UpdateDriverForPlugAndPlayDevicesW
|
|
103
|
+
UpdateDriverForPlugAndPlayDevicesW.argtypes = [
|
|
104
|
+
wintypes.HWND, wintypes.LPCWSTR, wintypes.LPCWSTR,
|
|
105
|
+
wintypes.DWORD, ctypes.POINTER(wintypes.BOOL)
|
|
106
|
+
]
|
|
107
|
+
UpdateDriverForPlugAndPlayDevicesW.restype = wintypes.BOOL
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# 1. Create a root-enumerated devnode (idempotent)
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
def create_root_enumerated_devnode(
|
|
114
|
+
class_guid: str,
|
|
115
|
+
friendly_name: str, # what shows in Device Manager
|
|
116
|
+
hardware_ids: "list[str] | str",
|
|
117
|
+
compatible_ids: "list[str] | str | None" = None,
|
|
118
|
+
devdesc_override: str | None = None,
|
|
119
|
+
create_flags: int = DICD_GENERATE_ID,
|
|
120
|
+
existing_ok: bool = True,
|
|
121
|
+
) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Programmatically create a *root‑enumerated* device node, set its
|
|
124
|
+
Hardware‑ID (and optional Compatible‑ID) list, then ask Plug and Play
|
|
125
|
+
to register/ install whatever driver matches those IDs.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
class_guid : string representation of a GUID.
|
|
130
|
+
Device‑class GUID (e.g. GUID_DEVCLASS_NET, GUID_DEVCLASS_MEDIA …).
|
|
131
|
+
Example:
|
|
132
|
+
class_guid="{4d36e972-e325-11ce-bfc1-08002be10318}"
|
|
133
|
+
This is the GUID for network adapters.
|
|
134
|
+
|
|
135
|
+
friendly_name : str
|
|
136
|
+
Initial instance name placed in the registry (DeviceDesc).
|
|
137
|
+
Also, will be shown in Device Manager.
|
|
138
|
+
|
|
139
|
+
hardware_ids : str | list[str]
|
|
140
|
+
One or more hardware IDs (MULTI_SZ). The *first* one is the key
|
|
141
|
+
identifier PnP uses when selecting an INF.
|
|
142
|
+
|
|
143
|
+
compatible_ids : str | list[str] | None
|
|
144
|
+
Optional Compatible‑ID list (another MULTI_SZ, lower priority).
|
|
145
|
+
|
|
146
|
+
devdesc_override : str | None
|
|
147
|
+
If supplied, written to SPDRP_DEVICEDESC (rarely necessary because
|
|
148
|
+
the INF’s own DeviceDesc usually replaces it).
|
|
149
|
+
|
|
150
|
+
create_flags : int
|
|
151
|
+
Flags for SetupDiCreateDeviceInfoW. Default is DICD_GENERATE_ID.
|
|
152
|
+
|
|
153
|
+
existing_ok : bool
|
|
154
|
+
If True, silently succeed when the devnode already exists.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
class_guid_bytes = uuid.UUID(class_guid).bytes_le
|
|
158
|
+
class_guid_object = (ctypes.c_byte * 16).from_buffer_copy(class_guid_bytes)
|
|
159
|
+
|
|
160
|
+
# --- 1. Create a temporary empty device‑info set -------------------
|
|
161
|
+
hdi = SetupDiCreateDeviceInfoList(class_guid_object, None) # Open a new, empty set
|
|
162
|
+
if hdi == wintypes.HANDLE(-1).value: # INVALID_HANDLE_VALUE?
|
|
163
|
+
raise ctypes.WinError(ctypes.get_last_error()) # Bail out on failure
|
|
164
|
+
|
|
165
|
+
# Prepare the SP_DEVINFO_DATA structure -----------------------------
|
|
166
|
+
devinfo = SP_DEVINFO_DATA() # Zero‑initialised struct
|
|
167
|
+
devinfo.cbSize = ctypes.sizeof(devinfo) # Must set cbSize field
|
|
168
|
+
|
|
169
|
+
# --- 2. Create (or open) the devnode itself ------------------------
|
|
170
|
+
if not SetupDiCreateDeviceInfoW(
|
|
171
|
+
hdi, # Info‑set handle
|
|
172
|
+
friendly_name, # Instance name
|
|
173
|
+
class_guid_object, # Class GUID
|
|
174
|
+
None, None, # (Description, parent window)
|
|
175
|
+
create_flags, # e.g. DICD_GENERATE_ID
|
|
176
|
+
ctypes.byref(devinfo) # Receives devinfo data
|
|
177
|
+
):
|
|
178
|
+
err = ctypes.get_last_error() # Capture error now
|
|
179
|
+
SetupDiDestroyDeviceInfoList(hdi) # Clean up handle
|
|
180
|
+
if not (existing_ok and err == 0xE0000217): # ERROR_DEVINST_ALREADY_EXISTS
|
|
181
|
+
raise ctypes.WinError(err) # Re‑raise unless allowed
|
|
182
|
+
|
|
183
|
+
# --- 3. Build MULTI_SZ buffers and write registry properties -------
|
|
184
|
+
def _multisz(lst_or_str): # Helper → MULTI_SZ buffer
|
|
185
|
+
buf = lst_or_str if isinstance(lst_or_str, str) else "\0".join(lst_or_str)
|
|
186
|
+
return ctypes.create_unicode_buffer(buf + "\0") # Extra trailing NUL
|
|
187
|
+
|
|
188
|
+
# Hardware‑ID list (required) ---------------------------
|
|
189
|
+
hwid = _multisz(hardware_ids) # Build MULTI_SZ buffer
|
|
190
|
+
if not SetupDiSetDeviceRegistryPropertyW(
|
|
191
|
+
hdi, ctypes.byref(devinfo), SPDRP_HARDWAREID, # Property to set
|
|
192
|
+
ctypes.cast(hwid, wintypes.LPBYTE), # Cast to LPBYTE
|
|
193
|
+
(len(hwid) + 1) * ctypes.sizeof(ctypes.c_wchar) # Size in bytes
|
|
194
|
+
):
|
|
195
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
196
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
197
|
+
|
|
198
|
+
# Compatible‑ID list (optional) -------------------------
|
|
199
|
+
if compatible_ids:
|
|
200
|
+
cid = _multisz(compatible_ids) # Build MULTI_SZ buffer
|
|
201
|
+
if not SetupDiSetDeviceRegistryPropertyW(
|
|
202
|
+
hdi, ctypes.byref(devinfo), SPDRP_COMPATIBLEIDS,
|
|
203
|
+
ctypes.cast(cid, wintypes.LPBYTE),
|
|
204
|
+
(len(cid) + 1) * ctypes.sizeof(ctypes.c_wchar)
|
|
205
|
+
):
|
|
206
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
207
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
208
|
+
|
|
209
|
+
# DeviceDesc override (optional) -----------------------
|
|
210
|
+
if devdesc_override:
|
|
211
|
+
desc = ctypes.create_unicode_buffer(devdesc_override + "\0")
|
|
212
|
+
if not SetupDiSetDeviceRegistryPropertyW(
|
|
213
|
+
hdi, ctypes.byref(devinfo), SPDRP_DEVICEDESC,
|
|
214
|
+
ctypes.cast(desc, wintypes.LPBYTE),
|
|
215
|
+
(len(desc) + 1) * ctypes.sizeof(ctypes.c_wchar)
|
|
216
|
+
):
|
|
217
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
218
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
219
|
+
|
|
220
|
+
# --- 4. Hand the devnode to the class installer --------------------
|
|
221
|
+
if not SetupDiCallClassInstaller(
|
|
222
|
+
DIF_REGISTERDEVICE, # “Install this device”
|
|
223
|
+
hdi, ctypes.byref(devinfo)
|
|
224
|
+
):
|
|
225
|
+
err = ctypes.get_last_error()
|
|
226
|
+
# ERROR_DI_DO_DEFAULT means “already registered / nothing to do”
|
|
227
|
+
if not (existing_ok and err == 0xE000020E):
|
|
228
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
229
|
+
raise ctypes.WinError(err)
|
|
230
|
+
|
|
231
|
+
# --- 5. Final cleanup ----------------------------------------------
|
|
232
|
+
SetupDiDestroyDeviceInfoList(hdi) # Always release handle
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
# ---------------------------------------------------------------------------
|
|
236
|
+
# 2. Bind driver from netloop.inf (idempotent)
|
|
237
|
+
# ---------------------------------------------------------------------------
|
|
238
|
+
def update_driver_for_hwids(
|
|
239
|
+
hardware_ids: "str | list[str]",
|
|
240
|
+
inf_path: str,
|
|
241
|
+
force_install: bool = False,
|
|
242
|
+
quiet: bool = True,
|
|
243
|
+
existing_ok: bool = True,
|
|
244
|
+
) -> bool:
|
|
245
|
+
"""
|
|
246
|
+
Install / update the driver in *inf_path* for every present device whose first
|
|
247
|
+
Hardware‑ID matches *hardware_ids*.
|
|
248
|
+
|
|
249
|
+
Parameters
|
|
250
|
+
----------
|
|
251
|
+
hardware_ids : str | list[str]
|
|
252
|
+
Single Hardware‑ID string or a list of IDs. Each is passed separately to
|
|
253
|
+
UpdateDriverForPlugAndPlayDevicesW.
|
|
254
|
+
|
|
255
|
+
inf_path : str
|
|
256
|
+
Full path to the target driver’s .INF file (must already be accessible or
|
|
257
|
+
pre‑staged).
|
|
258
|
+
|
|
259
|
+
force_install : bool, default False
|
|
260
|
+
If True the function sets INSTALLFLAG_FORCE so the specified INF will be
|
|
261
|
+
applied even when Windows thinks a “better” driver is already installed.
|
|
262
|
+
|
|
263
|
+
quiet : bool, default True
|
|
264
|
+
If True the installation runs without UI (parent window = NULL). Set
|
|
265
|
+
False if you want progress dialogs.
|
|
266
|
+
|
|
267
|
+
existing_ok : bool, default True
|
|
268
|
+
When *False*, a return code of ERROR_NO_MORE_ITEMS (no devices found) or
|
|
269
|
+
ERROR_DI_DO_DEFAULT (“already using this driver”) is treated as an error.
|
|
270
|
+
|
|
271
|
+
Returns
|
|
272
|
+
-------
|
|
273
|
+
bool
|
|
274
|
+
True → Windows signalled that a reboot is required.
|
|
275
|
+
False → No reboot required.
|
|
276
|
+
|
|
277
|
+
=====================================================================
|
|
278
|
+
|
|
279
|
+
Examples
|
|
280
|
+
--------
|
|
281
|
+
1. Force‑install the Microsoft KM‑Test Loopback driver that ships with
|
|
282
|
+
Windows (the same scenario as the original hard‑coded function)::
|
|
283
|
+
|
|
284
|
+
reboot_needed = update_driver_for_hwids(
|
|
285
|
+
hardware_ids="*ROOT\\NET\\0000",
|
|
286
|
+
inf_path=r"C:\\Windows\\INF\\netloop.inf",
|
|
287
|
+
force_install=True
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
2. Install an Intel i219‑V NIC driver only if Windows agrees it is the best
|
|
291
|
+
match (no force flag) for either of two possible PCI IDs::
|
|
292
|
+
|
|
293
|
+
reboot_needed = update_driver_for_hwids(
|
|
294
|
+
hardware_ids=[
|
|
295
|
+
"PCI\\VEN_8086&DEV_15B8",
|
|
296
|
+
"PCI\\VEN_8086&DEV_15BB"
|
|
297
|
+
],
|
|
298
|
+
inf_path=r"D:\\Drivers\\PRO1000\\e1r.inf"
|
|
299
|
+
)
|
|
300
|
+
"""
|
|
301
|
+
# Normalise to a Python list for uniform processing
|
|
302
|
+
ids: list[str] = [hardware_ids] if isinstance(hardware_ids, str) else list(hardware_ids)
|
|
303
|
+
|
|
304
|
+
# Build the flag word sent to UpdateDriverForPlugAndPlayDevicesW
|
|
305
|
+
flags = INSTALLFLAG_FORCE if force_install else 0
|
|
306
|
+
if quiet:
|
|
307
|
+
flags |= INSTALLFLAG_NONINTERACTIVE
|
|
308
|
+
|
|
309
|
+
any_reboot = False # track whether *any* call needs reboot
|
|
310
|
+
for hid in ids:
|
|
311
|
+
reboot = wintypes.BOOL(False)
|
|
312
|
+
ok = UpdateDriverForPlugAndPlayDevicesW(
|
|
313
|
+
None, # hwndParent → silent (we add INSTALLFLAG_NONINTERACTIVE)
|
|
314
|
+
hid, # first Hardware‑ID to match
|
|
315
|
+
inf_path, # target driver INF
|
|
316
|
+
flags, # INSTALLFLAG_* bitmask
|
|
317
|
+
ctypes.byref(reboot) # tells us if reboot required
|
|
318
|
+
)
|
|
319
|
+
if not ok:
|
|
320
|
+
err = ctypes.get_last_error()
|
|
321
|
+
# ERROR_NO_MORE_ITEMS (0xE000020B): no matching devices present
|
|
322
|
+
# ERROR_DI_DO_DEFAULT (0xE000020E): already using this driver
|
|
323
|
+
benign = {0xE000020B, 0xE000020E}
|
|
324
|
+
if not (existing_ok and err in benign):
|
|
325
|
+
raise ctypes.WinError(err)
|
|
326
|
+
any_reboot |= bool(reboot.value)
|
|
327
|
+
|
|
328
|
+
return any_reboot
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def add_device(
|
|
332
|
+
class_guid: str,
|
|
333
|
+
friendly_name: str,
|
|
334
|
+
hardware_ids: "list[str] | str",
|
|
335
|
+
inf_path: str,
|
|
336
|
+
compatible_ids: "list[str] | str | None" = None,
|
|
337
|
+
devdesc_override: str | None = None,
|
|
338
|
+
create_flags: int = DICD_GENERATE_ID,
|
|
339
|
+
existing_ok: bool = True,
|
|
340
|
+
force_install: bool = False,
|
|
341
|
+
quiet: bool = True
|
|
342
|
+
) -> None:
|
|
343
|
+
"""
|
|
344
|
+
Create a root-enumerated device node and bind a driver to it.
|
|
345
|
+
This is a wrapper around the two functions create_root_enumerated_devnode() and
|
|
346
|
+
update_driver_for_hwids().
|
|
347
|
+
|
|
348
|
+
This adds the device to the system and binds the driver to it.
|
|
349
|
+
|
|
350
|
+
:param class_guid: string representation of a GUID.
|
|
351
|
+
Device class GUID (e.g. GUID_DEVCLASS_NET, GUID_DEVCLASS_MEDIA …).
|
|
352
|
+
Example:
|
|
353
|
+
class_guid="{4d36e972-e325-11ce-bfc1-08002be10318}"
|
|
354
|
+
This is the GUID for network adapters.
|
|
355
|
+
:param friendly_name: str
|
|
356
|
+
Initial instance name placed in the registry (DeviceDesc).
|
|
357
|
+
Also, will be shown in Device Manager.
|
|
358
|
+
:param hardware_ids: str | list[str]
|
|
359
|
+
One or more hardware IDs (MULTI_SZ). The *first* one is the key
|
|
360
|
+
identifier PnP uses when selecting an INF.
|
|
361
|
+
:param inf_path: str
|
|
362
|
+
Full path to the target driver’s .INF file (must already be accessible or
|
|
363
|
+
pre-staged).
|
|
364
|
+
:param compatible_ids: str | list[str] | None
|
|
365
|
+
Optional Compatible-ID list (another MULTI_SZ, lower priority).
|
|
366
|
+
:param devdesc_override: str | None
|
|
367
|
+
If supplied, written to SPDRP_DEVICEDESC (rarely necessary because
|
|
368
|
+
the INF’s own DeviceDesc usually replaces it).
|
|
369
|
+
:param create_flags: int
|
|
370
|
+
Flags for SetupDiCreateDeviceInfoW. Default is DICD_GENERATE_ID.
|
|
371
|
+
:param existing_ok: bool
|
|
372
|
+
If True, silently succeed when the devnode already exists.
|
|
373
|
+
:param force_install: bool, default False
|
|
374
|
+
If True the function sets INSTALLFLAG_FORCE so the specified INF will be
|
|
375
|
+
applied even when Windows thinks a “better” driver is already installed.
|
|
376
|
+
:param quiet: bool, default True
|
|
377
|
+
If True the installation runs without UI (parent window = NULL). Set
|
|
378
|
+
False if you want progress dialogs.
|
|
379
|
+
:return: None
|
|
380
|
+
|
|
381
|
+
=====================================================================
|
|
382
|
+
Examples
|
|
383
|
+
--------
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
"""
|
|
388
|
+
create_root_enumerated_devnode(
|
|
389
|
+
class_guid=class_guid,
|
|
390
|
+
friendly_name=friendly_name,
|
|
391
|
+
hardware_ids=hardware_ids,
|
|
392
|
+
compatible_ids=compatible_ids,
|
|
393
|
+
devdesc_override=devdesc_override,
|
|
394
|
+
create_flags=create_flags,
|
|
395
|
+
existing_ok=existing_ok
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
update_driver_for_hwids(
|
|
399
|
+
hardware_ids=hardware_ids,
|
|
400
|
+
inf_path=inf_path,
|
|
401
|
+
force_install=force_install,
|
|
402
|
+
quiet=quiet,
|
|
403
|
+
existing_ok=existing_ok
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def remove_device(
|
|
408
|
+
pnp_device_id: str,
|
|
409
|
+
class_guid: str
|
|
410
|
+
) -> bool:
|
|
411
|
+
"""
|
|
412
|
+
Delete the single device whose PNPDeviceID
|
|
413
|
+
equals the string you pass in (case-insensitive). Returns True on
|
|
414
|
+
success, False if no matching devnode was found.
|
|
415
|
+
|
|
416
|
+
:param pnp_device_id: PNPDeviceID of the device to remove.
|
|
417
|
+
If you're using the Win32_NetworkAdapter class, you can
|
|
418
|
+
get the PNPDeviceID from the object itself: network_config.PNPDeviceID
|
|
419
|
+
:param class_guid: string representation of the device class GUID.
|
|
420
|
+
Example: '{4d36e972-e325-11ce-bfc1-08002be10318}' for network adapters.
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
class_guid_bytes = uuid.UUID(class_guid).bytes_le
|
|
424
|
+
class_guid_object = (ctypes.c_byte * 16).from_buffer_copy(class_guid_bytes)
|
|
425
|
+
|
|
426
|
+
# Get only PRESENT devices in the Network class
|
|
427
|
+
hdi = SetupDiGetClassDevsW(class_guid_object, None, None, DIGCF_PRESENT)
|
|
428
|
+
if hdi == wintypes.HANDLE(-1).value:
|
|
429
|
+
raise ctypes.WinError(ctypes.get_last_error())
|
|
430
|
+
|
|
431
|
+
devinfo = SP_DEVINFO_DATA()
|
|
432
|
+
devinfo.cbSize = ctypes.sizeof(devinfo)
|
|
433
|
+
index = 0
|
|
434
|
+
removed = False
|
|
435
|
+
|
|
436
|
+
# Helper to fetch the instance-ID of the current element
|
|
437
|
+
instance_buf_len = 512
|
|
438
|
+
GetInstanceId = setupapi.SetupDiGetDeviceInstanceIdW
|
|
439
|
+
GetInstanceId.argtypes = [
|
|
440
|
+
wintypes.HANDLE, ctypes.POINTER(SP_DEVINFO_DATA),
|
|
441
|
+
wintypes.LPWSTR, wintypes.DWORD, ctypes.POINTER(wintypes.DWORD)
|
|
442
|
+
]
|
|
443
|
+
GetInstanceId.restype = wintypes.BOOL
|
|
444
|
+
inst_buf = ctypes.create_unicode_buffer(instance_buf_len)
|
|
445
|
+
|
|
446
|
+
while SetupDiEnumDeviceInfo(hdi, index, ctypes.byref(devinfo)):
|
|
447
|
+
index += 1
|
|
448
|
+
|
|
449
|
+
if not GetInstanceId(hdi, ctypes.byref(devinfo),
|
|
450
|
+
inst_buf, instance_buf_len, None):
|
|
451
|
+
continue
|
|
452
|
+
|
|
453
|
+
if inst_buf.value.lower() != pnp_device_id.lower():
|
|
454
|
+
continue # not the target
|
|
455
|
+
|
|
456
|
+
# Found it → remove
|
|
457
|
+
if not SetupDiCallClassInstaller(DIF_REMOVE, hdi, ctypes.byref(devinfo)):
|
|
458
|
+
err = ctypes.get_last_error()
|
|
459
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
460
|
+
raise ctypes.WinError(err)
|
|
461
|
+
|
|
462
|
+
removed = True
|
|
463
|
+
break
|
|
464
|
+
|
|
465
|
+
SetupDiDestroyDeviceInfoList(hdi)
|
|
466
|
+
return removed
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class NotWindowsConsoleError(Exception):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Define QuickEdit mode bit (0x0040)
|
|
9
|
+
ENABLE_QUICK_EDIT = 0x0040
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def disable_quick_edit():
|
|
13
|
+
"""
|
|
14
|
+
Disables QuickEdit mode in the Windows Command Prompt.
|
|
15
|
+
This prevents the console from being paused when the user selects text.
|
|
16
|
+
NO ADMIN REQUIRED
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
kernel32 = ctypes.windll.kernel32
|
|
20
|
+
h_stdin = kernel32.GetStdHandle(-10) # -10 is STD_INPUT_HANDLE
|
|
21
|
+
|
|
22
|
+
# Get current console mode
|
|
23
|
+
mode = ctypes.c_uint()
|
|
24
|
+
if kernel32.GetConsoleMode(h_stdin, ctypes.byref(mode)) == 0:
|
|
25
|
+
try:
|
|
26
|
+
raise ctypes.WinError()
|
|
27
|
+
except OSError as e:
|
|
28
|
+
# This means that the code is not running in console window.
|
|
29
|
+
if e.errno == 9 and e.winerror == 6:
|
|
30
|
+
raise NotWindowsConsoleError("This code is not running in a Windows console.")
|
|
31
|
+
else:
|
|
32
|
+
raise e
|
|
33
|
+
|
|
34
|
+
# Disable QuickEdit Mode by clearing the corresponding bit
|
|
35
|
+
mode.value &= ~ENABLE_QUICK_EDIT
|
|
36
|
+
|
|
37
|
+
# Set the new console mode
|
|
38
|
+
if kernel32.SetConsoleMode(h_stdin, mode) == 0:
|
|
39
|
+
raise ctypes.WinError()
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import threading
|
|
2
|
+
|
|
1
3
|
import docker
|
|
4
|
+
from docker.models.containers import Container
|
|
5
|
+
from docker import DockerClient
|
|
2
6
|
|
|
3
7
|
from ...print_api import print_api
|
|
4
8
|
|
|
@@ -64,8 +68,8 @@ def change_image_content(
|
|
|
64
68
|
dockerw.change_image_content(
|
|
65
69
|
image_id_or_name="your_docker_image_id_or_name",
|
|
66
70
|
list_of_commands=[
|
|
67
|
-
"apt
|
|
68
|
-
"apt
|
|
71
|
+
"apt update",
|
|
72
|
+
"apt install -y python3"
|
|
69
73
|
]
|
|
70
74
|
)
|
|
71
75
|
----------------------
|
|
@@ -167,3 +171,110 @@ def add_execution_permissions_for_file(image_id_or_name: str, file_path: str, pr
|
|
|
167
171
|
list_of_commands=[f"chmod +x {file_path}"],
|
|
168
172
|
print_kwargs=print_kwargs
|
|
169
173
|
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def stop_remove_containers_by_image_name(image_name: str):
|
|
177
|
+
"""
|
|
178
|
+
Remove all containers by image name.
|
|
179
|
+
:param image_name: str, the name of the image.
|
|
180
|
+
:return:
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
def stop_remove_containers(container_instance: Container):
|
|
184
|
+
"""
|
|
185
|
+
Stop and remove a container instance.
|
|
186
|
+
:param container_instance: Container, the docker container object.
|
|
187
|
+
"""
|
|
188
|
+
if container_instance.status == "running":
|
|
189
|
+
print_api(f"Stopping container: [{container_instance.name}]. Short ID: [{container_instance.short_id}]")
|
|
190
|
+
container_instance.stop()
|
|
191
|
+
print_api(f"Removing container: [{container_instance.name}]. Short ID: [{container_instance.short_id}]")
|
|
192
|
+
container_instance.remove()
|
|
193
|
+
print_api(f"Container removed: [{container_instance.name}]. Short ID: [{container_instance.short_id}]", color='blue')
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
client = docker.from_env()
|
|
197
|
+
all_containers = client.containers.list(all=True)
|
|
198
|
+
|
|
199
|
+
containers_to_remove: list[Container] = []
|
|
200
|
+
for container in all_containers:
|
|
201
|
+
if any(image_name in tag for tag in container.image.tags):
|
|
202
|
+
containers_to_remove.append(container)
|
|
203
|
+
|
|
204
|
+
for removing_container in containers_to_remove:
|
|
205
|
+
threading.Thread(target=stop_remove_containers, args=(removing_container,), daemon=True).start()
|
|
206
|
+
|
|
207
|
+
client.close()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def start_container_without_stop(
|
|
211
|
+
image_name: str,
|
|
212
|
+
client: DockerClient = None,
|
|
213
|
+
**kwargs) -> tuple[DockerClient, Container]:
|
|
214
|
+
"""
|
|
215
|
+
Start a container in detached mode, this container will not run the entry point, but will run the infinite sleep.
|
|
216
|
+
This way the container will continue running, you can execute commands in it and stop it manually when needed.
|
|
217
|
+
:param image_name: str, the name of the image.
|
|
218
|
+
:param client: docker.DockerClient, the docker client. If not provided, it will use the default client.
|
|
219
|
+
:return: Container, the docker container object.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
if client is None:
|
|
223
|
+
client = docker.from_env()
|
|
224
|
+
|
|
225
|
+
kwargs.setdefault('detach', True)
|
|
226
|
+
kwargs.setdefault('mem_limit', '512m')
|
|
227
|
+
kwargs.setdefault('ulimits', [docker.types.Ulimit(name='nofile', soft=20000, hard=50000)])
|
|
228
|
+
kwargs.setdefault('remove', False)
|
|
229
|
+
|
|
230
|
+
# Start the container with a "do nothing" command so it stays running
|
|
231
|
+
print_api(f"Starting container from image '{image_name}'...")
|
|
232
|
+
container = client.containers.run(
|
|
233
|
+
image=image_name,
|
|
234
|
+
entrypoint=["/bin/sh", "-c", "tail -f /dev/null"],
|
|
235
|
+
**kwargs
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
stdout = container.logs(stdout=True, stderr=False).decode()
|
|
239
|
+
stderr = container.logs(stdout=False, stderr=True).decode()
|
|
240
|
+
if stdout:
|
|
241
|
+
print_api(f"Container stdout: {stdout}")
|
|
242
|
+
if stderr:
|
|
243
|
+
print_api(f"Container stderr: {stderr}")
|
|
244
|
+
|
|
245
|
+
if not stderr:
|
|
246
|
+
print_api("Container started successfully.")
|
|
247
|
+
|
|
248
|
+
print_api(f"Started container: [{container.name}]. Short ID: [{container.short_id}]")
|
|
249
|
+
|
|
250
|
+
return client, container
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def run_command_in_running_container(container: Container, command: list) -> tuple[int, bytes, str]:
|
|
254
|
+
"""
|
|
255
|
+
Run a command in a running container.
|
|
256
|
+
:param container: Container, the docker container object.
|
|
257
|
+
:param command: list, the command to run.
|
|
258
|
+
:return: tuple of (exit_code, output, string_output).
|
|
259
|
+
"""
|
|
260
|
+
|
|
261
|
+
# Run the command inside the already running container
|
|
262
|
+
exec_result = container.exec_run(cmd=command, stdout=True, stderr=True)
|
|
263
|
+
# Capture logs
|
|
264
|
+
output_text = exec_result.output.decode("utf-8", errors="replace")
|
|
265
|
+
execution_result_message: str = str()
|
|
266
|
+
if output_text:
|
|
267
|
+
execution_result_message = f"Container execution result:\n{output_text}"
|
|
268
|
+
|
|
269
|
+
if exec_result.exit_code != 0:
|
|
270
|
+
# logging.warning(f"Extraction command returned code {exec_result.exit_code} for '{filename}'")
|
|
271
|
+
code_message = f"Extraction command returned code {exec_result.exit_code}"
|
|
272
|
+
else:
|
|
273
|
+
code_message = "Extraction succeeded"
|
|
274
|
+
|
|
275
|
+
if execution_result_message:
|
|
276
|
+
execution_result_message += f"\n{code_message}"
|
|
277
|
+
else:
|
|
278
|
+
execution_result_message = code_message
|
|
279
|
+
|
|
280
|
+
return exec_result.exit_code, exec_result.output, execution_result_message
|
|
@@ -7,17 +7,5 @@ DEFAULT_KIBANA_PORT: str = '5601'
|
|
|
7
7
|
DEFAULT_KIBANA_HOST: str = 'localhost'
|
|
8
8
|
DEFAULT_KIBANA_URL: str = f"http://{DEFAULT_KIBANA_HOST}:{DEFAULT_KIBANA_PORT}"
|
|
9
9
|
|
|
10
|
-
ELASTIC_SEARCH_CONFIG_DIRECTORY: str = "/etc/elasticsearch"
|
|
11
|
-
|
|
12
|
-
ELASTIC_CONFIG_FILE: str = f"{ELASTIC_SEARCH_CONFIG_DIRECTORY}/elasticsearch.yml"
|
|
13
|
-
XPACK_SECURITY_SETTING_NAME: str = "xpack.security.enabled"
|
|
14
|
-
|
|
15
|
-
ELASTIC_JVM_OPTIONS_DIRECTORY: str = f"{ELASTIC_SEARCH_CONFIG_DIRECTORY}/jvm.options.d"
|
|
16
|
-
ELASTIC_JVM_OPTIONS_4GB_CUSTOM_FILE: str = f"{ELASTIC_JVM_OPTIONS_DIRECTORY}/4gb_memory_heap.options"
|
|
17
|
-
ELASTIC_JVM_OPTIONS_4GB_MEMORY_USAGE: list[str] = ['-Xms4g', '-Xmx4g']
|
|
18
|
-
|
|
19
|
-
UBUNTU_DEPENDENCY_PACKAGES: list[str] = ['apt-transport-https', 'openjdk-11-jdk', 'wget']
|
|
20
|
-
UBUNTU_ELASTIC_PACKAGE_NAME: str = 'elasticsearch'
|
|
21
10
|
UBUNTU_ELASTIC_SERVICE_NAME: str = 'elasticsearch'
|
|
22
|
-
UBUNTU_KIBANA_PACKAGE_NAME: str = 'kibana'
|
|
23
11
|
UBUNTU_KIBANA_SERVICE_NAME: str = 'kibana'
|