zscams 2.0.2__tar.gz → 2.0.4__tar.gz
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.
- {zscams-2.0.2 → zscams-2.0.4}/PKG-INFO +6 -11
- {zscams-2.0.2 → zscams-2.0.4}/pyproject.toml +7 -6
- zscams-2.0.4/zscams/__init__.py +3 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/backend/bootstrap.py +7 -4
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/mac.py +1 -1
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/os.py +64 -11
- zscams-2.0.4/zscams/deps.py +86 -0
- zscams-2.0.2/zscams/agent/src/support/__init__.py +0 -0
- zscams-2.0.2/zscams/lib/getmac/__init__.py +0 -3
- zscams-2.0.2/zscams/lib/getmac/__main__.py +0 -123
- zscams-2.0.2/zscams/lib/getmac/getmac.py +0 -1900
- zscams-2.0.2/zscams/lib/getmac/shutilwhich.py +0 -67
- {zscams-2.0.2 → zscams-2.0.4}/README.md +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/__main__.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/__init__.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/certificates/.gitkeep +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/config.yaml +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/configuration/config.j2 +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/configuration/service.j2 +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/keys/autoport.key +0 -0
- {zscams-2.0.2/zscams → zscams-2.0.4/zscams/agent/src}/__init__.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/__init__.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/backend/client.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/backend/exceptions.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/backend/update_machine_info.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/prerequisites.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/service_health_check.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/services.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/tunnel/__init__.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/tunnel/tls.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/core/tunnels.py +0 -0
- {zscams-2.0.2/zscams/agent/src → zscams-2.0.4/zscams/agent/src/services}/__init__.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/services/reverse_ssh.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/services/ssh_forwarder.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/services/system_monitor.py +0 -0
- {zscams-2.0.2/zscams/agent/src/services → zscams-2.0.4/zscams/agent/src/support}/__init__.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/cli.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/configuration.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/filesystem.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/logger.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/network.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/openssl.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/ssh.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/agent/src/support/yaml.py +0 -0
- {zscams-2.0.2 → zscams-2.0.4}/zscams/lib/.gitkeep +0 -0
|
@@ -1,25 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: zscams
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Async TLS tunnel client with SNI routing, auto-reconnect, and health checks
|
|
5
5
|
Author: OCD - Cairo Software Team
|
|
6
6
|
Maintainer: OCD - Cairo Software Team
|
|
7
|
-
|
|
8
|
-
Classifier: Programming Language :: Python :: 2.7
|
|
7
|
+
Requires-Python: >=3.9
|
|
9
8
|
Classifier: Programming Language :: Python :: 3
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.4
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.5
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.6
|
|
13
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
15
9
|
Classifier: Programming Language :: Python :: 3.9
|
|
16
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
-
Requires-Dist: PyYAML
|
|
21
|
-
Requires-Dist: cryptography
|
|
22
|
-
Requires-Dist:
|
|
14
|
+
Requires-Dist: PyYAML ; sys_platform != "freebsd"
|
|
15
|
+
Requires-Dist: cryptography ; sys_platform != "freebsd"
|
|
16
|
+
Requires-Dist: getmac
|
|
17
|
+
Requires-Dist: psutil ; sys_platform != "freebsd"
|
|
23
18
|
Requires-Dist: requests
|
|
24
19
|
Description-Content-Type: text/markdown
|
|
25
20
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "zscams_agent"
|
|
3
|
-
requires-python = "^3.9.2"
|
|
4
3
|
|
|
5
4
|
[tool.poetry]
|
|
6
5
|
name = "zscams"
|
|
7
|
-
version = "2.0.
|
|
6
|
+
version = "2.0.4"
|
|
8
7
|
description = "Async TLS tunnel client with SNI routing, auto-reconnect, and health checks"
|
|
9
8
|
authors = ["OCD - Cairo Software Team"]
|
|
10
9
|
maintainers = ["OCD - Cairo Software Team"]
|
|
@@ -12,10 +11,12 @@ readme = "README.md"
|
|
|
12
11
|
packages = [ { include = "zscams"}]
|
|
13
12
|
|
|
14
13
|
[tool.poetry.dependencies]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
python = ">=3.9"
|
|
15
|
+
requests = "*"
|
|
16
|
+
getmac = "*"
|
|
17
|
+
PyYAML = { version = "*", markers = "sys_platform != 'freebsd'" }
|
|
18
|
+
cryptography = { version = "*", markers = "sys_platform != 'freebsd'" }
|
|
19
|
+
psutil = { version = "*", markers = "sys_platform != 'freebsd'" }
|
|
19
20
|
|
|
20
21
|
[tool.poetry.scripts]
|
|
21
22
|
zscams = "zscams.__main__:main"
|
|
@@ -6,7 +6,7 @@ from typing import cast
|
|
|
6
6
|
from zscams.agent.src.core.backend.client import backend_client
|
|
7
7
|
from zscams.agent.src.support.configuration import reinitialize
|
|
8
8
|
from zscams.agent.src.support.logger import get_logger
|
|
9
|
-
from zscams.agent.src.support.os import create_system_user,
|
|
9
|
+
from zscams.agent.src.support.os import create_system_user, install_service
|
|
10
10
|
from zscams.agent.src.support.ssh import add_to_authorized_keys
|
|
11
11
|
from zscams.agent.src.support.cli import prompt
|
|
12
12
|
|
|
@@ -43,9 +43,11 @@ def bootstrap():
|
|
|
43
43
|
required=True,
|
|
44
44
|
startswith="",
|
|
45
45
|
)
|
|
46
|
-
equipment_name =
|
|
46
|
+
equipment_name = (
|
|
47
|
+
f"{equipment_type.lower()}-{customer_name.lower()}-{connector_name.lower()}"
|
|
48
|
+
)
|
|
47
49
|
enforced_id = prompt("Enforced ID", "Enforced Agent ID")
|
|
48
|
-
reinitialize(equipment_name=equipment_name,equipment_type=equipment_type)
|
|
50
|
+
reinitialize(equipment_name=equipment_name, equipment_type=equipment_type)
|
|
49
51
|
cm_info = backend_client.bootstrap(equipment_name, enforced_id or None)
|
|
50
52
|
|
|
51
53
|
sys_user = cast(str, cm_info.get("ssh_user"))
|
|
@@ -63,6 +65,7 @@ def install_zscams_systemd_service(user_to_run_as: str):
|
|
|
63
65
|
"user_to_run_as": user_to_run_as,
|
|
64
66
|
}
|
|
65
67
|
import zscams
|
|
68
|
+
|
|
66
69
|
BASE_DIR = Path(zscams.__file__).resolve().parent
|
|
67
70
|
template_path = f"{BASE_DIR}/agent/configuration/service.j2"
|
|
68
71
|
with open(template_path) as f:
|
|
@@ -70,4 +73,4 @@ def install_zscams_systemd_service(user_to_run_as: str):
|
|
|
70
73
|
|
|
71
74
|
rendered_config = template.format(**data)
|
|
72
75
|
|
|
73
|
-
|
|
76
|
+
install_service("zscams.service", rendered_config)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import subprocess
|
|
3
|
-
|
|
3
|
+
import platform
|
|
4
|
+
import subprocess
|
|
4
5
|
from zscams.agent.src.support.logger import get_logger
|
|
5
6
|
|
|
6
7
|
from .filesystem import write_to_file
|
|
@@ -9,7 +10,7 @@ logger = get_logger("os_support")
|
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def is_linux():
|
|
12
|
-
if sys.platform.startswith("linux"):
|
|
13
|
+
if sys.platform.lower().startswith("linux"):
|
|
13
14
|
return True
|
|
14
15
|
return False
|
|
15
16
|
|
|
@@ -28,10 +29,18 @@ def system_user_exists(username: str):
|
|
|
28
29
|
return False
|
|
29
30
|
|
|
30
31
|
|
|
32
|
+
def is_freebsd():
|
|
33
|
+
return (
|
|
34
|
+
platform.system().lower() == "freebsd"
|
|
35
|
+
or platform.system().lower() == "zscaleros"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
31
39
|
def create_system_user(username: str):
|
|
32
|
-
|
|
40
|
+
# Support both Linux and FreeBSD
|
|
41
|
+
if not (platform.system() == "Linux" or is_freebsd()):
|
|
33
42
|
logger.error(
|
|
34
|
-
"Error creating system user: This script is intended to run on Linux systems."
|
|
43
|
+
"Error creating system user: This script is intended to run on Linux or FreeBSD systems."
|
|
35
44
|
)
|
|
36
45
|
return
|
|
37
46
|
|
|
@@ -39,24 +48,68 @@ def create_system_user(username: str):
|
|
|
39
48
|
logger.error("Error creating system user: Username must be provided.")
|
|
40
49
|
return
|
|
41
50
|
|
|
51
|
+
# Assuming system_user_exists is already updated to handle both
|
|
42
52
|
if system_user_exists(username):
|
|
43
53
|
logger.warning("User '%s' already exists.", username)
|
|
44
54
|
return
|
|
45
55
|
|
|
46
56
|
try:
|
|
47
|
-
|
|
57
|
+
if is_freebsd():
|
|
58
|
+
cmd = ["sudo", "pw", "useradd", "-n", username, "-m", "-s", "/bin/sh"]
|
|
59
|
+
else:
|
|
60
|
+
# Standard Linux useradd
|
|
61
|
+
cmd = ["sudo", "useradd", "-m", "-s", "/bin/bash", username]
|
|
62
|
+
|
|
63
|
+
subprocess.run(cmd, check=True)
|
|
48
64
|
logger.info("System user '%s' created successfully.", username)
|
|
65
|
+
|
|
49
66
|
except subprocess.CalledProcessError as e:
|
|
50
67
|
logger.error("Failed to create user '%s': %s", username, e)
|
|
51
68
|
|
|
52
69
|
|
|
53
|
-
def
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
def install_service(service_name: str, content: str):
|
|
71
|
+
"""
|
|
72
|
+
Main entry point to install services.
|
|
73
|
+
Redirects to systemd for Linux and rc.d for FreeBSD.
|
|
74
|
+
"""
|
|
75
|
+
if is_linux():
|
|
76
|
+
install_systemd_service(service_name, content)
|
|
77
|
+
elif is_freebsd():
|
|
78
|
+
install_rc_service(service_name, content)
|
|
79
|
+
else:
|
|
80
|
+
logger.error("Unsupported OS for service installation.")
|
|
81
|
+
|
|
59
82
|
|
|
83
|
+
def install_rc_service(service_name: str, content: str):
|
|
84
|
+
"""
|
|
85
|
+
Installs a FreeBSD rc.d script.
|
|
86
|
+
Note: 'content' for FreeBSD should be a valid rc.subr shell script.
|
|
87
|
+
"""
|
|
88
|
+
service_path = f"/usr/local/etc/rc.d/{service_name}"
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
# 1. Write the rc script
|
|
92
|
+
logger.debug("Installing FreeBSD rc script: %s", service_path)
|
|
93
|
+
# Using sudo tee to ensure write permissions on restricted paths
|
|
94
|
+
echo_cmd = f"printf '%s' '{content}' | sudo tee {service_path}"
|
|
95
|
+
subprocess.run(echo_cmd, shell=True, check=True, stdout=subprocess.DEVNULL)
|
|
96
|
+
|
|
97
|
+
# 2. Make it executable (Crucial for FreeBSD)
|
|
98
|
+
subprocess.run(["sudo", "chmod", "+x", service_path], check=True)
|
|
99
|
+
|
|
100
|
+
# 3. Enable and Start
|
|
101
|
+
# In FreeBSD, enabling adds 'service_name_enable="YES"' to /etc/rc.conf
|
|
102
|
+
logger.debug("Enabling and starting FreeBSD service %s...", service_name)
|
|
103
|
+
subprocess.run(["sudo", "sysrc", f"{service_name}_enable=YES"], check=True)
|
|
104
|
+
subprocess.run(["sudo", "service", service_name, "start"], check=True)
|
|
105
|
+
|
|
106
|
+
logger.info("Service %s installed and started successfully.", service_name)
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.error("Failed to install FreeBSD service %s: %s", service_name, e)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def install_systemd_service(service_name: str, content: str):
|
|
60
113
|
service_path = f"/etc/systemd/system/{service_name}"
|
|
61
114
|
|
|
62
115
|
try:
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
import platform
|
|
4
|
+
import os
|
|
5
|
+
import importlib.util
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def is_installed(name):
|
|
9
|
+
return importlib.util.find_spec(name) is not None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def ensure_native_deps():
|
|
13
|
+
# Only run this logic on FreeBSD
|
|
14
|
+
if (
|
|
15
|
+
platform.system().lower() != "freebsd"
|
|
16
|
+
and platform.system().lower() != "zscaleros"
|
|
17
|
+
):
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
# 1. Define what we need and how FreeBSD names them
|
|
21
|
+
py_ver = f"py{sys.version_info.major}{sys.version_info.minor}"
|
|
22
|
+
deps_map = {
|
|
23
|
+
"cryptography": f"{py_ver}-cryptography",
|
|
24
|
+
"yaml": f"{py_ver}-pyyaml",
|
|
25
|
+
"psutil": f"{py_ver}-psutil",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Identify which modules are actually missing
|
|
29
|
+
missing_mods = [mod for mod in deps_map if not is_installed(mod)]
|
|
30
|
+
|
|
31
|
+
if not missing_mods:
|
|
32
|
+
return # Everything is already installed
|
|
33
|
+
|
|
34
|
+
print(
|
|
35
|
+
f"--> ZscalerOS/FreeBSD detected. Missing requirements: {', '.join(missing_mods)}"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# 2. Ensure the FreeBSD Mirror is configured
|
|
39
|
+
# We use a custom local file so we don't overwrite Zscaler's system configs
|
|
40
|
+
repo_conf_dir = "/usr/local/etc/pkg/repos"
|
|
41
|
+
repo_conf_file = f"{repo_conf_dir}/FreeBSD.conf"
|
|
42
|
+
|
|
43
|
+
if not os.path.exists(repo_conf_file):
|
|
44
|
+
print("--> Mirror not found. Configuring official FreeBSD repository...")
|
|
45
|
+
# Hardcoding the ABI to 13 because ZscalerOS identifies as 42-RELEASE
|
|
46
|
+
mirror_config = (
|
|
47
|
+
"FreeBSD: { "
|
|
48
|
+
'url: "pkg+http://pkg.FreeBSD.org/FreeBSD:13:amd64/latest", '
|
|
49
|
+
'mirror_type: "srv", '
|
|
50
|
+
'signature_type: "fingerprints", '
|
|
51
|
+
'fingerprints: "/usr/share/keys/pkg", '
|
|
52
|
+
"enabled: yes "
|
|
53
|
+
"}"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
subprocess.run(["sudo", "mkdir", "-p", repo_conf_dir], check=True)
|
|
58
|
+
# Use printf/tee to handle the sudo write to a restricted path
|
|
59
|
+
subprocess.run(
|
|
60
|
+
f"printf '{mirror_config}' | sudo tee {repo_conf_file}",
|
|
61
|
+
shell=True,
|
|
62
|
+
check=True,
|
|
63
|
+
capture_output=True,
|
|
64
|
+
)
|
|
65
|
+
print(
|
|
66
|
+
"--> Mirror configured. Updating package database (this may take a moment)..."
|
|
67
|
+
)
|
|
68
|
+
subprocess.run(["sudo", "pkg", "update", "-f"], check=True)
|
|
69
|
+
except subprocess.CalledProcessError as e:
|
|
70
|
+
print(f"--> Failed to configure mirror. Error: {e}")
|
|
71
|
+
sys.exit(1)
|
|
72
|
+
|
|
73
|
+
# 3. Install the missing packages
|
|
74
|
+
targets = [deps_map[m] for m in missing_mods]
|
|
75
|
+
print(f"--> Attempting auto-install of: {', '.join(targets)}")
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
# Install via pkg
|
|
79
|
+
subprocess.run(["sudo", "pkg", "install", "-y"] + targets, check=True)
|
|
80
|
+
print("--> Dependencies installed successfully!")
|
|
81
|
+
print("--> Please restart your command to apply changes.")
|
|
82
|
+
sys.exit(0)
|
|
83
|
+
except subprocess.CalledProcessError:
|
|
84
|
+
print("--> Error: Failed to install packages. Check your internet connection.")
|
|
85
|
+
print(f"--> Manual command: sudo pkg install {' '.join(targets)}")
|
|
86
|
+
sys.exit(1)
|
|
File without changes
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
from __future__ import print_function
|
|
5
|
-
|
|
6
|
-
import argparse
|
|
7
|
-
import logging
|
|
8
|
-
import sys
|
|
9
|
-
|
|
10
|
-
from . import getmac
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def main(): # type: () -> None
|
|
14
|
-
parser = argparse.ArgumentParser(
|
|
15
|
-
"getmac",
|
|
16
|
-
description="Get the MAC address of system network "
|
|
17
|
-
"interfaces or remote hosts on the LAN",
|
|
18
|
-
)
|
|
19
|
-
parser.add_argument(
|
|
20
|
-
"--version", action="version", version="getmac %s" % getmac.__version__
|
|
21
|
-
)
|
|
22
|
-
parser.add_argument(
|
|
23
|
-
"-v", "--verbose", action="store_true", help="Enable output messages"
|
|
24
|
-
)
|
|
25
|
-
parser.add_argument(
|
|
26
|
-
"-d",
|
|
27
|
-
"--debug",
|
|
28
|
-
action="count",
|
|
29
|
-
help="Enable debugging output. Add characters to "
|
|
30
|
-
"increase verbosity of output, e.g. '-dd'.",
|
|
31
|
-
)
|
|
32
|
-
parser.add_argument(
|
|
33
|
-
"-N",
|
|
34
|
-
"--no-net",
|
|
35
|
-
"--no-network-requests",
|
|
36
|
-
action="store_true",
|
|
37
|
-
dest="NO_NET",
|
|
38
|
-
help="Do not use arping or send a UDP packet to refresh the ARP table",
|
|
39
|
-
)
|
|
40
|
-
parser.add_argument(
|
|
41
|
-
"--override-port",
|
|
42
|
-
type=int,
|
|
43
|
-
metavar="PORT",
|
|
44
|
-
help="Override the default UDP port used to refresh the ARP table "
|
|
45
|
-
"if network requests are enabled and arping is unavailable",
|
|
46
|
-
)
|
|
47
|
-
parser.add_argument(
|
|
48
|
-
"--override-platform",
|
|
49
|
-
type=str,
|
|
50
|
-
default=None,
|
|
51
|
-
metavar="PLATFORM",
|
|
52
|
-
help="Override the platform detection with the given value "
|
|
53
|
-
"(e.g. 'linux', 'windows', 'freebsd', etc.'). "
|
|
54
|
-
"Any values returned by platform.system() are valid.",
|
|
55
|
-
)
|
|
56
|
-
parser.add_argument(
|
|
57
|
-
"--force-method",
|
|
58
|
-
type=str,
|
|
59
|
-
default=None,
|
|
60
|
-
metavar="METHOD",
|
|
61
|
-
help="Force a specific method to be used, e.g. 'IpNeighborShow'. "
|
|
62
|
-
"This will be used regardless of it's method type or platform "
|
|
63
|
-
"compatibility, and Method.test() will NOT be checked!",
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
group = parser.add_mutually_exclusive_group(required=False)
|
|
67
|
-
group.add_argument(
|
|
68
|
-
"-i",
|
|
69
|
-
"--interface",
|
|
70
|
-
type=str,
|
|
71
|
-
default=None,
|
|
72
|
-
help="Name of a network interface on the system",
|
|
73
|
-
)
|
|
74
|
-
group.add_argument(
|
|
75
|
-
"-4", "--ip", type=str, default=None, help="IPv4 address of a remote host"
|
|
76
|
-
)
|
|
77
|
-
group.add_argument(
|
|
78
|
-
"-6", "--ip6", type=str, default=None, help="IPv6 address of a remote host"
|
|
79
|
-
)
|
|
80
|
-
group.add_argument(
|
|
81
|
-
"-n", "--hostname", type=str, default=None, help="Hostname of a remote host"
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
args = parser.parse_args()
|
|
85
|
-
|
|
86
|
-
if args.debug or args.verbose:
|
|
87
|
-
logging.basicConfig(
|
|
88
|
-
format="%(levelname)-8s %(message)s", level=logging.DEBUG, stream=sys.stderr
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
if args.debug:
|
|
92
|
-
getmac.DEBUG = args.debug
|
|
93
|
-
|
|
94
|
-
if args.override_port:
|
|
95
|
-
port = int(args.override_port)
|
|
96
|
-
getmac.log.debug(
|
|
97
|
-
"Using UDP port %d (overriding the default port %d)", port, getmac.PORT
|
|
98
|
-
)
|
|
99
|
-
getmac.PORT = port
|
|
100
|
-
|
|
101
|
-
if args.override_platform:
|
|
102
|
-
getmac.OVERRIDE_PLATFORM = args.override_platform.strip().lower()
|
|
103
|
-
|
|
104
|
-
if args.force_method:
|
|
105
|
-
getmac.FORCE_METHOD = args.force_method.strip().lower()
|
|
106
|
-
|
|
107
|
-
mac = getmac.get_mac_address(
|
|
108
|
-
interface=args.interface,
|
|
109
|
-
ip=args.ip,
|
|
110
|
-
ip6=args.ip6,
|
|
111
|
-
hostname=args.hostname,
|
|
112
|
-
network_request=not args.NO_NET,
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
if mac is not None:
|
|
116
|
-
print(mac) # noqa: T001, T201
|
|
117
|
-
sys.exit(0) # Exit success!
|
|
118
|
-
else:
|
|
119
|
-
sys.exit(1) # Exit with error since it failed to find a MAC
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if __name__ == "__main__":
|
|
123
|
-
main()
|