diff --git a/.github/workflows/ci-docker-deployment.yaml b/.github/workflows/ci-docker-deployment.yaml index cf7c3356f..dc6666419 100644 --- a/.github/workflows/ci-docker-deployment.yaml +++ b/.github/workflows/ci-docker-deployment.yaml @@ -14,7 +14,7 @@ jobs: strategy: matrix: python-version: - - 3.10 + - 3.13 steps: - name: Check out code uses: actions/checkout@v4 diff --git a/.github/workflows/ci-main.yaml b/.github/workflows/ci-main.yaml index e6432bfeb..aee0110c9 100644 --- a/.github/workflows/ci-main.yaml +++ b/.github/workflows/ci-main.yaml @@ -70,7 +70,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.13" - uses: pre-commit/action@v3.0.1 trivy-scan: @@ -102,7 +102,7 @@ jobs: strategy: matrix: python-version: - - "3.10" + - "3.13" steps: - uses: actions/checkout@v4 - name: Setup python @@ -153,6 +153,7 @@ jobs: echo "${{ steps.get_commit_message.outputs.commit_message }}" test-integration-microk8s: + continue-on-error: true name: Run integration tests in microk8s deployment needs: - integration-tests-check @@ -177,11 +178,19 @@ jobs: - name: run automatic_setup_microk8s.sh run: integration_tests/automatic_setup_microk8s.sh integration - name: run tests + env: + MATRIX_INDEX: ${{ matrix.index }} working-directory: integration_tests run: | poetry run pytest --splunk_host="localhost" --splunk_password="changeme2" --trap_external_ip="$(hostname -I | cut -d " " -f1)" --sc4snmp_deployment="microk8s" -m part${{ matrix.index }} - + - name: Upload integration logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: integration-logs-microk8s-part${{ matrix.index }} + path: integration_tests/logs/ test-integration-compose: + continue-on-error: true name: Run integration tests in docker compose deployment needs: - integration-tests-check @@ -197,7 +206,7 @@ jobs: - name: Setup python uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.13" - name: Install docker compose run: | # Add Docker's official GPG key: @@ -218,5 +227,7 @@ jobs: run: integration_tests/automatic_setup_compose.sh integration - name: run tests working-directory: integration_tests + env: + MATRIX_INDEX: ${{ matrix.index }} run: | - poetry run pytest --splunk_host="localhost" --splunk_password="changeme2" --trap_external_ip="$(hostname -I | cut -d " " -f1)" --sc4snmp_deployment="docker-compose" -m part${{ matrix.index }} + poetry run pytest --splunk_host="localhost" --splunk_password="changeme2" --trap_external_ip="$(hostname -I | cut -d " " -f1)" --sc4snmp_deployment="docker-compose" -m part${{ matrix.index }} \ No newline at end of file diff --git a/.github/workflows/ci-ui-tests.yaml b/.github/workflows/ci-ui-tests.yaml index aef5eb4eb..eea5d5c1c 100644 --- a/.github/workflows/ci-ui-tests.yaml +++ b/.github/workflows/ci-ui-tests.yaml @@ -54,7 +54,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.13" - name: remove not used docker images run: | diff --git a/.github/workflows/mike.yaml b/.github/workflows/mike.yaml index 67171f31d..2d56a555a 100644 --- a/.github/workflows/mike.yaml +++ b/.github/workflows/mike.yaml @@ -40,7 +40,7 @@ jobs: token: "${{ secrets.PAT_CLATOOL }}" - uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.13" - name: Upload Docs run: | sudo apt update diff --git a/.github/workflows/offline-installation.yaml b/.github/workflows/offline-installation.yaml index f15362233..57f1153df 100644 --- a/.github/workflows/offline-installation.yaml +++ b/.github/workflows/offline-installation.yaml @@ -14,7 +14,7 @@ jobs: strategy: matrix: python-version: - - "3.10" + - "3.13" steps: - name: Check out code uses: actions/checkout@v4 diff --git a/Dockerfile b/Dockerfile index 9ca6c431e..53ead0dfe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.10-alpine AS base +FROM python:3.13-alpine AS base ENV PYTHONFAULTHANDLER=1 \ PYTHONHASHSEED=random \ diff --git a/charts/splunk-connect-for-snmp/templates/scheduler/deployment.yaml b/charts/splunk-connect-for-snmp/templates/scheduler/deployment.yaml index 4c625b115..c80aff79c 100644 --- a/charts/splunk-connect-for-snmp/templates/scheduler/deployment.yaml +++ b/charts/splunk-connect-for-snmp/templates/scheduler/deployment.yaml @@ -43,6 +43,8 @@ spec: "celery", "beat", ] env: + - name: USER + value: {{ .Values.deploymentUser | default "sc4snmp" | quote }} - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL diff --git a/charts/splunk-connect-for-snmp/templates/sim/deployment.yaml b/charts/splunk-connect-for-snmp/templates/sim/deployment.yaml index dfecc4d1c..7a6c789b7 100644 --- a/charts/splunk-connect-for-snmp/templates/sim/deployment.yaml +++ b/charts/splunk-connect-for-snmp/templates/sim/deployment.yaml @@ -44,6 +44,8 @@ spec: imagePullPolicy: {{ .Values.sim.pullPolicy | default "IfNotPresent" }} args: ["--config=/config/otel-collector-config.yaml"] env: + - name: USER + value: {{ .Values.deploymentUser | default "sc4snmp" | quote }} - name: signalfxToken valueFrom: secretKeyRef: diff --git a/charts/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/charts/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 75660e398..0f2720966 100644 --- a/charts/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/charts/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -27,5 +27,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: {{ .Values.deploymentUser | default "sc4snmp" | quote }} restartPolicy: Never diff --git a/charts/splunk-connect-for-snmp/templates/traps/deployment.yaml b/charts/splunk-connect-for-snmp/templates/traps/deployment.yaml index 069662092..724f44d2a 100644 --- a/charts/splunk-connect-for-snmp/templates/traps/deployment.yaml +++ b/charts/splunk-connect-for-snmp/templates/traps/deployment.yaml @@ -45,6 +45,8 @@ spec: "trap" ] env: + - name: USER + value: {{ .Values.deploymentUser | default "sc4snmp" | quote }} - name: CONFIG_PATH value: /app/config/config.yaml - name: CELERY_BROKER_URL diff --git a/charts/splunk-connect-for-snmp/templates/worker/_helpers.tpl b/charts/splunk-connect-for-snmp/templates/worker/_helpers.tpl index d385262d6..e607bb5c5 100644 --- a/charts/splunk-connect-for-snmp/templates/worker/_helpers.tpl +++ b/charts/splunk-connect-for-snmp/templates/worker/_helpers.tpl @@ -125,6 +125,8 @@ Common labels value: {{ .Values.poller.maxOidToProcess | default "70" | quote }} - name: MAX_REPETITIONS value: {{ .Values.poller.maxRepetitions | default "10" | quote }} +- name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: {{ .Values.poller.maxSnmpBulkWalkConcurrency | default "5" | quote }} - name: PYSNMP_DEBUG value: {{ .Values.pysnmpDebug | default "" | quote }} - name: PROFILES_RELOAD_DELAY diff --git a/charts/splunk-connect-for-snmp/templates/worker/flower/deployment.yaml b/charts/splunk-connect-for-snmp/templates/worker/flower/deployment.yaml index e2704a83d..65686aa31 100644 --- a/charts/splunk-connect-for-snmp/templates/worker/flower/deployment.yaml +++ b/charts/splunk-connect-for-snmp/templates/worker/flower/deployment.yaml @@ -40,6 +40,8 @@ spec: "celery", "flower", ] env: + - name: USER + value: {{ .Values.deploymentUser | default "sc4snmp" | quote }} {{- include "environmental-variables" . | nindent 12 }} ports: - containerPort: 5555 diff --git a/charts/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml b/charts/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml index 54bb8fe95..ad04298b5 100644 --- a/charts/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml +++ b/charts/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml @@ -45,6 +45,8 @@ spec: "celery", "worker-poller", ] env: + - name: USER + value: {{ .Values.deploymentUser | default "sc4snmp" | quote }} {{- include "environmental-variables" . | nindent 12 }} {{- include "environmental-variables-poller" . | nindent 12 }} {{- if .Values.worker.livenessProbe.enabled }} diff --git a/charts/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/charts/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index 548fa84e9..7187ce3ef 100644 --- a/charts/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/charts/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -44,6 +44,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: {{ .Values.deploymentUser | default "sc4snmp" | quote }} {{- include "environmental-variables" . | nindent 12 }} {{- include "environmental-variables-sender" . | nindent 12 }} {{- if .Values.splunk.mtls.enabled }} diff --git a/charts/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml b/charts/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml index 379beaf0c..e26fef4b9 100644 --- a/charts/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml +++ b/charts/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml @@ -45,6 +45,8 @@ spec: "celery", "worker-trap", ] env: + - name: USER + value: {{ .Values.deploymentUser | default "sc4snmp" | quote }} {{- include "environmental-variables" . | nindent 12 }} {{- include "environmental-variables-trap" . | nindent 12 }} volumeMounts: diff --git a/charts/splunk-connect-for-snmp/values.schema.json b/charts/splunk-connect-for-snmp/values.schema.json index 3f6a2a3f2..0299a9994 100644 --- a/charts/splunk-connect-for-snmp/values.schema.json +++ b/charts/splunk-connect-for-snmp/values.schema.json @@ -34,6 +34,9 @@ } } }, + "deploymentUser" : { + "type": "string" + }, "imagePullSecrets": { "type": "array" }, @@ -365,6 +368,9 @@ "maxRepetitions": { "type": "integer" }, + "maxSnmpBulkWalkConcurrency": { + "type": "integer" + }, "usernameSecrets": { "type": "array" }, diff --git a/charts/splunk-connect-for-snmp/values.yaml b/charts/splunk-connect-for-snmp/values.yaml index 89dcab439..635cad566 100644 --- a/charts/splunk-connect-for-snmp/values.yaml +++ b/charts/splunk-connect-for-snmp/values.yaml @@ -247,6 +247,11 @@ poller: # If this number is too high it can cause issues with SNMP response especially for old SNMP devices. maxRepetitions: 10 + # The maximum number of concurrent SNMP bulk walks (bulk_walk_cmd) that can be executed at the same time. + # Setting this control determines the number of OIDsubtrees are walked in parallel and helps prevent overwhelming the target device or network. + # Default is 5. + maxSnmpBulkWalkConcurrency: 5 + # list of kubernetes secrets name that will be used for polling # https://splunk.github.io/splunk-connect-for-snmp/main/microk8s/configuration/poller-configuration/#define-usernamesecrets usernameSecrets: [] @@ -506,6 +511,8 @@ serviceAccount: # This parameter allows to use SC4SNMP for older version of Kubernetes that doesn't support autoscaling/v2 useDeprecatedAPI: false +# A user name to set in the deployment environment. +deploymentUser: "sc4snmp" ############################################################################# ### Please do not modify below values, unless you know what you're doing! ### ############################################################################# diff --git a/docker_compose/.env b/docker_compose/.env index c2b170577..eea1d8b12 100644 --- a/docker_compose/.env +++ b/docker_compose/.env @@ -6,6 +6,7 @@ TRAPS_CONFIG_FILE_ABSOLUTE_PATH= INVENTORY_FILE_ABSOLUTE_PATH= COREFILE_ABS_PATH= SC4SNMP_VERSION="1.14.1" +DEPLOYMENT_USER= # Network configuration COREDNS_ADDRESS=172.28.0.255 @@ -54,6 +55,7 @@ WORKER_DISABLE_MONGO_DEBUG_LOGGING=true UDP_CONNECTION_TIMEOUT=3 MAX_OID_TO_PROCESS=70 MAX_REPETITIONS=10 +MAX_SNMP_BULK_WALK_CONCURRENCY=5 # Worker Poller WORKER_POLLER_CONCURRENCY=4 diff --git a/docker_compose/docker-compose.yaml b/docker_compose/docker-compose.yaml index 8a180acff..8a58082e3 100644 --- a/docker_compose/docker-compose.yaml +++ b/docker_compose/docker-compose.yaml @@ -6,6 +6,7 @@ x-general_sc4snmp_data: &general_sc4snmp_data MIB_SOURCES: http://snmp-mibserver:8000/asn1/@mib@ MIB_INDEX: http://snmp-mibserver:8000/index.csv MIB_STANDARD: http://snmp-mibserver:8000/standard.txt + USER: ${DEPLOYMENT_USER:-sc4snmp} x-splunk_general_setup: &splunk_general_setup SPLUNK_HEC_HOST: ${SPLUNK_HEC_HOST} @@ -37,6 +38,7 @@ x-workers_general_setup: &workers_general_setup UDP_CONNECTION_TIMEOUT: ${UDP_CONNECTION_TIMEOUT:-3} MAX_OID_TO_PROCESS: ${MAX_OID_TO_PROCESS:-70} MAX_REPETITIONS: ${MAX_REPETITIONS:-10} + MAX_SNMP_BULK_WALK_CONCURRENCY: ${MAX_SNMP_BULK_WALK_CONCURRENCY:-5} PROFILES_RELOAD_DELAY: ${PROFILES_RELOAD_DELAY:-60} x-ipv6: &ipv6 diff --git a/docs/dockercompose/6-env-file-configuration.md b/docs/dockercompose/6-env-file-configuration.md index 96e0c7976..7bf6eb869 100644 --- a/docs/dockercompose/6-env-file-configuration.md +++ b/docs/dockercompose/6-env-file-configuration.md @@ -13,6 +13,7 @@ Inside the directory with the docker compose files, there is a `.env`. Variables | `INVENTORY_FILE_ABSOLUTE_PATH` | Absolute path to [inventory.csv](./3-inventory-configuration.md) file | | `COREFILE_ABS_PATH` | Absolute path to Corefile used by coreDNS. Default Corefile can be found inside the `docker_compose` | | `SC4SNMP_VERSION` | Version of SC4SNMP | +| `DEPLOYMENT_USER` | A user name to set in the deployment environment. The default value is sc4snmp. | ## Network configuration @@ -66,7 +67,7 @@ Inside the directory with the docker compose files, there is a `.env`. Variables ### General | Variable | Description | -|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| +|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `WALK_RETRY_MAX_INTERVAL` | Maximum time interval between walk attempts | | `WALK_MAX_RETRIES` | Maximum number of walk retries | | `METRICS_INDEXING_ENABLED` | Details can be found in [append oid index part to the metrics](../microk8s/configuration/poller-configuration.md#append-oid-index-part-to-the-metrics) | @@ -75,6 +76,9 @@ Inside the directory with the docker compose files, there is a `.env`. Variables | `WORKER_LOG_LEVEL` | Logging level of the workers, possible options: DEBUG, INFO, WARNING, ERROR, CRITICAL, or FATAL | | `UDP_CONNECTION_TIMEOUT` | Timeout in seconds for SNMP operations | | `MAX_OID_TO_PROCESS` | Sometimes SNMP Agent cannot accept more than X OIDs per once, so if the error "TooBig" is visible in logs, decrease the number of MAX_OID_TO_PROCESS | +| `MAX_REPETITIONS` | The amount of requested next oids in response for each of varbinds in one request sent | +| `MAX_SNMP_BULK_WALK_CONCURRENCY` | The maximum number of concurrent SNMP bulk walks (bulk_walk_cmd) that can be executed at the same time. Default is 5. Setting this control determines the number of OID subtrees are walked in parallel and helps prevent overwhelming the target device or network. | + | | `MAX_REPETITIONS` | The amount of requested next oids in response for each of varbinds in one request sent | ### Worker Poller diff --git a/docs/microk8s/configuration/deployment-configuration.md b/docs/microk8s/configuration/deployment-configuration.md index b04de7d6e..33769880e 100644 --- a/docs/microk8s/configuration/deployment-configuration.md +++ b/docs/microk8s/configuration/deployment-configuration.md @@ -11,6 +11,19 @@ microk8s helm3 inspect values splunk-connect-for-snmp/splunk-connect-for-snmp > The whole file is divided into the following parts: +To configure `deploymentUser`: + +- The `deploymentUser` configuration is kept in the `values.yaml` file as a global (top-level) parameter. +- `values.yaml` is used during the installation process for configuring Kubernetes values. + +- This parameter defines a user name to set in the deployment environment. The default value is sc4snmp. + +See the following deploymentUser example configuration: + +```yaml + deploymentUser: "user1" +``` + To configure the endpoint for sending SNMP data: - `splunk` - in case you use Splunk Enterprise/Cloud. diff --git a/docs/microk8s/configuration/poller-configuration.md b/docs/microk8s/configuration/poller-configuration.md index 1c6af9045..d16d665c0 100644 --- a/docs/microk8s/configuration/poller-configuration.md +++ b/docs/microk8s/configuration/poller-configuration.md @@ -42,6 +42,20 @@ When IPv6 is enabled and device is dual stack, the hostname resolution will try The log level for poller can be set by changing the value for the key `logLevel`. The allowed values are: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` or `FATAL`. The default value is `INFO`. +### Define maxSnmpBulkWalkConcurrency +The maximum number of concurrent SNMP bulk walks (bulk_walk_cmd) that can be executed at the same time. +Default is 5. + +`maxSnmpBulkWalkConcurrency` Setting this control determines the number of OID subtrees are walked in parallel and helps prevent overwhelming the target device or network. + +For example: + +The configured variables: +```yaml +poller: + maxSnmpBulkWalkConcurrency: 2 +``` + ### Define maxRepetitions The maxRepetitions is a parameter used in SNMP GetBulk call. It is responsible for controlling the amount of variables in one request. @@ -58,7 +72,8 @@ The configured variables: poller: maxRepetitions: 2 ``` -The requested varbinds in one getBulk call: + +The requested varbinds in one get_bulk call: ``` IP-MIB.ipNetToMediaNetAddress ``` diff --git a/integration_tests/automatic_setup_compose.sh b/integration_tests/automatic_setup_compose.sh index 1579fa751..cad93f69b 100755 --- a/integration_tests/automatic_setup_compose.sh +++ b/integration_tests/automatic_setup_compose.sh @@ -44,7 +44,9 @@ deploy_poetry() { poetry install poetry add --group dev splunk-sdk poetry add --group dev splunklib - poetry add --group dev pysnmplib + poetry add --group dev pysnmp + poetry add --group dev pytest-asyncio + poetry add --group dev pysnmpcrypto } wait_for_containers_to_be_up() { diff --git a/integration_tests/automatic_setup_microk8s.sh b/integration_tests/automatic_setup_microk8s.sh index cf0d60998..55f0df867 100755 --- a/integration_tests/automatic_setup_microk8s.sh +++ b/integration_tests/automatic_setup_microk8s.sh @@ -44,7 +44,9 @@ deploy_poetry() { poetry install poetry add --group dev splunk-sdk poetry add --group dev splunklib - poetry add --group dev pysnmplib + poetry add --group dev pysnmp + poetry add --group dev pytest-asyncio + poetry add --group dev pysnmpcrypto } wait_for_pod_initialization() { diff --git a/integration_tests/conftest.py b/integration_tests/conftest.py index f303505c9..b2e00f360 100644 --- a/integration_tests/conftest.py +++ b/integration_tests/conftest.py @@ -14,7 +14,9 @@ # limitations under the License. # ######################################################################## import logging +import os import time +from logging.handlers import RotatingFileHandler import pytest import splunklib.client as client @@ -87,3 +89,27 @@ def setup_splunk(request): raise time.sleep(1) return service + + +def pytest_configure(config): + """ + Configure rotating log files per CI matrix index and mirror pytest logs. + """ + os.makedirs("logs", exist_ok=True) + + matrix_index = os.getenv("MATRIX_INDEX", "unknown") + log_file = f"logs/integration_part_{matrix_index}.log" + + file_handler = RotatingFileHandler( + log_file, maxBytes=5 * 1024 * 1024, backupCount=10, encoding="utf-8" + ) + formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s") + file_handler.setFormatter(formatter) + + root_logger = logging.getLogger() + root_logger.setLevel(logging.DEBUG) + root_logger.addHandler(file_handler) + + logging.getLogger("pytest").setLevel(logging.DEBUG) + + print(f"Logging initialized for MATRIX_INDEX={matrix_index}, writing to {log_file}") diff --git a/integration_tests/deploy_and_test.sh b/integration_tests/deploy_and_test.sh index 258ce4437..1df746a03 100755 --- a/integration_tests/deploy_and_test.sh +++ b/integration_tests/deploy_and_test.sh @@ -91,7 +91,9 @@ deploy_poetry() { poetry install poetry add -D splunk-sdk poetry add -D splunklib - poetry add -D pysnmplib + poetry add -D pysnmp + poetry add -D pytest-asyncio + poetry add -D pysnmpcrypto } run_integration_tests() { diff --git a/integration_tests/pytest.ini b/integration_tests/pytest.ini index edbb83602..5b7af3697 100644 --- a/integration_tests/pytest.ini +++ b/integration_tests/pytest.ini @@ -1,4 +1,8 @@ [pytest] +asyncio_mode = strict +asyncio_debug = true +log_cli = true +log_cli_level = DEBUG markers = part1: marks tests as belonging to part 1 of a test suite part2: marks tests as belonging to part 2 of a test suite diff --git a/integration_tests/splunk_test_utils.py b/integration_tests/splunk_test_utils.py index cffe7b681..e83ee530b 100644 --- a/integration_tests/splunk_test_utils.py +++ b/integration_tests/splunk_test_utils.py @@ -13,7 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. # ######################################################################## +import logging import os +import re import time import ruamel @@ -295,6 +297,61 @@ def wait_for_pod_initialization_microk8s(): os.system("chmod a+x check_for_pods.sh && ./check_for_pods.sh") +def mask_ip_addresses(text): + """Mask all IPv4 and IPv6 addresses in a given string.""" + ipv4_pattern = r"\b\d{1,3}(?:\.\d{1,3}){3}\b" + ipv6_pattern = r"\b(?:[A-Fa-f0-9]{0,4}:){2,7}[A-Fa-f0-9]{0,4}\b" + masked = re.sub(ipv4_pattern, "[IPv4_MASKED]", text) + masked = re.sub(ipv6_pattern, "[IPv6_MASKED]", masked) + return masked + + +def log_poller_pod_logs(namespace="sc4snmp", logger=None): + import subprocess + + """Fetch and echo logs from poller pods (MicroK8s) or containers (Docker Compose).""" + + os.system('echo "===== ALL POLLER PODS ====="') + + list_pods_cmd = ( + f"sudo microk8s kubectl get pods -A | grep poll | awk '{{print $2}}'" + ) + os.system(list_pods_cmd) + + os.system('echo "===== STARTING POLLER LOGS ====="') + logger.info("===== STARTING POLLER LOGS =====") + logger.info(f"list_pods_cmd={list_pods_cmd}") + + pods = subprocess.getoutput(list_pods_cmd).splitlines() + if not pods: + os.system('echo " No poller pods found."') + if logger: + logger.info("===== No poller pods found. =====") + return + + for pod in pods: + os.system(f'echo "----- Logs from: {pod} -----"') + + raw_logs = subprocess.getoutput( + f"sudo microk8s kubectl logs {pod} -n {namespace}" + ) + masked_logs = mask_ip_addresses(raw_logs) + + print(raw_logs) + + if logger: + logger.info( + f"----- Logs from: {pod} -----\n{raw_logs}\n----------------------------" + ) + + os.system('echo "----------------------------"') + + os.system('echo "===== END POLLER LOGS ====="') + + if logger: + logger.info("===== End of poller logs =====") + + # if __name__ == "__main__": # update_inventory(['192.168.0.1,,2c,public,,,600,,,', # '192.168.0.2,,2c,public,,,602,,,']) diff --git a/integration_tests/test_poller_integration.py b/integration_tests/test_poller_integration.py index fbbc699e1..5a5cbd8e4 100644 --- a/integration_tests/test_poller_integration.py +++ b/integration_tests/test_poller_integration.py @@ -14,13 +14,16 @@ # limitations under the License. # ######################################################################## import logging +import os import time +from logging.handlers import RotatingFileHandler import pytest from ruamel.yaml.scalarstring import DoubleQuotedScalarString as dq from ruamel.yaml.scalarstring import SingleQuotedScalarString as sq from integration_tests.splunk_test_utils import ( + log_poller_pod_logs, splunk_single_search, update_file_microk8s, update_groups_compose, @@ -129,6 +132,7 @@ def test_static_profiles_metrics(self, setup_splunk): assert metric_count > 0 def test_static_profiles_event(self, setup_splunk): + log_poller_pod_logs(logger=logger) search_string = """search index=netops sourcetype="sc4snmp:event" "IF-MIB.ifType" AND NOT "IF-MIB.ifAdminStatus" """ logger.info("Integration test static profile - events") result_count, metric_count = run_retried_single_search( @@ -1034,6 +1038,7 @@ def setup_single_gt_and_lt_profiles(request): class TestSingleGtAndLtCorrectCondition: def test_gt_profile(self, request, setup_splunk): time.sleep(20) + log_poller_pod_logs(logger=logger) search_string = """| mpreview index=netmetrics | search profiles=gt_profile """ result_count, metric_count = run_retried_single_search( setup_splunk, search_string, 2 @@ -1043,6 +1048,7 @@ def test_gt_profile(self, request, setup_splunk): def test_lt_profile(self, request, setup_splunk): time.sleep(20) + log_poller_pod_logs(logger=logger) search_string = """| mpreview index=netmetrics | search profiles=lt_profile """ result_count, metric_count = run_retried_single_search( setup_splunk, search_string, 2 @@ -1321,6 +1327,7 @@ def setup_single_gt_and_lt_profiles_with_negation(request): class TestSingleGtAndLtWithNegationCorrectCondition: def test_not_gt_profile(self, request, setup_splunk): time.sleep(20) + log_poller_pod_logs(logger=logger) search_string = ( """| mpreview index=netmetrics | search profiles=not_gt_profile """ ) @@ -1332,6 +1339,7 @@ def test_not_gt_profile(self, request, setup_splunk): def test_not_lt_profile(self, request, setup_splunk): time.sleep(20) + log_poller_pod_logs(logger=logger) search_string = ( """| mpreview index=netmetrics | search profiles=not_lt_profile """ ) @@ -1525,6 +1533,7 @@ def setup_single_regex_and_options_profiles_with_negation(request): class TestSingleRegexWithNegationCorrectCondition: def test_not_regex_profile(self, request, setup_splunk): time.sleep(20) + log_poller_pod_logs(logger=logger) search_string = ( """| mpreview index=netmetrics | search profiles=not_regex_profile """ ) @@ -1536,6 +1545,7 @@ def test_not_regex_profile(self, request, setup_splunk): def test_not_regex_with_options_profile(self, request, setup_splunk): time.sleep(20) + log_poller_pod_logs(logger=logger) search_string = ( """| mpreview index=netmetrics | search profiles=not_options_profile """ ) @@ -1606,7 +1616,7 @@ def setup_multiple_conditions_profiles(request): ] ) upgrade_docker_compose() - time.sleep(120) + time.sleep(360) yield if deployment == "microk8s": update_file_microk8s( @@ -1623,7 +1633,7 @@ def setup_multiple_conditions_profiles(request): ] ) upgrade_docker_compose() - time.sleep(120) + time.sleep(360) @pytest.mark.usefixtures("setup_multiple_conditions_profiles") @@ -1631,6 +1641,7 @@ def setup_multiple_conditions_profiles(request): class TestMultipleCorrectConditions: def test_gt_and_equals_profile(self, request, setup_splunk): time.sleep(20) + log_poller_pod_logs(logger=logger) search_string = ( """| mpreview index=netmetrics | search profiles=gt_and_equals_profile """ ) @@ -1642,6 +1653,7 @@ def test_gt_and_equals_profile(self, request, setup_splunk): def test_lt_and_in_profile(self, request, setup_splunk): time.sleep(20) + log_poller_pod_logs(logger=logger) search_string = ( """| mpreview index=netmetrics | search profiles=lt_and_in_profile """ ) diff --git a/integration_tests/test_trap_integration.py b/integration_tests/test_trap_integration.py index 02160a759..715e81b38 100644 --- a/integration_tests/test_trap_integration.py +++ b/integration_tests/test_trap_integration.py @@ -13,11 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # ######################################################################## +import asyncio import logging -import time import pytest -from pysnmp.hlapi import * +from pysnmp.hlapi.v3arch.asyncio import * from integration_tests.splunk_test_utils import ( create_v3_secrets_compose, @@ -34,58 +34,55 @@ logger = logging.getLogger(__name__) -def send_trap( +async def send_trap( host, port, object_identity, mib_to_load, community, mp_model, *var_binds ): - iterator = sendNotification( + error_indication, error_status, error_index, varBinds = await send_notification( SnmpEngine(), CommunityData(community, mpModel=mp_model), - UdpTransportTarget((host, port)), + await UdpTransportTarget.create((host, port)), ContextData(), "trap", NotificationType(ObjectIdentity(object_identity)) - .addVarBinds(*var_binds) - .loadMibs(mib_to_load), + .add_varbinds(*var_binds) + .load_mibs(mib_to_load), ) - error_indication, error_status, error_index, var_binds = next(iterator) - if error_indication: logger.error(f"{error_indication}") -def send_v3_trap(host, port, object_identity, *var_binds): - iterator = sendNotification( +async def send_v3_trap(host, port, object_identity, *var_binds): + error_indication, error_status, error_index, varBinds = await send_notification( SnmpEngine(OctetString(hexValue="80003a8c04")), UsmUserData( - "snmp-poller", - "PASSWORD1", - "PASSWORD1", + userName="snmp-poller", + authKey="PASSWORD1", + privKey="PASSWORD1", authProtocol=(1, 3, 6, 1, 6, 3, 10, 1, 1, 3), privProtocol=(1, 3, 6, 1, 6, 3, 10, 1, 2, 4), ), - UdpTransportTarget((host, port)), + await UdpTransportTarget.create((host, port)), ContextData(), "trap", - NotificationType(ObjectIdentity(object_identity)).addVarBinds(*var_binds), + NotificationType(ObjectIdentity(object_identity)).add_varbinds(*var_binds), ) - error_indication, error_status, error_index, var_binds = next(iterator) - if error_indication: logger.error(f"{error_indication}") @pytest.mark.part6 -def test_trap_v1(request, setup_splunk): +@pytest.mark.asyncio +async def test_trap_v1(request, setup_splunk): trap_external_ip = request.config.getoption("trap_external_ip") logger.info(f"I have: {trap_external_ip}") - time.sleep(2) + await asyncio.sleep(2) # send trap varbind1 = ("1.3.6.1.6.3.1.1.4.3.0", "1.3.6.1.4.1.20408.4.1.1.2") varbind2 = ("1.3.6.1.2.1.1.4.0", OctetString("my contact")) - send_trap( + await send_trap( trap_external_ip, 162, "1.3.6.1.6.3.1.1.5.2", @@ -97,7 +94,7 @@ def test_trap_v1(request, setup_splunk): ) # wait for the message to be processed - time.sleep(5) + await asyncio.sleep(5) search_query = """search index="netops" sourcetype="sc4snmp:traps" earliest=-1m | head 1""" @@ -108,15 +105,16 @@ def test_trap_v1(request, setup_splunk): @pytest.mark.part6 -def test_trap_v2(request, setup_splunk): +@pytest.mark.asyncio +async def test_trap_v2(request, setup_splunk): trap_external_ip = request.config.getoption("trap_external_ip") logger.info(f"I have: {trap_external_ip}") - time.sleep(2) + await asyncio.sleep(2) # send trap varbind1 = ("1.3.6.1.6.3.1.1.4.3.0", "1.3.6.1.4.1.20408.4.1.1.2") varbind2 = ("1.3.6.1.2.1.1.1.0", OctetString("my system")) - send_trap( + await send_trap( trap_external_ip, 162, "1.3.6.1.6.3.1.1.5.2", @@ -128,7 +126,7 @@ def test_trap_v2(request, setup_splunk): ) # wait for the message to be processed - time.sleep(5) + await asyncio.sleep(5) search_query = """search index="netops" sourcetype="sc4snmp:traps" earliest=-1m | head 1""" @@ -139,19 +137,20 @@ def test_trap_v2(request, setup_splunk): @pytest.mark.part6 -def test_added_varbind(request, setup_splunk): +@pytest.mark.asyncio +async def test_added_varbind(request, setup_splunk): trap_external_ip = request.config.getoption("trap_external_ip") logger.info(f"I have: {trap_external_ip}") - time.sleep(2) + await asyncio.sleep(2) # send trap varbind1 = ("1.3.6.1.2.1.1.1.0", OctetString("test_added_varbind")) - send_trap( + await send_trap( trap_external_ip, 162, "1.3.6.1.2.1.2.1", "SNMPv2-MIB", "public", 1, varbind1 ) # wait for the message to be processed - time.sleep(5) + await asyncio.sleep(5) search_query = ( """search index="netops" "SNMPv2-MIB.sysDescr.value"="test_added_varbind" """ @@ -163,15 +162,16 @@ def test_added_varbind(request, setup_splunk): @pytest.mark.part6 -def test_many_traps(request, setup_splunk): +@pytest.mark.asyncio +async def test_many_traps(request, setup_splunk): trap_external_ip = request.config.getoption("trap_external_ip") logger.info(f"I have: {trap_external_ip}") - time.sleep(2) + await asyncio.sleep(2) # send trap varbind1 = ("1.3.6.1.2.1.1.1.0", OctetString("test_many_traps")) for _ in range(5): - send_trap( + await send_trap( trap_external_ip, 162, "1.3.6.1.2.1.2.1", @@ -182,7 +182,7 @@ def test_many_traps(request, setup_splunk): ) # wait for the message to be processed - time.sleep(2) + await asyncio.sleep(5) search_query = ( """search index="netops" "SNMPv2-MIB.sysDescr.value"="test_many_traps" """ @@ -194,15 +194,16 @@ def test_many_traps(request, setup_splunk): @pytest.mark.part6 -def test_more_than_one_varbind(request, setup_splunk): +@pytest.mark.asyncio +async def test_more_than_one_varbind(request, setup_splunk): trap_external_ip = request.config.getoption("trap_external_ip") logger.info(f"I have: {trap_external_ip}") - time.sleep(2) + await asyncio.sleep(2) # send trap varbind1 = ("1.3.6.1.2.1.1.4.0", OctetString("test_more_than_one_varbind_contact")) varbind2 = ("1.3.6.1.2.1.1.1.0", OctetString("test_more_than_one_varbind")) - send_trap( + await send_trap( trap_external_ip, 162, "1.3.6.1.2.1.2.1", @@ -214,7 +215,7 @@ def test_more_than_one_varbind(request, setup_splunk): ) # wait for the message to be processed - time.sleep(2) + await asyncio.sleep(2) search_query = """search index="netops" | search "SNMPv2-MIB.sysDescr.value"="test_more_than_one_varbind" "SNMPv2-MIB.sysContact.value"=test_more_than_one_varbind_contact """ @@ -225,14 +226,15 @@ def test_more_than_one_varbind(request, setup_splunk): @pytest.mark.part6 -def test_loading_mibs(request, setup_splunk): +@pytest.mark.asyncio +async def test_loading_mibs(request, setup_splunk): trap_external_ip = request.config.getoption("trap_external_ip") logger.info(f"I have: {trap_external_ip}") - time.sleep(2) + await asyncio.sleep(2) # send trap varbind1 = ("1.3.6.1.6.3.1.1.4.1.0", "1.3.6.1.4.1.15597.1.1.1.1.0.1") - send_trap( + await send_trap( trap_external_ip, 162, "1.3.6.1.4.1.15597.1.1.1.1", @@ -243,7 +245,7 @@ def test_loading_mibs(request, setup_splunk): ) # wait for the message to be processed - time.sleep(2) + await asyncio.sleep(2) search_query = """search index=netops "SNMPv2-MIB.snmpTrapOID.value"="AVAMAR-MCS-MIB::eventTrap" """ @@ -253,7 +255,8 @@ def test_loading_mibs(request, setup_splunk): @pytest.mark.part6 -def test_trap_v3(request, setup_splunk): +@pytest.mark.asyncio +async def test_trap_v3(request, setup_splunk): trap_external_ip = request.config.getoption("trap_external_ip") deployment = request.config.getoption("sc4snmp_deployment") if deployment == "microk8s": @@ -269,13 +272,13 @@ def test_trap_v3(request, setup_splunk): wait_for_pod_initialization_microk8s() else: wait_for_containers_initialization() - time.sleep(15) + await asyncio.sleep(20) # send trap varbind1 = ("1.3.6.1.2.1.1.4.0", OctetString("test_trap_v3")) - send_v3_trap(trap_external_ip, 162, "1.3.6.1.2.1.1.0", varbind1) + await send_v3_trap(trap_external_ip, 162, "1.3.6.1.2.1.1.0", varbind1) # wait for the message to be processed - time.sleep(2) + await asyncio.sleep(5) search_query = ( """search index=netops "SNMPv2-MIB.sysContact.value"="test_trap_v3" """ diff --git a/poetry.lock b/poetry.lock index 3f3dad23f..20cae6265 100644 --- a/poetry.lock +++ b/poetry.lock @@ -15,19 +15,6 @@ files = [ [package.dependencies] vine = ">=5.0.0,<6.0.0" -[[package]] -name = "async-timeout" -version = "5.0.1" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.8" -groups = ["main"] -markers = "python_full_version < \"3.11.3\"" -files = [ - {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"}, - {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"}, -] - [[package]] name = "attrs" version = "25.3.0" @@ -108,7 +95,6 @@ files = [ [package.dependencies] attrs = ">=24.3.0" -exceptiongroup = {version = ">=1.1.1", markers = "python_version < \"3.11\""} typing-extensions = ">=4.12.2" [package.extras] @@ -213,6 +199,104 @@ files = [ {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, ] +[[package]] +name = "cffi" +version = "2.0.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + [[package]] name = "charset-normalizer" version = "3.4.2" @@ -472,11 +556,85 @@ files = [ {file = "coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27"}, ] +[package.extras] +toml = ["tomli ; python_full_version <= \"3.11.0a6\""] + +[[package]] +name = "cryptography" +version = "46.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = "!=3.9.0,!=3.9.1,>=3.8" +groups = ["main"] +files = [ + {file = "cryptography-46.0.2-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3e32ab7dd1b1ef67b9232c4cf5e2ee4cd517d4316ea910acaaa9c5712a1c663"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1fd1a69086926b623ef8126b4c33d5399ce9e2f3fac07c9c734c2a4ec38b6d02"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb7fb9cd44c2582aa5990cf61a4183e6f54eea3172e54963787ba47287edd135"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9066cfd7f146f291869a9898b01df1c9b0e314bfa182cef432043f13fc462c92"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:97e83bf4f2f2c084d8dd792d13841d0a9b241643151686010866bbd076b19659"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:4a766d2a5d8127364fd936572c6e6757682fc5dfcbdba1632d4554943199f2fa"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fab8f805e9675e61ed8538f192aad70500fa6afb33a8803932999b1049363a08"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1e3b6428a3d56043bff0bb85b41c535734204e599c1c0977e1d0f261b02f3ad5"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:1a88634851d9b8de8bb53726f4300ab191d3b2f42595e2581a54b26aba71b7cc"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:be939b99d4e091eec9a2bcf41aaf8f351f312cd19ff74b5c83480f08a8a43e0b"}, + {file = "cryptography-46.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f13b040649bc18e7eb37936009b24fd31ca095a5c647be8bb6aaf1761142bd1"}, + {file = "cryptography-46.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bdc25e4e01b261a8fda4e98618f1c9515febcecebc9566ddf4a70c63967043b"}, + {file = "cryptography-46.0.2-cp311-abi3-win32.whl", hash = "sha256:8b9bf67b11ef9e28f4d78ff88b04ed0929fcd0e4f70bb0f704cfc32a5c6311ee"}, + {file = "cryptography-46.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:758cfc7f4c38c5c5274b55a57ef1910107436f4ae842478c4989abbd24bd5acb"}, + {file = "cryptography-46.0.2-cp311-abi3-win_arm64.whl", hash = "sha256:218abd64a2e72f8472c2102febb596793347a3e65fafbb4ad50519969da44470"}, + {file = "cryptography-46.0.2-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:bda55e8dbe8533937956c996beaa20266a8eca3570402e52ae52ed60de1faca8"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7155c0b004e936d381b15425273aee1cebc94f879c0ce82b0d7fecbf755d53a"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a61c154cc5488272a6c4b86e8d5beff4639cdb173d75325ce464d723cda0052b"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:9ec3f2e2173f36a9679d3b06d3d01121ab9b57c979de1e6a244b98d51fea1b20"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2fafb6aa24e702bbf74de4cb23bfa2c3beb7ab7683a299062b69724c92e0fa73"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:0c7ffe8c9b1fcbb07a26d7c9fa5e857c2fe80d72d7b9e0353dcf1d2180ae60ee"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5840f05518caa86b09d23f8b9405a7b6d5400085aa14a72a98fdf5cf1568c0d2"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:27c53b4f6a682a1b645fbf1cd5058c72cf2f5aeba7d74314c36838c7cbc06e0f"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:512c0250065e0a6b286b2db4bbcc2e67d810acd53eb81733e71314340366279e"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:07c0eb6657c0e9cca5891f4e35081dbf985c8131825e21d99b4f440a8f496f36"}, + {file = "cryptography-46.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48b983089378f50cba258f7f7aa28198c3f6e13e607eaf10472c26320332ca9a"}, + {file = "cryptography-46.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e6f6775eaaa08c0eec73e301f7592f4367ccde5e4e4df8e58320f2ebf161ea2c"}, + {file = "cryptography-46.0.2-cp314-cp314t-win32.whl", hash = "sha256:e8633996579961f9b5a3008683344c2558d38420029d3c0bc7ff77c17949a4e1"}, + {file = "cryptography-46.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:48c01988ecbb32979bb98731f5c2b2f79042a6c58cc9a319c8c2f9987c7f68f9"}, + {file = "cryptography-46.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:8e2ad4d1a5899b7caa3a450e33ee2734be7cc0689010964703a7c4bcc8dd4fd0"}, + {file = "cryptography-46.0.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a08e7401a94c002e79dc3bc5231b6558cd4b2280ee525c4673f650a37e2c7685"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d30bc11d35743bf4ddf76674a0a369ec8a21f87aaa09b0661b04c5f6c46e8d7b"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bca3f0ce67e5a2a2cf524e86f44697c4323a86e0fd7ba857de1c30d52c11ede1"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ff798ad7a957a5021dcbab78dfff681f0cf15744d0e6af62bd6746984d9c9e9c"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cb5e8daac840e8879407acbe689a174f5ebaf344a062f8918e526824eb5d97af"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:3f37aa12b2d91e157827d90ce78f6180f0c02319468a0aea86ab5a9566da644b"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e38f203160a48b93010b07493c15f2babb4e0f2319bbd001885adb3f3696d21"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d19f5f48883752b5ab34cff9e2f7e4a7f216296f33714e77d1beb03d108632b6"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:04911b149eae142ccd8c9a68892a70c21613864afb47aba92d8c7ed9cc001023"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:8b16c1ede6a937c291d41176934268e4ccac2c6521c69d3f5961c5a1e11e039e"}, + {file = "cryptography-46.0.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:747b6f4a4a23d5a215aadd1d0b12233b4119c4313df83ab4137631d43672cc90"}, + {file = "cryptography-46.0.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b275e398ab3a7905e168c036aad54b5969d63d3d9099a0a66cc147a3cc983be"}, + {file = "cryptography-46.0.2-cp38-abi3-win32.whl", hash = "sha256:0b507c8e033307e37af61cb9f7159b416173bdf5b41d11c4df2e499a1d8e007c"}, + {file = "cryptography-46.0.2-cp38-abi3-win_amd64.whl", hash = "sha256:f9b2dc7668418fb6f221e4bf701f716e05e8eadb4f1988a2487b11aedf8abe62"}, + {file = "cryptography-46.0.2-cp38-abi3-win_arm64.whl", hash = "sha256:91447f2b17e83c9e0c89f133119d83f94ce6e0fb55dd47da0a959316e6e9cfa1"}, + {file = "cryptography-46.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f25a41f5b34b371a06dad3f01799706631331adc7d6c05253f5bca22068c7a34"}, + {file = "cryptography-46.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e12b61e0b86611e3f4c1756686d9086c1d36e6fd15326f5658112ad1f1cc8807"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1d3b3edd145953832e09607986f2bd86f85d1dc9c48ced41808b18009d9f30e5"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fe245cf4a73c20592f0f48da39748b3513db114465be78f0a36da847221bd1b4"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2b9cad9cf71d0c45566624ff76654e9bae5f8a25970c250a26ccfc73f8553e2d"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9bd26f2f75a925fdf5e0a446c0de2714f17819bf560b44b7480e4dd632ad6c46"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:7282d8f092b5be7172d6472f29b0631f39f18512a3642aefe52c3c0e0ccfad5a"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c4b93af7920cdf80f71650769464ccf1fb49a4b56ae0024173c24c48eb6b1612"}, + {file = "cryptography-46.0.2.tar.gz", hash = "sha256:21b6fc8c71a3f9a604f028a329e5560009cc4a3a828bfea5fcba8eb7647d88fe"}, +] + [package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} +cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""} [package.extras] -toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] +docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] +nox = ["nox[uv] (>=2024.4.15)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] +sdist = ["build (>=1.0.0)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test-randomorder = ["pytest-randomly"] [[package]] name = "dnspython" @@ -499,25 +657,6 @@ idna = ["idna (>=3.7)"] trio = ["trio (>=0.23)"] wmi = ["wmi (>=1.5.1)"] -[[package]] -name = "exceptiongroup" -version = "1.3.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["main", "dev"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "flower" version = "2.0.1" @@ -647,7 +786,7 @@ version = "3.1.6" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -914,7 +1053,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" -groups = ["dev"] +groups = ["main", "dev"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -1408,54 +1547,28 @@ files = [ wcwidth = "*" [[package]] -name = "pycryptodomex" -version = "3.23.0" -description = "Cryptographic library for Python" +name = "pyasn1" +version = "0.6.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, +] + +[[package]] +name = "pycparser" +version = "2.23" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" groups = ["main"] +markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" files = [ - {file = "pycryptodomex-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7"}, - {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e"}, - {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541"}, - {file = "pycryptodomex-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648"}, - {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08"}, - {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708"}, - {file = "pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9"}, - {file = "pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51"}, - {file = "pycryptodomex-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea"}, - {file = "pycryptodomex-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66"}, - {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5"}, - {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798"}, - {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f"}, - {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea"}, - {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe"}, - {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785"}, - {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9"}, - {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a"}, - {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff"}, - {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245"}, - {file = "pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da"}, + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, ] [[package]] @@ -1671,53 +1784,57 @@ all = ["filelock (>=3.0)", "redis (>=3.3,<4.0)", "redis-py-cluster (>=2.1.3,<3.0 docs = ["furo (>=2022.3.4,<2023.0.0)", "myst-parser (>=0.17)", "sphinx (>=4.3.0,<5.0.0)", "sphinx-autodoc-typehints (>=1.17,<2.0)", "sphinx-copybutton (>=0.5)", "sphinxcontrib-apidoc (>=0.3,<0.4)"] [[package]] -name = "pysnmp-pyasn1" -version = "1.1.3" -description = "ASN.1 types and codecs" +name = "pysmi" +version = "1.6.2" +description = "A pure-Python implementation of SNMP/SMI MIB parsing and conversion library." optional = false -python-versions = ">=3.8,<4.0" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pysnmp-pyasn1-1.1.3.tar.gz", hash = "sha256:fc559133ec6717e9d96dd4bd69c981310b23364dc2280a9b5f40f684fb6b4b8a"}, - {file = "pysnmp_pyasn1-1.1.3-py3-none-any.whl", hash = "sha256:d9a471b058adb9f2c3ce3aa85f800f2beef1a86c03b08d182a5653c9880fbd5e"}, + {file = "pysmi-1.6.2-py3-none-any.whl", hash = "sha256:45a3a3b25b9e0465e6a49e47ba70d5eab0424f6f1131ca406ca3f385f027247e"}, + {file = "pysmi-1.6.2.tar.gz", hash = "sha256:abed01673113886d10f0f336426859238fc13b5383c7e28e13dbcd5af0443ba1"}, ] +[package.dependencies] +Jinja2 = ">=3.1.3" +ply = ">=3.11" +requests = ">=2.26.0" + +[package.extras] +dev = ["black (==22.3.0)", "bump2version (>=1.0.1)", "doc8 (>=1.1.1)", "flake8 (>=5.0.4)", "flake8-docstrings (>=1.7.0)", "flake8-import-order (>=0.18.2)", "flake8-rst-docstrings (>=0.3.0)", "furo (>=2023.1.1)", "isort (>=5.10.1)", "pep8-naming (>=0.14.1)", "pre-commit (==2.21.0)", "pysnmp (>=7.1.16)", "pytest (>=6.2.5)", "pytest-cov (>=3.0.0)", "sphinx (>=7.0.0,<8.0.0)", "sphinx-copybutton (>=0.5.2)", "sphinx-notfound-page (>=1.0.0)", "sphinx-sitemap-lextudio (>=2.5.2)"] + [[package]] -name = "pysnmp-pysmi" -version = "1.1.12" -description = "" +name = "pysnmp" +version = "7.1.21" +description = "A Python library for SNMP" optional = false -python-versions = "<4.0,>=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pysnmp_pysmi-1.1.12-py3-none-any.whl", hash = "sha256:a868be988f6578323a8b7eb7c8be7a8a539b061de90b8c2b5ddfdc8f9d376ba2"}, - {file = "pysnmp_pysmi-1.1.12.tar.gz", hash = "sha256:7d12ee12fb99c08449318430ce1b1633f6eee5d938e880215f41cc9976ecdcbb"}, + {file = "pysnmp-7.1.21-py3-none-any.whl", hash = "sha256:e3fed6eab479a4ec7baf8fa1583666ce46401cf1c3ce3c4e2b0be55e5d454c75"}, + {file = "pysnmp-7.1.21.tar.gz", hash = "sha256:d5fa54cf2021af1c93a439eec66ce716fc8df425c55ecc7ed5bca9f35e8145b2"}, ] [package.dependencies] -ply = ">=3.11,<4.0" -requests = ">=2.31.0,<3.0.0" +pyasn1 = ">=0.4.8,<0.5.0 || >0.5.0" + +[package.extras] +dev = ["Sphinx (>=7.0.0,<8.0.0)", "bump2version (>=1.0.1)", "codecov (>=2.1.12)", "cryptography (>=44.0.1)", "doc8 (>=1.0.0)", "flake8 (>=7.0.0)", "furo (>=2023.1.1)", "jinja2 (>=3.1.6)", "pep8-naming (>=0.14.1)", "pre-commit (==2.21.0)", "pysmi (>=1.6.1)", "pytest (>=7.2.0)", "pytest-asyncio (>=0.21.1)", "pytest-codecov (>=0.4.0)", "pytest-cov (>=4.1.0)", "ruff (>=0.11.7)", "sphinx-copybutton (>=0.5.2)", "sphinx-notfound-page (>=1.0.0)", "sphinx-polyversion (>=1.0.0)", "sphinx-sitemap-lextudio (>=2.5.2)"] [[package]] -name = "pysnmplib" -version = "5.0.24" -description = "" +name = "pysnmpcrypto" +version = "0.1.0" +description = "Strong cryptography support for PySNMP (SNMP library for Python)" optional = false -python-versions = "^3.8" +python-versions = "<4.0,>=3.8" groups = ["main"] -files = [] -develop = false +files = [ + {file = "pysnmpcrypto-0.1.0-py3-none-any.whl", hash = "sha256:85ee5013d62aaf8c511b842689c964e4fdda0367aa5df89d5ece789350e7f4fb"}, + {file = "pysnmpcrypto-0.1.0.tar.gz", hash = "sha256:ee6819247ceee09aa2de54b998c51ccf595d75a9ccc2e303383b39a5ac63816c"}, +] [package.dependencies] -pycryptodomex = "^3.11.0" -pysnmp-pyasn1 = "^1.1.3" -pysnmp-pysmi = "^1.0.4" - -[package.source] -type = "git" -url = "https://github.com/pysnmp/pysnmp.git" -reference = "main" -resolved_reference = "4891556e7db831a5a9b27d4bad8ff102609b2a2c" +cryptography = ">=43.0.1" [[package]] name = "pytest" @@ -1733,16 +1850,34 @@ files = [ [package.dependencies] colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} iniconfig = ">=1" packaging = ">=20" pluggy = ">=1.5,<2" pygments = ">=2.7.2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "pytest-cov" version = "6.1.1" @@ -1894,9 +2029,6 @@ files = [ {file = "redis-6.2.0.tar.gz", hash = "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977"}, ] -[package.dependencies] -async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\""} - [package.extras] hiredis = ["hiredis (>=3.2.0)"] jwt = ["pyjwt (>=2.9.0)"] @@ -2250,49 +2382,6 @@ all = ["tornado (>=4.0)", "twisted"] tornado = ["tornado (>=4.0)"] twisted = ["twisted"] -[[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["dev"] -markers = "python_full_version <= \"3.11.0a6\"" -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] - [[package]] name = "tornado" version = "6.5.1" @@ -2326,7 +2415,7 @@ files = [ {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] -markers = {dev = "python_version == \"3.10\""} +markers = {dev = "python_version == \"3.12\""} [[package]] name = "tzdata" @@ -2599,5 +2688,5 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" -python-versions = ">=3.10,<3.12" -content-hash = "1f0b0fd25a8222113dfded6c013d08b5186e14b65080429666d1f90f73a59490" +python-versions = ">=3.12,<3.14" +content-hash = "31f71e3e9868ed7fba83e757479c5805209568c9de7ccc8650632e8b5801832c" diff --git a/pyproject.toml b/pyproject.toml index e6ae2785f..428386e26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ testpaths = ["test"] python_files = ["test_*.py"] [tool.poetry.dependencies] -python = ">=3.10,<3.12" +python = ">=3.12,<3.14" pymongo = {extras = ["srv"], version = "^4.0.0"} requests = {extras = ["crypto"], version = "^2.31.0"} celery = {extras = ["tblib"], version = "5.5.3"} @@ -43,10 +43,12 @@ mongolock = "^1.3.4" pika = "^1.2.0" JSON-log-formatter ="^1.0.0" "ruamel.yaml" = "^0.18.0" -pysnmplib = {git = "https://github.com/pysnmp/pysnmp.git", branch = "main"} urllib3 = "^2.0.0" jsonschema = "4.24.0" flower = "^2.0.1" +pysnmp = "^7.1.21" +pysnmpcrypto = "^0.1.0" +pysmi = "^1.6.2" [tool.poetry.group.dev.dependencies] pytest = "^8.0.0" @@ -56,6 +58,7 @@ mkdocs = "^1.2.2" mkdocs-material = "^9.0.0" python-dotenv = "^1.0.0" mkdocs-video = "^1.5.0" +pytest-asyncio = "^1.2.0" [build-system] requires = ["poetry>=0.12"] diff --git a/rendered/manifests/tests/splunk-connect-for-snmp/templates/scheduler/deployment.yaml b/rendered/manifests/tests/splunk-connect-for-snmp/templates/scheduler/deployment.yaml index 40c223461..73365edbc 100644 --- a/rendered/manifests/tests/splunk-connect-for-snmp/templates/scheduler/deployment.yaml +++ b/rendered/manifests/tests/splunk-connect-for-snmp/templates/scheduler/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "beat", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL diff --git a/rendered/manifests/tests/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/rendered/manifests/tests/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 6851a86ec..19541d40e 100644 --- a/rendered/manifests/tests/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/rendered/manifests/tests/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -31,5 +31,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: "sc4snmp" restartPolicy: Never diff --git a/rendered/manifests/tests/splunk-connect-for-snmp/templates/traps/deployment.yaml b/rendered/manifests/tests/splunk-connect-for-snmp/templates/traps/deployment.yaml index 65c9b834f..d6aa70e51 100644 --- a/rendered/manifests/tests/splunk-connect-for-snmp/templates/traps/deployment.yaml +++ b/rendered/manifests/tests/splunk-connect-for-snmp/templates/traps/deployment.yaml @@ -42,6 +42,8 @@ spec: "trap" ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: CELERY_BROKER_URL diff --git a/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml b/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml index 01dfdcb78..a94d9dd9c 100644 --- a/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml +++ b/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-poller", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index fe08ce4ed..a6efc574f 100644 --- a/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml b/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml index 82675bad1..004020304 100644 --- a/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml +++ b/rendered/manifests/tests/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-trap", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/scheduler/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/scheduler/deployment.yaml index 40c223461..73365edbc 100644 --- a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/scheduler/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/scheduler/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "beat", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL diff --git a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 6851a86ec..19541d40e 100644 --- a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -31,5 +31,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: "sc4snmp" restartPolicy: Never diff --git a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/traps/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/traps/deployment.yaml index e66ea7f05..6f0ad238e 100644 --- a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/traps/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/traps/deployment.yaml @@ -41,6 +41,8 @@ spec: "trap" ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: CELERY_BROKER_URL diff --git a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml index 8a1867d9f..8930bc3f7 100644 --- a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml @@ -41,6 +41,8 @@ spec: "celery", "worker-poller", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -69,6 +71,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index f89d4ca52..cddabc8b2 100644 --- a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -41,6 +41,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -69,6 +71,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml index 4fbae94b8..971b60673 100644 --- a/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml @@ -41,6 +41,8 @@ spec: "celery", "worker-trap", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -69,6 +71,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/scheduler/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/scheduler/deployment.yaml index 40c223461..73365edbc 100644 --- a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/scheduler/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/scheduler/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "beat", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL diff --git a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 6851a86ec..19541d40e 100644 --- a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -31,5 +31,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: "sc4snmp" restartPolicy: Never diff --git a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/traps/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/traps/deployment.yaml index e66ea7f05..6f0ad238e 100644 --- a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/traps/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/traps/deployment.yaml @@ -41,6 +41,8 @@ spec: "trap" ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: CELERY_BROKER_URL diff --git a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml index 8a1867d9f..8930bc3f7 100644 --- a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml @@ -41,6 +41,8 @@ spec: "celery", "worker-poller", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -69,6 +71,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index f89d4ca52..cddabc8b2 100644 --- a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -41,6 +41,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -69,6 +71,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml index 4fbae94b8..971b60673 100644 --- a/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml +++ b/rendered/manifests/tests_autoscaling_enabled_deprecated/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml @@ -41,6 +41,8 @@ spec: "celery", "worker-trap", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -69,6 +71,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/scheduler/deployment.yaml b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/scheduler/deployment.yaml index 40c223461..73365edbc 100644 --- a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/scheduler/deployment.yaml +++ b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/scheduler/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "beat", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL diff --git a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 6851a86ec..19541d40e 100644 --- a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -31,5 +31,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: "sc4snmp" restartPolicy: Never diff --git a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/traps/deployment.yaml b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/traps/deployment.yaml index 65c9b834f..d6aa70e51 100644 --- a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/traps/deployment.yaml +++ b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/traps/deployment.yaml @@ -42,6 +42,8 @@ spec: "trap" ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: CELERY_BROKER_URL diff --git a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml index 01dfdcb78..a94d9dd9c 100644 --- a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml +++ b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-poller", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index fe08ce4ed..a6efc574f 100644 --- a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml index 82675bad1..004020304 100644 --- a/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml +++ b/rendered/manifests/tests_enable_ui/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-trap", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/scheduler/deployment.yaml b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/scheduler/deployment.yaml index 40c223461..73365edbc 100644 --- a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/scheduler/deployment.yaml +++ b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/scheduler/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "beat", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL diff --git a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 6851a86ec..19541d40e 100644 --- a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -31,5 +31,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: "sc4snmp" restartPolicy: Never diff --git a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/traps/deployment.yaml b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/traps/deployment.yaml index 65c9b834f..d6aa70e51 100644 --- a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/traps/deployment.yaml +++ b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/traps/deployment.yaml @@ -42,6 +42,8 @@ spec: "trap" ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: CELERY_BROKER_URL diff --git a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml index 01dfdcb78..a94d9dd9c 100644 --- a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml +++ b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-poller", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index fe08ce4ed..a6efc574f 100644 --- a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml index 82675bad1..004020304 100644 --- a/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml +++ b/rendered/manifests/tests_mongodb_custom_image/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-trap", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/scheduler/deployment.yaml b/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/scheduler/deployment.yaml index 40c223461..73365edbc 100644 --- a/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/scheduler/deployment.yaml +++ b/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/scheduler/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "beat", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL diff --git a/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 6851a86ec..19541d40e 100644 --- a/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -31,5 +31,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: "sc4snmp" restartPolicy: Never diff --git a/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml b/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml index 01dfdcb78..a94d9dd9c 100644 --- a/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml +++ b/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-poller", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index fe08ce4ed..a6efc574f 100644 --- a/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/rendered/manifests/tests_only_polling/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 6851a86ec..19541d40e 100644 --- a/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -31,5 +31,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: "sc4snmp" restartPolicy: Never diff --git a/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/traps/deployment.yaml b/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/traps/deployment.yaml index 65c9b834f..d6aa70e51 100644 --- a/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/traps/deployment.yaml +++ b/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/traps/deployment.yaml @@ -42,6 +42,8 @@ spec: "trap" ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: CELERY_BROKER_URL diff --git a/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index fe08ce4ed..a6efc574f 100644 --- a/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml b/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml index 82675bad1..004020304 100644 --- a/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml +++ b/rendered/manifests/tests_only_traps/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-trap", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/scheduler/deployment.yaml b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/scheduler/deployment.yaml index 40c223461..73365edbc 100644 --- a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/scheduler/deployment.yaml +++ b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/scheduler/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "beat", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL diff --git a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/tests/test-connection.yaml b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/tests/test-connection.yaml index 6851a86ec..19541d40e 100644 --- a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/tests/test-connection.yaml +++ b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/tests/test-connection.yaml @@ -31,5 +31,8 @@ spec: memory: 128Mi requests: cpu: 100m - memory: 128Mi + memory: 128Mi + env: + - name: USER + value: "sc4snmp" restartPolicy: Never diff --git a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/traps/deployment.yaml b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/traps/deployment.yaml index 65c9b834f..d6aa70e51 100644 --- a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/traps/deployment.yaml +++ b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/traps/deployment.yaml @@ -42,6 +42,8 @@ spec: "trap" ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: CELERY_BROKER_URL diff --git a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml index 61b4fa2de..1731fe52e 100644 --- a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml +++ b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/poller/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-poller", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml index 2235659a2..8704e01a5 100644 --- a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml +++ b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/sender/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-sender", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml index b699b77f8..75fd56e5d 100644 --- a/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml +++ b/rendered/manifests/tests_probes_enabled/splunk-connect-for-snmp/templates/worker/trap/deployment.yaml @@ -42,6 +42,8 @@ spec: "celery", "worker-trap", ] env: + - name: USER + value: "sc4snmp" - name: CONFIG_PATH value: /app/config/config.yaml - name: REDIS_URL @@ -70,6 +72,8 @@ spec: value: "70" - name: MAX_REPETITIONS value: "10" + - name: MAX_SNMP_BULK_WALK_CONCURRENCY + value: "5" - name: PYSNMP_DEBUG value: "" - name: PROFILES_RELOAD_DELAY diff --git a/splunk_connect_for_snmp/snmp/auth.py b/splunk_connect_for_snmp/snmp/auth.py index cf89bc297..57b15de0c 100644 --- a/splunk_connect_for_snmp/snmp/auth.py +++ b/splunk_connect_for_snmp/snmp/auth.py @@ -18,14 +18,14 @@ from ipaddress import ip_address from typing import Any, Dict, Union -from pysnmp.hlapi import ( +from pysnmp.hlapi.asyncio import ( CommunityData, ContextData, SnmpEngine, Udp6TransportTarget, UdpTransportTarget, UsmUserData, - getCmd, + get_cmd, ) from pysnmp.proto.api.v2c import OctetString from pysnmp.smi.rfc1902 import ObjectIdentity, ObjectType @@ -56,14 +56,14 @@ def get_secret_value( # To discover remote SNMP EngineID we will tap on SNMP engine inner workings # by setting up execution point observer setup on INTERNAL class PDU processing # -def get_security_engine_id(logger, ir: InventoryRecord, snmp_engine: SnmpEngine): +async def get_security_engine_id(logger, ir: InventoryRecord, snmp_engine: SnmpEngine): observer_context: Dict[Any, Any] = {} - transport_target = setup_transport_target(ir) + transport_target = await setup_transport_target(ir) # Register a callback to be invoked at specified execution point of # SNMP Engine and passed local variables at execution point's local scope - snmp_engine.observer.registerObserver( + snmp_engine.observer.register_observer( lambda e, p, v, c: c.update(securityEngineId=v["securityEngineId"]), "rfc3412.prepareDataElements:internal", cbCtx=observer_context, @@ -72,14 +72,12 @@ def get_security_engine_id(logger, ir: InventoryRecord, snmp_engine: SnmpEngine) # Send probe SNMP request with invalid credentials auth_data = UsmUserData("non-existing-user") - error_indication, _, _, _ = next( - getCmd( - snmp_engine, - auth_data, - transport_target, - ContextData(), - ObjectType(ObjectIdentity("SNMPv2-MIB", "sysDescr", 0)), - ) + error_indication, _, _, _ = await get_cmd( + snmp_engine, + auth_data, + transport_target, + ContextData(), + ObjectType(ObjectIdentity("SNMPv2-MIB", "sysDescr", 0)), ) # See if our SNMP engine received REPORT PDU containing securityEngineId @@ -90,14 +88,16 @@ def get_security_engine_id(logger, ir: InventoryRecord, snmp_engine: SnmpEngine) return security_engine_id -def setup_transport_target(ir): +async def setup_transport_target(ir): ip = get_ip_from_socket(ir) if IPv6_ENABLED else ir.address if IPv6_ENABLED and ip_address(ip).version == 6: - return Udp6TransportTarget( + return await Udp6TransportTarget.create( (ir.address, ir.port), timeout=UDP_CONNECTION_TIMEOUT ) - return UdpTransportTarget((ir.address, ir.port), timeout=UDP_CONNECTION_TIMEOUT) + return await UdpTransportTarget.create( + (ir.address, ir.port), timeout=UDP_CONNECTION_TIMEOUT + ) def get_ip_from_socket(ir): @@ -116,7 +116,9 @@ def fetch_security_engine_id(observer_context, error_indication, ipaddress): ) -def get_auth_v3(logger, ir: InventoryRecord, snmp_engine: SnmpEngine) -> UsmUserData: +async def get_auth_v3( + logger, ir: InventoryRecord, snmp_engine: SnmpEngine +) -> UsmUserData: location = os.path.join("secrets/snmpv3", ir.secret) # type: ignore if os.path.exists(location): username = get_secret_value(location, "userName", required=True) @@ -147,7 +149,7 @@ def get_auth_v3(logger, ir: InventoryRecord, snmp_engine: SnmpEngine) -> UsmUser security_engine_id = OctetString(hexValue=ir.security_engine) logger.debug(f"Security eng from profile {security_engine_id}") else: - security_engine_id = get_security_engine_id(logger, ir, snmp_engine) + security_engine_id = await get_security_engine_id(logger, ir, snmp_engine) logger.debug(f"Security eng dynamic {security_engine_id}") security_name = None @@ -178,7 +180,7 @@ def get_auth_v1(ir: InventoryRecord) -> CommunityData: return CommunityData(ir.community, mpModel=0) -def get_auth( +async def get_auth( logger, ir: InventoryRecord, snmp_engine: SnmpEngine ) -> Union[UsmUserData, CommunityData]: if ir.version == "1": @@ -186,4 +188,4 @@ def get_auth( elif ir.version == "2c": return get_auth_v2c(ir) else: - return get_auth_v3(logger, ir, snmp_engine) + return await get_auth_v3(logger, ir, snmp_engine) diff --git a/splunk_connect_for_snmp/snmp/const.py b/splunk_connect_for_snmp/snmp/const.py index 2f1bcc3a5..979642352 100644 --- a/splunk_connect_for_snmp/snmp/const.py +++ b/splunk_connect_for_snmp/snmp/const.py @@ -16,25 +16,25 @@ from pysnmp.entity import config AuthProtocolMap = { - "MD5": config.usmHMACMD5AuthProtocol, - "SHA": config.usmHMACSHAAuthProtocol, - "SHA224": config.usmHMAC128SHA224AuthProtocol, - "SHA256": config.usmHMAC192SHA256AuthProtocol, - "SHA384": config.usmHMAC256SHA384AuthProtocol, - "SHA512": config.usmHMAC384SHA512AuthProtocol, - "NONE": config.usmNoAuthProtocol, + "MD5": config.USM_AUTH_HMAC96_MD5, + "SHA": config.USM_AUTH_HMAC96_SHA, + "SHA224": config.USM_AUTH_HMAC128_SHA224, + "SHA256": config.USM_AUTH_HMAC192_SHA256, + "SHA384": config.USM_AUTH_HMAC256_SHA384, + "SHA512": config.USM_AUTH_HMAC384_SHA512, + "NONE": config.USM_AUTH_NONE, } PrivProtocolMap = { - "DES": config.usmDESPrivProtocol, - "3DES": config.usm3DESEDEPrivProtocol, - "AES": config.usmAesCfb128Protocol, - "AES128": config.usmAesCfb128Protocol, - "AES192": config.usmAesCfb192Protocol, - "AES192BLMT": config.usmAesBlumenthalCfb192Protocol, - "AES256": config.usmAesCfb256Protocol, - "AES256BLMT": config.usmAesBlumenthalCfb256Protocol, - "NONE": config.usmNoPrivProtocol, + "DES": config.USM_PRIV_CBC56_DES, + "3DES": config.USM_PRIV_CBC168_3DES, + "AES": config.USM_PRIV_CFB128_AES, + "AES128": config.USM_PRIV_CFB128_AES, + "AES192": config.USM_PRIV_CFB192_AES, + "AES192BLMT": config.USM_PRIV_CFB192_AES_BLUMENTHAL, + "AES256": config.USM_PRIV_CFB256_AES, + "AES256BLMT": config.USM_PRIV_CFB256_AES_BLUMENTHAL, + "NONE": config.USM_PRIV_NONE, } DEFAULT_POLLING_FREQUENCY = 60 diff --git a/splunk_connect_for_snmp/snmp/context.py b/splunk_connect_for_snmp/snmp/context.py index 1e1dc0d0d..ca6d61e5a 100644 --- a/splunk_connect_for_snmp/snmp/context.py +++ b/splunk_connect_for_snmp/snmp/context.py @@ -14,8 +14,8 @@ # limitations under the License. # -from pysnmp.hlapi import ContextData +from pysnmp.hlapi.asyncio import ContextData def get_context_data() -> ContextData: - return ContextData(None, "") + return ContextData(contextEngineId=None, contextName=b"") diff --git a/splunk_connect_for_snmp/snmp/manager.py b/splunk_connect_for_snmp/snmp/manager.py index bdc05f6bd..5323f46fa 100644 --- a/splunk_connect_for_snmp/snmp/manager.py +++ b/splunk_connect_for_snmp/snmp/manager.py @@ -14,10 +14,14 @@ # limitations under the License. # import typing +from asyncio import Queue, QueueEmpty, TaskGroup +from collections import defaultdict from contextlib import suppress from pysnmp.proto.errind import EmptyResponse from pysnmp.smi import error +from pysnmp.smi.builder import MibBuilder +from pysnmp.smi.error import SmiError from requests import Session from splunk_connect_for_snmp.common.collection_manager import ProfilesManager @@ -38,7 +42,7 @@ import pymongo from celery import Task from celery.utils.log import get_task_logger -from pysnmp.hlapi import SnmpEngine, bulkCmd, getCmd +from pysnmp.hlapi.asyncio import SnmpEngine, bulk_walk_cmd, get_cmd from pysnmp.smi import compiler, view from pysnmp.smi.rfc1902 import ObjectIdentity, ObjectType from requests_cache import MongoCache @@ -65,6 +69,7 @@ MAX_OID_TO_PROCESS = int(os.getenv("MAX_OID_TO_PROCESS", 70)) PYSNMP_DEBUG = os.getenv("PYSNMP_DEBUG", "") MAX_REPETITIONS = int(os.getenv("MAX_REPETITIONS", 10)) +MAX_SNMP_BULK_WALK_CONCURRENCY = int(os.getenv("MAX_SNMP_BULK_WALK_CONCURRENCY", 5)) DEFAULT_STANDARD_MIBS = [ "HOST-RESOURCES-MIB", @@ -126,15 +131,15 @@ def get_inventory(mongo_inventory, address): def _any_failure_happened( - error_indication, error_status, error_index, varbinds: list, address, walk + error_indication, error_status, error_index, varbinds: tuple, address, walk ) -> bool: """ This function checks if any failure happened during GET or BULK operation. - @param error_indication: - @param error_status: - @param error_index: index of varbind where error appeared - @param varbinds: list of varbinds - @return: if any failure happened + :param error_indication: + :param error_status: + :param error_index: index of varbind where error appeared + :param varbinds: a sequential tuple of varbinds + :return: if any failure happened """ if error_indication: if isinstance(error_indication, EmptyResponse) and IGNORE_EMPTY_VARBINDS: @@ -266,6 +271,110 @@ def extract_indexes(index): return indexes_to_return +def get_max_bulk_walk_concurrency(count: int) -> int: + """ + Return the effective bulk concurrency, Default to 5 if not set. + + :param count: Desired integer number of concurrent SNMP operations (bulk_walk_cmd) + (count determined based on the lenght of bulk varbinds) + + :return int: The concurrency to use for SNMP operation + """ + if count < MAX_SNMP_BULK_WALK_CONCURRENCY: + return count + return MAX_SNMP_BULK_WALK_CONCURRENCY + + +def patch_inet_address_classes(mib_builder: MibBuilder) -> bool: + """ + Adjust InetAddress classes for compatibility with legacy length-prefixed + OID index decoding used by earlier PySNMP versions (RFC 4001). + + ## NOTE + In current pysmp, the INET-ADDRESS-MIB definitions for + InetAddressIPv4, InetAddressIPv6, InetAddressIPv4z, and InetAddressIPv6z + include a `fixed_length` attribute, e.g.: + + class InetAddressIPv4(TextualConvention, OctetString): + subtypeSpec = OctetString.subtypeSpec + ConstraintsUnion( + ValueSizeConstraint(4, 4), + ) + fixed_length = 4 + + Older pysnmp releases did **not** define `fixed_length`. When present, + the SNMP `setFromName()` method takes the *fixed-length* branch: + + elif obj.is_fixed_length(): + fixed_length = obj.get_fixed_length() + return obj.clone(tuple(value[:fixed_length])), value[fixed_length:] + + instead of the *length-prefixed* branch required by RFC 4001: + + else: + return obj.clone(tuple(value[1:value[0]+1])), value[value[0]+1:] + + This changes how address-based table indices (e.g. **IP-MIB::ipAddressTable**) + are parsed. + + Example: + OID index: 1.3.6.1.2.1.4.34.1.3.1.4.127.0.0.1 + Expected: ipAddressIfIndex.ipv4."127.0.0.1" + + Index portion: (1, 4, 127, 0, 0, 1) + |---| |-----------| + | |__ Address octets + |__ Length prefix (4) + + With `fixed_length = 4`: + --> Parsed as (4,127,0,0) + leftover (1) + --> Raises “Excessive instance identifier sub-OIDs left …” + + Without `fixed_length`: + - Correctly parses (127,0,0,1) as the IPv4 address. + + Fix: + This patch disables the `fixed_length` attribute. + + :param mib_builder: MibBuilder that has loaded INET-ADDRESS-MIB. + :return: True if patch applied successfully, False otherwise. + """ + + try: + (InetAddressIPv4, InetAddressIPv6, InetAddressIPv4z, InetAddressIPv6z) = ( + mib_builder.import_symbols( + "INET-ADDRESS-MIB", + "InetAddressIPv4", + "InetAddressIPv6", + "InetAddressIPv4z", + "InetAddressIPv6z", + ) + ) + + logger.info("Applying InetAddress monkey patch for lextudio pysnmp v7.x bug...") + + classes_to_patch = [ + ("InetAddressIPv4", InetAddressIPv4), + ("InetAddressIPv6", InetAddressIPv6), + ("InetAddressIPv4z", InetAddressIPv4z), + ("InetAddressIPv6z", InetAddressIPv6z), + ] + + for class_name, cls in classes_to_patch: + cls.fixed_length = None + logger.info(f"Removed the problematic fixed_length attribute {class_name}") + + logger.info("All InetAddress classes successfully patched") + return True + + except ImportError as e: + logger.info(f"Could not import INET-ADDRESS-MIB for patching: {e}") + return False + + except Exception as e: + logger.info(f"Unexpected error while patching InetAddress classes: {e}") + return False + + class Poller(Task): def __init__(self, **kwargs): self.standard_mibs = [] @@ -291,13 +400,13 @@ def __init__(self, **kwargs): self.last_modified = time.time() self.snmpEngine = SnmpEngine() self.already_loaded_mibs = set() - self.builder = self.snmpEngine.getMibBuilder() + self.builder = self.snmpEngine.get_mib_builder() self.mib_view_controller = view.MibViewController(self.builder) - compiler.addMibCompiler(self.builder, sources=[MIB_SOURCES]) + compiler.add_mib_compiler(self.builder, sources=[MIB_SOURCES]) for mib in DEFAULT_STANDARD_MIBS: self.standard_mibs.append(mib) - self.builder.loadModules(mib) + self.builder.load_modules(mib) mib_response = self.session.get(f"{MIB_INDEX}") self.mib_map = {} @@ -310,15 +419,32 @@ def __init__(self, **kwargs): logger.debug(f"Loaded {len(self.mib_map.keys())} mib map entries") else: logger.error( - f"Unable to load mib map from index http error {self.mib_response.status_code}" + f"Unable to load mib map from index http error {mib_response.status_code}" ) - def do_work( + async def do_work( self, ir: InventoryRecord, walk: bool = False, profiles: Union[List[str], None] = None, ): + """ + ## NOTE + - When a task arrived at poll queue starts with a fresh SnmpEngine (which has no transport_dispatcher + attached), SNMP requests (get_cmd or bulk_walk_cmd or any other) run normally. + - if a later task finds that the SnmpEngine already has a transport_dispatcher, it reuse that transport_dispatcher. + this causes SNMP requests to hang infinite time. + - If this hang occurs, then as per our Celery configuration, any task that + remains in the queue longer than the default 2400s will be forcefully + hard-timed-out and discarded. + - The issue does not always appear on the alternate task but it may happen + on the second, third, or any subsequent task, depending on timing and + concurrency. + + The only way to eliminate this hang is to create new SnmpEngine for each poll task. + + """ + snmpEngine = SnmpEngine() retry = False address = transform_address_to_key(ir.address, ir.port) logger.info(f"Preparing task for {ir.address}") @@ -333,10 +459,10 @@ def do_work( address, walk=walk, profiles=profiles ) - auth_data = get_auth(logger, ir, self.snmpEngine) + auth_data = await get_auth(logger, ir, snmpEngine) context_data = get_context_data() - transport = setup_transport_target(ir) + transport = await setup_transport_target(ir) metrics: Dict[str, Any] = {} if not varbinds_get and not varbinds_bulk: @@ -344,7 +470,7 @@ def do_work( return False, {} if varbinds_bulk: - self.run_bulk_request( + await self.run_bulk_request( address, auth_data, bulk_mapping, @@ -357,7 +483,7 @@ def do_work( ) if varbinds_get: - self.run_get_request( + await self.run_get_request( address, auth_data, context_data, @@ -377,7 +503,7 @@ def do_work( return retry, metrics - def run_get_request( + async def run_get_request( self, address, auth_data, @@ -391,25 +517,32 @@ def run_get_request( ): # some devices cannot process more OID than X, so it is necessary to divide it on chunks for varbind_chunk in self.get_varbind_chunk(varbinds_get, MAX_OID_TO_PROCESS): - for ( + try: + (error_indication, error_status, error_index, varbind_table) = ( + await get_cmd( + SnmpEngine(), + auth_data, + transport, + context_data, + *varbind_chunk, + lookupMib=True, + ) + ) + except Exception as e: + logger.exception(f"Error while performing get_cmd: {e}") + continue + + if not _any_failure_happened( error_indication, error_status, error_index, varbind_table, - ) in getCmd( - self.snmpEngine, auth_data, transport, context_data, *varbind_chunk + ir.address, + walk, ): - if not _any_failure_happened( - error_indication, - error_status, - error_index, - varbind_table, - ir.address, - walk, - ): - self.process_snmp_data(varbind_table, metrics, address, get_mapping) + self.process_snmp_data(varbind_table, metrics, address, get_mapping) - def run_bulk_request( + async def run_bulk_request( self, address, auth_data, @@ -421,38 +554,121 @@ def run_bulk_request( varbinds_bulk, walk, ): - for ( - error_indication, - error_status, - error_index, - varbind_table, - ) in bulkCmd( - self.snmpEngine, - auth_data, - transport, - context_data, - 0, - MAX_REPETITIONS, - *varbinds_bulk, - lexicographicMode=False, - ignoreNonIncreasingOid=is_increasing_oids_ignored(ir.address, ir.port), - ): - if not _any_failure_happened( - error_indication, - error_status, - error_index, - varbind_table, - ir.address, - walk, - ): - _, tmp_mibs, _ = self.process_snmp_data( - varbind_table, metrics, address, bulk_mapping - ) - if tmp_mibs: - self.load_mibs(tmp_mibs) - self.process_snmp_data( - varbind_table, metrics, address, bulk_mapping + """ + Perform asynchronous SNMP BULK requests on multiple varbinds with concurrency control. + + This function uses bulk_walk_cmd to asynchronously walk each SNMP varbind, + ensuring that at most `MAX_SNMP_BULK_WALK_CONCURRENCY` walks are running concurrently. + It processes the received SNMP data, and handles failure if any. + + :param address: IP address of the SNMP device to query + :param auth_data: SNMP authentication data + :param bulk_mapping: mapping dictionary to process SNMP metrics + :param context_data: SNMP ContextData object + :param ir: object containing SNMP device info + :param metrics: dictionary to store metrics collected from SNMP responses + :param transport: SNMP transport target + :param varbinds_bulk: set of SNMP varbinds to query + :param walk: boolean flag indicating if it is a walk operation + + :return: + + ## NOTE + - The current `bulk_cmd` of PySNMP does not support the `lexicographicMode` option. + As a result, the walk is not strictly confined to the requested varBind subtree and may go beyond the requested OID subtree, + with a high chance of duplicate OIDs. + + - Used `bulk_walk_cmd` of pysnmp, which supports `lexicographicMode` and walks a subtree correctly, + but handles only one varBind at a time. + """ + + async def _walk_single_varbind(varbind, wid): + """ + Asynchronously walk a single SNMP varbind and process the results. + :param varbind: SNMP ObjectType varbind to be walked + :return: + """ + try: + async for ( + error_indication, + error_status, + error_index, + varbind_table, + ) in bulk_walk_cmd( + SnmpEngine(), + auth_data, + transport, + context_data, + 0, + MAX_REPETITIONS, + varbind, + lexicographicMode=False, + lookupMib=True, + ignoreNonIncreasingOid=is_increasing_oids_ignored( + ir.address, ir.port + ), + ): + if not _any_failure_happened( + error_indication, + error_status, + error_index, + varbind_table, + ir.address, + walk, + ): + _, tmp_mibs, _ = self.process_snmp_data( + varbind_table, metrics, address, bulk_mapping + ) + if tmp_mibs: + self.load_mibs(tmp_mibs) + self.process_snmp_data( + varbind_table, metrics, address, bulk_mapping + ) + except Exception as e: + logger.exception(f"Error while performing bulk_walk_cmd: {e}") + + # Preparing the queue for bulk request + bulk_queue: Queue[tuple[int, ObjectType]] = Queue() + for _wid, _varbind in enumerate(varbinds_bulk, start=1): + bulk_queue.put_nowait((_wid, _varbind)) + + async def _worker(worker_id: int): + """ + Worker coroutine that continuously fetches tasks from the bulk_queue and + executes SNMP walks using _walk_single_varbind. + :param worker_id: integer ID of the worker + + :return: + """ + while True: + try: + _wid, _varbind = bulk_queue.get_nowait() + except QueueEmpty: + # Queue is empty indicating that all the varbinds proceed for the poll/walk task. + logger.debug( + f"BulkQueue worker-{worker_id} found no tasks in the queue and is exiting." + ) + break + except Exception as e: + logger.error( + f"BulkQueue worker-{worker_id} encountered an error: {e}." ) + break + + logger.debug(f"BulkQueue worker-{worker_id} picking up task-{_wid}") + await _walk_single_varbind(_varbind, worker_id) + bulk_queue.task_done() + logger.debug(f"BulkQueue worker-{worker_id} completed task-{_wid}") + + try: + async with TaskGroup() as tg: + for wid in range( + 1, get_max_bulk_walk_concurrency(len(varbinds_bulk)) + 1 + ): + tg.create_task(_worker(wid)) + except ExceptionGroup as eg: + for e in eg.exceptions: + raise e def get_varbind_chunk(self, lst, n): for i in range(0, len(lst), n): @@ -463,7 +679,7 @@ def load_mibs(self, mibs: List[str]) -> None: for mib in mibs: if mib: try: - self.builder.loadModules(mib) + self.builder.load_modules(mib) except Exception as e: logger.warning(f"Error loading mib for {mib}, {e}") @@ -517,6 +733,7 @@ def process_snmp_data(self, varbind_table, metrics, target, mapping={}): retry = False remotemibs = [] for varbind in varbind_table: + index, metric, mib, oid, varbind_id = self.init_snmp_data(varbind) if is_mib_resolved(varbind_id): @@ -629,7 +846,41 @@ def handle_groupkey_without_metrics(self, group_key, index, mapping, metrics): metrics[group_key]["profiles"] = [] def init_snmp_data(self, varbind): - mib, metric, index = varbind[0].getMibSymbol() - varbind_id = varbind[0].prettyPrint() - oid = str(varbind[0].getOid()) + """ + Extract SNMP varbind information in a way that preserves compatibility with + older PySNMP behavior while avoiding changes to the underlying library. + + :param varbind: ObjectType + + :return: A resolved index, metric, mib, oid, varbind_id + + ## NOTE + - In old fork of pysnmp, calling `getMibSymbol()` and `prettyPrint()` on + a varbind returned fully resolved MIB names, variable names, and indices. + + - In lextudio's pysnmp, `get_mib_symbol()` and `prettyPrint()` + by default may return partially resolved names as snmp request it self not + resolved it fully, unless `resolve_with_mib()` is explicitly called. + This is why `metric` and `varbind_id` appear different from older versions. + """ + oid = str(varbind[0]) + + try: + resolved_oid = ObjectIdentity(oid).resolve_with_mib( + self.mib_view_controller + ) + mib, metric, index = resolved_oid.get_mib_symbol() + varbind_id = resolved_oid.prettyPrint() + except SmiError as se: + logger.info(f"===> SmiError for oid={oid}: {se} <===") + patch_inet_address_classes(self.builder) + resolved_oid = ObjectIdentity(oid).resolve_with_mib( + self.mib_view_controller + ) + mib, metric, index = resolved_oid.get_mib_symbol() + varbind_id = resolved_oid.prettyPrint() + logger.info( + f"===== {mib}, oid={oid}, varbind_id={varbind_id}, metric={metric} val={varbind[1]} =====" + ) + return index, metric, mib, oid, varbind_id diff --git a/splunk_connect_for_snmp/snmp/tasks.py b/splunk_connect_for_snmp/snmp/tasks.py index 03bc2d238..8be8771a2 100644 --- a/splunk_connect_for_snmp/snmp/tasks.py +++ b/splunk_connect_for_snmp/snmp/tasks.py @@ -15,6 +15,7 @@ # import logging import re +from asyncio import run from contextlib import suppress from pysnmp.smi.error import SmiError @@ -55,22 +56,7 @@ IPv6_ENABLED = human_bool(os.getenv("IPv6_ENABLED", "false").lower()) -@shared_task( - bind=True, - base=Poller, - retry_backoff=30, - retry_backoff_max=WALK_RETRY_MAX_INTERVAL, - max_retries=WALK_MAX_RETRIES, - autoretry_for=( - MongoLockLocked, - SnmpActionError, - ), - throws=( - SnmpActionError, - SnmpActionError, - ), -) -def walk(self, **kwargs): +async def walk_async_wrapper(self, **kwargs): address = kwargs["address"] profile = kwargs.get("profile", []) group = kwargs.get("group") @@ -84,7 +70,7 @@ def walk(self, **kwargs): ir = get_inventory(mongo_inventory, address) retry = True while retry: - retry, result = self.do_work(ir, walk=True, profiles=profile) + retry, result = await self.do_work(ir, walk=True, profiles=profile) # After a Walk tell schedule to recalc work = { @@ -102,15 +88,23 @@ def walk(self, **kwargs): @shared_task( bind=True, base=Poller, - default_retry_delay=5, - max_retries=3, - retry_backoff=True, - retry_backoff_max=1, - retry_jitter=True, - expires=30, + retry_backoff=30, + retry_backoff_max=WALK_RETRY_MAX_INTERVAL, + max_retries=WALK_MAX_RETRIES, + autoretry_for=( + MongoLockLocked, + SnmpActionError, + ), + throws=( + SnmpActionError, + SnmpActionError, + ), ) -def poll(self, **kwargs): +def walk(self, **kwargs): + return run(walk_async_wrapper(self, **kwargs)) + +async def poll_async_wrapper(self, **kwargs): address = kwargs["address"] profiles = kwargs["profiles"] group = kwargs.get("group") @@ -119,7 +113,7 @@ def poll(self, **kwargs): mongo_inventory = mongo_db.inventory ir = get_inventory(mongo_inventory, address) - _, result = self.do_work(ir, profiles=profiles) + _, result = await self.do_work(ir, profiles=profiles) # After a Walk tell schedule to recalc work = { @@ -135,6 +129,20 @@ def poll(self, **kwargs): return work +@shared_task( + bind=True, + base=Poller, + default_retry_delay=5, + max_retries=3, + retry_backoff=True, + retry_backoff_max=1, + retry_jitter=True, + expires=30, +) +def poll(self, **kwargs): + return run(poll_async_wrapper(self, **kwargs)) + + @ttl_lru_cache(maxsize=MAX_DNS_CACHE_SIZE_TRAPS, ttl=TTL_DNS_CACHE_TRAPS) def resolve_address(address: str): try: @@ -169,7 +177,7 @@ def trap(self, work): return _build_result(result, work["host"], fields) -def _process_work_data(self, work, varbind_table, not_translated_oids): +def _process_work_data(self: Poller, work, varbind_table, not_translated_oids): """Process the data in work to populate varbinds.""" for w in work["data"]: if OID_VALIDATOR.match(w[1]): @@ -177,7 +185,7 @@ def _process_work_data(self, work, varbind_table, not_translated_oids): try: varbind_table.append( - ObjectType(ObjectIdentity(w[0]), w[1]).resolveWithMib( + ObjectType(ObjectIdentity(w[0]), w[1]).resolve_with_mib( self.mib_view_controller ) ) @@ -185,7 +193,7 @@ def _process_work_data(self, work, varbind_table, not_translated_oids): not_translated_oids.append((w[0], w[1])) -def _load_mib_if_needed(self, oid, host): +def _load_mib_if_needed(self: Poller, oid, host): """Load the MIB if it is known and not already loaded.""" with suppress(Exception): found, mib = self.is_mib_known(oid, oid, host) @@ -195,7 +203,7 @@ def _load_mib_if_needed(self, oid, host): def _process_remaining_oids( - self, not_translated_oids, remotemibs, remaining_oids, host, varbind_table + self: Poller, not_translated_oids, remotemibs, remaining_oids, host, varbind_table ): """Process OIDs that could not be translated and add them to other oids.""" for oid in not_translated_oids: @@ -210,12 +218,12 @@ def _process_remaining_oids( _resolve_remaining_oids(self, remaining_oids, varbind_table) -def _resolve_remaining_oids(self, remaining_oids, varbind_table): +def _resolve_remaining_oids(self: Poller, remaining_oids, varbind_table): """Resolve remaining OIDs.""" for w in remaining_oids: try: varbind_table.append( - ObjectType(ObjectIdentity(w[0]), w[1]).resolveWithMib( + ObjectType(ObjectIdentity(w[0]), w[1]).resolve_with_mib( self.mib_view_controller ) ) diff --git a/splunk_connect_for_snmp/traps.py b/splunk_connect_for_snmp/traps.py index a1629cdda..049f38ac5 100644 --- a/splunk_connect_for_snmp/traps.py +++ b/splunk_connect_for_snmp/traps.py @@ -138,13 +138,18 @@ def decode_security_context(hexstr: bytes) -> str | None: # Callback function for receiving notifications # noinspection PyUnusedLocal def cb_fun( - snmp_engine, state_reference, context_engine_id, context_name, varbinds, cb_ctx + snmp_engine: engine.SnmpEngine, + state_reference, + context_engine_id, + context_name, + varbinds, + cb_ctx, ): logger.debug( 'Notification from ContextEngineId "%s", ContextName "%s"' % (context_engine_id.prettyPrint(), context_name.prettyPrint()) ) - exec_context = snmp_engine.observer.getExecutionContext( + exec_context = snmp_engine.observer.get_execution_context( "rfc3412.receiveMessage:request" ) @@ -188,18 +193,18 @@ def authentication_observer_cb_fun(snmp_engine, execpoint, variables, contexts): ) -def add_communities(config_base, snmp_engine): +def add_communities(config_base: dict, snmp_engine: engine.SnmpEngine): idx = 0 if "communities" in config_base: if "2c" in config_base["communities"]: for community in config_base["communities"]["2c"]: idx += 1 - config.addV1System(snmp_engine, idx, community) + config.add_v1_system(snmp_engine, str(idx), community) if "1" in config_base["communities"] or 1 in config_base["communities"]: v = config_base["communities"].get("1", config_base["communities"].get(1)) for community in v: idx += 1 - config.addV1System(snmp_engine, idx, community) + config.add_v1_system(snmp_engine, str(idx), community) def main(): @@ -213,7 +218,7 @@ def main(): # Register a callback function to log errors with traps authentication observer_context: Dict[Any, Any] = {} - snmp_engine.observer.registerObserver( + snmp_engine.observer.register_observer( authentication_observer_cb_fun, "rfc2576.prepareDataElements:sm-failure", "rfc3412.prepareDataElements:sm-failure", @@ -222,17 +227,17 @@ def main(): # UDP socket over IPv6 listens also for IPv4 if IPv6_ENABLED: - config.addTransport( + config.add_transport( snmp_engine, - udp6.domainName, - udp6.Udp6Transport().openServerMode(("::", 2162)), + udp6.DOMAIN_NAME, + udp6.Udp6Transport().open_server_mode(("::", 2162)), ) else: # UDP over IPv4, first listening interface/port - config.addTransport( + config.add_transport( snmp_engine, - udp.domainName, - udp.UdpTransport().openServerMode(("0.0.0.0", 2162)), + udp.DOMAIN_NAME, + udp.UdpTransport().open_server_mode(("0.0.0.0", 2162)), ) with open(CONFIG_PATH, encoding="utf-8") as file: @@ -261,13 +266,13 @@ def main(): priv_protocol = PrivProtocolMap.get(priv_protocol.upper(), "NONE") for security_engine_id in SECURITY_ENGINE_ID_LIST: - config.addV3User( + config.add_v3_user( snmp_engine, userName=username, authProtocol=auth_protocol, - authKey=auth_key, + authKey=auth_key if auth_key else None, privProtocol=priv_protocol, - privKey=priv_key, + privKey=priv_key if priv_key else None, securityEngineId=v2c.OctetString(hexValue=security_engine_id), ) logger.debug( diff --git a/splunk_connect_for_snmp/walk.py b/splunk_connect_for_snmp/walk.py index 386dab603..67b40fb86 100644 --- a/splunk_connect_for_snmp/walk.py +++ b/splunk_connect_for_snmp/walk.py @@ -15,6 +15,7 @@ # import logging import sys +from asyncio import run from csv import DictReader from splunk_connect_for_snmp.common.inventory_record import InventoryRecord @@ -30,7 +31,7 @@ logger.addHandler(handler) -def run_walk(): +async def run_walk(): poller = Poller(no_mongo=True) with open("inventory.csv", encoding="utf-8") as csv_file: @@ -44,11 +45,11 @@ def run_walk(): ir = InventoryRecord(**source_record) retry = True while retry: - retry, result = poller.do_work(ir, walk=True) + retry, result = await poller.do_work(ir, walk=True) logger.debug(result) except Exception as e: logger.exception(e) if __name__ == "__main__": - run_walk() + run(run_walk()) diff --git a/test/snmp/test_auth.py b/test/snmp/test_auth.py index 9281227ef..549a4eaea 100644 --- a/test/snmp/test_auth.py +++ b/test/snmp/test_auth.py @@ -1,11 +1,11 @@ -from unittest import TestCase -from unittest.mock import MagicMock, Mock, mock_open, patch +from unittest import IsolatedAsyncioTestCase +from unittest.mock import AsyncMock, Mock, mock_open, patch from pysnmp.entity.config import ( - usmAesBlumenthalCfb192Protocol, - usmHMAC128SHA224AuthProtocol, - usmNoAuthProtocol, - usmNoPrivProtocol, + USM_AUTH_HMAC128_SHA224, + USM_AUTH_NONE, + USM_PRIV_CFB192_AES_BLUMENTHAL, + USM_PRIV_NONE, ) from pysnmp.proto.rfc1902 import OctetString @@ -41,7 +41,7 @@ ) -class TestAuth(TestCase): +class TestAuth(IsolatedAsyncioTestCase): @patch("builtins.open", new_callable=mock_open, read_data=mock_value) @patch("os.path.exists") def test_get_secret_value_exists(self, m_exists, m_open): @@ -66,9 +66,9 @@ def test_get_secret_value_default(self, m_exists): value = get_secret_value("/location", "key", default="default value") self.assertEqual("default value", value) - @patch("splunk_connect_for_snmp.snmp.auth.getCmd") + @patch("splunk_connect_for_snmp.snmp.auth.get_cmd", new_callable=AsyncMock) @patch("splunk_connect_for_snmp.snmp.auth.fetch_security_engine_id") - def test_get_security_engine_id_not_present(self, m_fetch, m_get_cmd): + async def test_get_security_engine_id_not_present(self, m_fetch, m_get_cmd): ir2 = InventoryRecord( **{ "address": "192.168.0.1", @@ -87,24 +87,20 @@ def test_get_security_engine_id_not_present(self, m_fetch, m_get_cmd): snmpEngine = Mock() logger = Mock() - m_get_cmd.return_value = iter( - [(None, 0, 0, "Oid1"), (None, 0, 0, "Oid2"), (None, 0, 0, "Oid3")] - ) + m_get_cmd.return_value = (None, 0, 0, ["Oid1", "Oid2", "Oid3"]) m_fetch.side_effect = Exception("boom") with self.assertRaises(Exception) as e: - get_security_engine_id(logger, ir2, snmpEngine) + await get_security_engine_id(logger, ir2, snmpEngine) self.assertEqual("boom", e.exception.args[0]) - - calls = snmpEngine.observer.registerObserver.call_args_list + calls = snmpEngine.observer.register_observer.call_args_list self.assertEqual("rfc3412.prepareDataElements:internal", calls[0].args[1]) - m_get_cmd.assert_called() - @patch("splunk_connect_for_snmp.snmp.auth.getCmd") + @patch("splunk_connect_for_snmp.snmp.auth.get_cmd", new_callable=AsyncMock) @patch("splunk_connect_for_snmp.snmp.auth.fetch_security_engine_id") - def test_get_security_engine_id(self, m_fetch, m_get_cmd): + async def test_get_security_engine_id(self, m_fetch, m_get_cmd): ir2 = InventoryRecord( **{ "address": "192.168.0.1", @@ -121,17 +117,16 @@ def test_get_security_engine_id(self, m_fetch, m_get_cmd): ) snmpEngine = Mock() + snmpEngine.observer = Mock() + snmpEngine.observer.register_observer = Mock() + logger = Mock() m_fetch.return_value = "My test value" + m_get_cmd.return_value = (None, 0, 0, ["Oid1", "Oid2", "Oid3"]) - m_get_cmd.return_value = iter( - [(None, 0, 0, "Oid1"), (None, 0, 0, "Oid2"), (None, 0, 0, "Oid3")] - ) - - result = get_security_engine_id(logger, ir2, snmpEngine) - - calls = snmpEngine.observer.registerObserver.call_args_list + result = await get_security_engine_id(logger, ir2, snmpEngine) + calls = snmpEngine.observer.register_observer.call_args_list self.assertEqual("rfc3412.prepareDataElements:internal", calls[0].args[1]) m_get_cmd.assert_called() @@ -153,7 +148,7 @@ def test_fetch_security_engine_id_missing(self): @patch("os.path.exists") @patch("splunk_connect_for_snmp.snmp.auth.get_secret_value") - def test_get_auth_v3(self, m_get_secret_value, m_exists): + async def test_get_auth_v3(self, m_get_secret_value, m_exists): m_exists.return_value = True m_get_secret_value.side_effect = [ "secret1", @@ -167,14 +162,14 @@ def test_get_auth_v3(self, m_get_secret_value, m_exists): logger = Mock() snmpEngine = Mock() - result = get_auth_v3(logger, ir, snmpEngine) + result = await get_auth_v3(logger, ir, snmpEngine) security_engine_result = OctetString(hexValue="80003a8c04") self.assertEqual("secret1", result.userName) - self.assertEqual("secret2", result.authKey) - self.assertEqual("secret3", result.privKey) - self.assertEqual("authPriv", result.securityLevel) - self.assertEqual(usmHMAC128SHA224AuthProtocol, result.authProtocol) - self.assertEqual(usmAesBlumenthalCfb192Protocol, result.privProtocol) + self.assertEqual("secret2", result.authentication_key) + self.assertEqual("secret3", result.privacy_key) + self.assertEqual("authPriv", result.security_level) + self.assertEqual(USM_AUTH_HMAC128_SHA224, result.authentication_protocol) + self.assertEqual(USM_PRIV_CFB192_AES_BLUMENTHAL, result.privacy_protocol) self.assertEqual(security_engine_result._value, result.securityEngineId._value) self.assertEqual("secret1", result.securityName) self.assertEqual(1, result.authKeyType) @@ -183,7 +178,7 @@ def test_get_auth_v3(self, m_get_secret_value, m_exists): @patch("os.path.exists") @patch("splunk_connect_for_snmp.snmp.auth.get_secret_value") @patch("splunk_connect_for_snmp.snmp.auth.get_security_engine_id") - def test_get_auth_v3_security_engine_not_str( + async def test_get_auth_v3_security_engine_not_str( self, m_get_security_engine_id, m_get_secret_value, m_exists ): m_exists.return_value = True @@ -215,16 +210,16 @@ def test_get_auth_v3_security_engine_not_str( } ) - result = get_auth_v3(logger, ir2, snmpEngine) + result = await get_auth_v3(logger, ir2, snmpEngine) m_get_security_engine_id.assert_called() self.assertEqual("secret1", result.userName) - self.assertEqual("secret2", result.authKey) - self.assertEqual("secret3", result.privKey) - self.assertEqual("authPriv", result.securityLevel) - self.assertEqual(usmHMAC128SHA224AuthProtocol, result.authProtocol) - self.assertEqual(usmAesBlumenthalCfb192Protocol, result.privProtocol) + self.assertEqual("secret2", result.authentication_key) + self.assertEqual("secret3", result.privacy_key) + self.assertEqual("authPriv", result.security_level) + self.assertEqual(USM_AUTH_HMAC128_SHA224, result.authentication_protocol) + self.assertEqual(USM_PRIV_CFB192_AES_BLUMENTHAL, result.privacy_protocol) self.assertEqual("ENGINE123", result.securityEngineId) self.assertEqual("secret1", result.securityName) self.assertEqual(1, result.authKeyType) @@ -232,7 +227,7 @@ def test_get_auth_v3_security_engine_not_str( @patch("os.path.exists") @patch("splunk_connect_for_snmp.snmp.auth.get_secret_value") - def test_get_auth_v3_exception(self, m_get_secret_value, m_exists): + async def test_get_auth_v3_exception(self, m_get_secret_value, m_exists): m_exists.return_value = False m_get_secret_value.side_effect = [ "secret1", @@ -248,12 +243,12 @@ def test_get_auth_v3_exception(self, m_get_secret_value, m_exists): snmpEngine = Mock() with self.assertRaises(Exception) as e: - get_auth_v3(logger, ir, snmpEngine) + await get_auth_v3(logger, ir, snmpEngine) self.assertEqual("invalid username from secret secret_ir", e.exception.args[0]) @patch("os.path.exists") @patch("splunk_connect_for_snmp.snmp.auth.get_secret_value") - def test_get_auth_v3_noauthnopriv(self, m_get_secret_value, m_exists): + async def test_get_auth_v3_noauthnopriv(self, m_get_secret_value, m_exists): m_exists.return_value = True m_get_secret_value.side_effect = [ "secret1", @@ -267,14 +262,14 @@ def test_get_auth_v3_noauthnopriv(self, m_get_secret_value, m_exists): logger = Mock() snmpEngine = Mock() - result = get_auth_v3(logger, ir, snmpEngine) + result = await get_auth_v3(logger, ir, snmpEngine) security_engine_result = OctetString(hexValue="80003a8c04") self.assertEqual("secret1", result.userName) - self.assertIsNone(result.authKey) - self.assertIsNone(result.privKey) - self.assertEqual("noAuthNoPriv", result.securityLevel) - self.assertEqual(usmNoAuthProtocol, result.authProtocol) - self.assertEqual(usmNoPrivProtocol, result.privProtocol) + self.assertIsNone(result.authentication_key) + self.assertIsNone(result.privacy_key) + self.assertEqual("noAuthNoPriv", result.security_level) + self.assertEqual(USM_AUTH_NONE, result.authentication_protocol) + self.assertEqual(USM_PRIV_NONE, result.privacy_protocol) self.assertEqual(security_engine_result._value, result.securityEngineId._value) self.assertEqual("secret1", result.securityName) self.assertEqual(1, result.authKeyType) @@ -282,7 +277,7 @@ def test_get_auth_v3_noauthnopriv(self, m_get_secret_value, m_exists): @patch("os.path.exists") @patch("splunk_connect_for_snmp.snmp.auth.get_secret_value") - def test_get_auth_v3_authnopriv(self, m_get_secret_value, m_exists): + async def test_get_auth_v3_authnopriv(self, m_get_secret_value, m_exists): m_exists.return_value = True m_get_secret_value.side_effect = [ "secret1", @@ -296,14 +291,14 @@ def test_get_auth_v3_authnopriv(self, m_get_secret_value, m_exists): logger = Mock() snmpEngine = Mock() - result = get_auth_v3(logger, ir, snmpEngine) + result = await get_auth_v3(logger, ir, snmpEngine) security_engine_result = OctetString(hexValue="80003a8c04") self.assertEqual("secret1", result.userName) - self.assertEqual("secret2", result.authKey) - self.assertIsNone(result.privKey) - self.assertEqual("authNoPriv", result.securityLevel) - self.assertEqual(usmHMAC128SHA224AuthProtocol, result.authProtocol) - self.assertEqual(usmNoPrivProtocol, result.privProtocol) + self.assertEqual("secret2", result.authentication_key) + self.assertIsNone(result.privacy_key) + self.assertEqual("authNoPriv", result.security_level) + self.assertEqual(USM_AUTH_HMAC128_SHA224, result.authentication_protocol) + self.assertEqual(USM_PRIV_NONE, result.privacy_protocol) self.assertEqual(security_engine_result._value, result.securityEngineId._value) self.assertEqual("secret1", result.securityName) self.assertEqual(1, result.authKeyType) @@ -312,53 +307,62 @@ def test_get_auth_v3_authnopriv(self, m_get_secret_value, m_exists): def test_get_auth_v2c(self): result = get_auth_v2c(ir) self.assertEqual("public", result.communityName) - self.assertEqual(1, result.mpModel) + self.assertEqual(1, result.message_processing_model) def test_get_auth_v1(self): result = get_auth_v1(ir) self.assertEqual("public", result.communityName) - self.assertEqual(0, result.mpModel) + self.assertEqual(0, result.message_processing_model) @patch("splunk_connect_for_snmp.snmp.auth.get_auth_v1") - def test_get_auth_1(self, m_get_auth): + async def test_get_auth_1(self, m_get_auth): ir.version = "1" - get_auth(Mock(), ir, Mock()) + await get_auth(Mock(), ir, Mock()) m_get_auth.assert_called() @patch("splunk_connect_for_snmp.snmp.auth.get_auth_v2c") - def test_get_auth_2c(self, m_get_auth): + async def test_get_auth_2c(self, m_get_auth): ir.version = "2c" - get_auth(Mock(), ir, Mock()) + await get_auth(Mock(), ir, Mock()) m_get_auth.assert_called() @patch("splunk_connect_for_snmp.snmp.auth.get_auth_v3") - def test_get_auth_3(self, m_get_auth): + async def test_get_auth_3(self, m_get_auth): ir.version = "3" - get_auth(Mock(), ir, Mock()) + await get_auth(Mock(), ir, Mock()) m_get_auth.assert_called() - @patch("splunk_connect_for_snmp.snmp.auth.Udp6TransportTarget") - @patch("splunk_connect_for_snmp.snmp.auth.UdpTransportTarget") - def test_setup_transport_target_ipv4( - self, m_setup_udp_transport_target, m_setup_udp6_transport_target - ): + @patch( + "splunk_connect_for_snmp.snmp.auth.UdpTransportTarget.create", + new_callable=AsyncMock, + ) + @patch( + "splunk_connect_for_snmp.snmp.auth.Udp6TransportTarget.create", + new_callable=AsyncMock, + ) + async def test_setup_transport_target_ipv4(self, m_udp6_create, m_udp_create): ir.address = "127.0.0.1" ir.port = 161 - m_setup_udp_transport_target.return_value = "UDP4" - m_setup_udp6_transport_target.return_value = "UDP6" - transport = setup_transport_target(ir) + + m_udp_create.return_value = "UDP4" + m_udp6_create.return_value = "UDP6" + + transport = await setup_transport_target(ir) self.assertEqual("UDP4", transport) - @patch("splunk_connect_for_snmp.snmp.auth.IPv6_ENABLED") - @patch("splunk_connect_for_snmp.snmp.auth.Udp6TransportTarget") - @patch("splunk_connect_for_snmp.snmp.auth.UdpTransportTarget") - def test_setup_transport_target_ipv6( - self, m_setup_udp_transport_target, m_setup_udp6_transport_target, ipv6_enabled - ): - ipv6_enabled.return_value = True + @patch("splunk_connect_for_snmp.snmp.auth.IPv6_ENABLED", True) + @patch( + "splunk_connect_for_snmp.snmp.auth.UdpTransportTarget.create", + new_callable=AsyncMock, + ) + @patch( + "splunk_connect_for_snmp.snmp.auth.Udp6TransportTarget.create", + new_callable=AsyncMock, + ) + async def test_setup_transport_target_ipv6(self, m_udp6_create, m_udp_create): ir.address = "2001:0db8:ac10:fe01::0001" ir.port = 161 - m_setup_udp_transport_target.return_value = "UDP4" - m_setup_udp6_transport_target.return_value = "UDP6" - transport = setup_transport_target(ir) + m_udp_create.return_value = "UDP4" + m_udp6_create.return_value = "UDP6" + transport = await setup_transport_target(ir) self.assertEqual("UDP6", transport) diff --git a/test/snmp/test_do_work.py b/test/snmp/test_do_work.py index 92c9132d4..88bb31848 100644 --- a/test/snmp/test_do_work.py +++ b/test/snmp/test_do_work.py @@ -1,5 +1,7 @@ -from unittest import TestCase -from unittest.mock import MagicMock, patch +import asyncio +from queue import Empty +from unittest import IsolatedAsyncioTestCase +from unittest.mock import AsyncMock, MagicMock, patch from splunk_connect_for_snmp.common.inventory_record import InventoryRecord from splunk_connect_for_snmp.snmp.exceptions import SnmpActionError @@ -22,54 +24,58 @@ ) -class TestDoWork(TestCase): +class TestDoWork(IsolatedAsyncioTestCase): @patch("pymongo.MongoClient", MagicMock()) @patch("mongolock.MongoLock.__init__", MagicMock()) @patch("mongolock.MongoLock.lock", MagicMock()) @patch("mongolock.MongoLock.release", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.auth.get_auth", None) + @patch("splunk_connect_for_snmp.snmp.auth.get_auth", new_callable=AsyncMock) @patch("splunk_connect_for_snmp.snmp.manager.get_context_data", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.manager.setup_transport_target", MagicMock()) - def test_do_work_no_work_to_do(self): + @patch( + "splunk_connect_for_snmp.snmp.manager.setup_transport_target", + new_callable=AsyncMock, + ) + async def test_do_work_no_work_to_do(self, setup_transport, get_auth): poller = Poller.__new__(Poller) poller.last_modified = 1609675634 poller.snmpEngine = None poller.profiles_manager = MagicMock() poller.profiles_collection = MagicMock() + poller.refresh_snmp_engine = MagicMock() poller.profiles_collection.process_profiles = MagicMock() - poller.already_loaded_mibs = {} + poller.already_loaded_mibs = set() varbinds_bulk, varbinds_get = set(), set() get_mapping, bulk_mapping = {}, {} - poller.get_varbinds = MagicMock() - poller.get_varbinds.return_value = ( - varbinds_get, - get_mapping, - varbinds_bulk, - bulk_mapping, + poller.get_varbinds = MagicMock( + return_value=(varbinds_get, get_mapping, varbinds_bulk, bulk_mapping) ) - result = poller.do_work(inventory_record) + result = await poller.do_work(inventory_record) self.assertEqual(result, (False, {})) - @patch("pymongo.MongoClient", MagicMock()) @patch("mongolock.MongoLock.__init__", MagicMock()) @patch("mongolock.MongoLock.lock", MagicMock()) @patch("mongolock.MongoLock.release", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.auth.get_auth", None) + @patch("splunk_connect_for_snmp.snmp.auth.get_auth", new_callable=AsyncMock) @patch("splunk_connect_for_snmp.snmp.manager.get_context_data", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.manager.setup_transport_target", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.manager.bulkCmd") - @patch("splunk_connect_for_snmp.snmp.manager.getCmd") + @patch( + "splunk_connect_for_snmp.snmp.manager.setup_transport_target", + new_callable=AsyncMock, + ) + @patch("splunk_connect_for_snmp.snmp.manager.bulk_walk_cmd") + @patch("splunk_connect_for_snmp.snmp.manager.get_cmd", new_callable=AsyncMock) @patch("splunk_connect_for_snmp.common.collection_manager.ProfilesManager") - def test_do_work_bulk(self, load_profiles, getCmd, bulkCmd): + async def test_do_work_bulk_varbinds( + self, load_profiles, get_cmd, bulk_walk_cmd, setup_transport_target, get_auth + ): poller = Poller.__new__(Poller) poller.last_modified = 1609675634 poller.snmpEngine = None poller.builder = MagicMock() poller.profiles_manager = MagicMock() - m_process_data = MagicMock() - m_process_data.return_value = (False, [], {}) - poller.process_snmp_data = m_process_data + poller.refresh_snmp_engine = MagicMock() + poller.process_snmp_data = MagicMock(return_value=(False, [], {})) + requested_profiles = ["profile1", "profile2"] poller.profiles = { "profile1": { @@ -78,33 +84,53 @@ def test_do_work_bulk(self, load_profiles, getCmd, bulkCmd): }, "profile2": {"frequency": 20, "varBinds": [["UDP-MIB", "udpOutDatagrams"]]}, } - poller.already_loaded_mibs = {} + poller.already_loaded_mibs = set() poller.profiles_collection = ProfileCollection(poller.profiles) poller.profiles_collection.process_profiles() - bulkCmd.return_value = [(None, 0, 0, "Oid1"), (None, 0, 0, "Oid2")] - poller.do_work(inventory_record, profiles=requested_profiles) - self.assertEqual(poller.process_snmp_data.call_count, 2) - self.assertEqual(getCmd.call_count, 0) - self.assertEqual(bulkCmd.call_count, 1) - @patch("pymongo.MongoClient", MagicMock()) + def bulk_walk_cmd_mock(*args, **kwargs): + async def _gen(): + yield (None, 0, 0, ["Oid1"]) + yield (None, 0, 0, ["Oid2"]) + yield (None, 0, 0, ["Oid3"]) + + return _gen() + + bulk_walk_cmd.side_effect = bulk_walk_cmd_mock + get_auth.return_value = MagicMock() + setup_transport_target.return_value = MagicMock() + + # Call the async do_work + await poller.do_work(inventory_record, profiles=requested_profiles) + + # Assertions + self.assertEqual(poller.process_snmp_data.call_count, 9) + get_cmd.assert_not_called() + self.assertEqual(bulk_walk_cmd.call_count, 3) + @patch("mongolock.MongoLock.__init__", MagicMock()) @patch("mongolock.MongoLock.lock", MagicMock()) @patch("mongolock.MongoLock.release", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.auth.get_auth", None) + @patch("splunk_connect_for_snmp.snmp.auth.get_auth", new_callable=AsyncMock) @patch("splunk_connect_for_snmp.snmp.manager.get_context_data", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.manager.setup_transport_target", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.manager.bulkCmd") - @patch("splunk_connect_for_snmp.snmp.manager.getCmd") + @patch( + "splunk_connect_for_snmp.snmp.manager.setup_transport_target", + new_callable=AsyncMock, + ) + @patch("splunk_connect_for_snmp.snmp.manager.bulk_walk_cmd", new_callable=AsyncMock) + @patch("splunk_connect_for_snmp.snmp.manager.get_cmd") @patch( "splunk_connect_for_snmp.common.collection_manager.ProfilesManager.return_collection" ) - def test_do_work_get(self, load_profiles, getCmd, bulkCmd): + async def test_do_work_get( + self, load_profiles, get_cmd, bulk_walk_cmd, setup_transport_target, get_auth + ): poller = Poller.__new__(Poller) poller.last_modified = 1609675634 poller.snmpEngine = None poller.builder = MagicMock() poller.process_snmp_data = MagicMock() + poller.refresh_snmp_engine = MagicMock() poller.profiles_manager = MagicMock() requested_profiles = ["profile1", "profile2"] poller.profiles = { @@ -117,48 +143,63 @@ def test_do_work_get(self, load_profiles, getCmd, bulkCmd): "varBinds": [["UDP-MIB", "udpOutDatagrams", 1]], }, } - poller.already_loaded_mibs = {} + poller.already_loaded_mibs = set() poller.profiles_collection = ProfileCollection(poller.profiles) poller.profiles_collection.process_profiles() - getCmd.return_value = [ - (None, 0, 0, "Oid1"), - (None, 0, 0, "Oid2"), - (None, 0, 0, "Oid3"), + get_cmd.side_effect = [ + (None, 0, 0, ["Oid1"]), + (None, 0, 0, ["Oid2"]), + (None, 0, 0, ["Oid3"]), ] - poller.do_work(inventory_record, profiles=requested_profiles) - self.assertEqual(poller.process_snmp_data.call_count, 3) - self.assertEqual(getCmd.call_count, 1) - self.assertEqual(bulkCmd.call_count, 0) + await poller.do_work(inventory_record, profiles=requested_profiles) + self.assertEqual(poller.process_snmp_data.call_count, 1) + self.assertEqual(get_cmd.call_count, 1) + self.assertEqual(bulk_walk_cmd.call_count, 0) @patch("pymongo.MongoClient", MagicMock()) @patch("mongolock.MongoLock.__init__", MagicMock()) @patch("mongolock.MongoLock.lock", MagicMock()) @patch("mongolock.MongoLock.release", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.auth.get_auth", None) + @patch("splunk_connect_for_snmp.snmp.auth.get_auth", new_callable=AsyncMock) @patch("splunk_connect_for_snmp.snmp.manager.get_context_data", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.manager.setup_transport_target", MagicMock()) - @patch("splunk_connect_for_snmp.snmp.manager.bulkCmd") - @patch("splunk_connect_for_snmp.snmp.manager.getCmd") + @patch( + "splunk_connect_for_snmp.snmp.manager.setup_transport_target", + new_callable=AsyncMock, + ) + @patch("splunk_connect_for_snmp.snmp.manager.bulk_walk_cmd", new_callable=AsyncMock) + @patch("splunk_connect_for_snmp.snmp.manager.get_cmd", new_callable=AsyncMock) @patch( "splunk_connect_for_snmp.common.collection_manager.ProfilesManager.return_collection" ) - def test_do_work_errors(self, load_profiles, getCmd, bulkCmd): + async def test_do_work_errors( + self, + load_profiles, + get_cmd_mock, + bulk_walk_cmd_mock, + setup_transport_target, + get_auth, + ): poller = Poller.__new__(Poller) poller.last_modified = 1609675634 poller.snmpEngine = None poller.builder = MagicMock() poller.process_snmp_data = MagicMock() + poller.refresh_snmp_engine = MagicMock() poller.profiles_manager = MagicMock() requested_profiles = ["profile1"] poller.profiles = { "profile1": {"frequency": 20, "varBinds": [["IF-MIB", "ifDescr", 1]]} } - poller.already_loaded_mibs = {} + poller.already_loaded_mibs = set() poller.profiles_collection = ProfileCollection(poller.profiles) poller.profiles_collection.process_profiles() - getCmd.return_value = [(True, True, 2, [])] + + async def get_cmd_failure(*args, **kwargs): + return (True, True, 2, []) + + get_cmd_mock.side_effect = get_cmd_failure with self.assertRaises(SnmpActionError): - poller.do_work(inventory_record, profiles=requested_profiles) + await poller.do_work(inventory_record, profiles=requested_profiles) self.assertEqual(poller.process_snmp_data.call_count, 0) - self.assertEqual(getCmd.call_count, 1) - self.assertEqual(bulkCmd.call_count, 0) + self.assertEqual(get_cmd_mock.call_count, 1) + self.assertEqual(bulk_walk_cmd_mock.call_count, 0) diff --git a/test/snmp/test_get_varbinds.py b/test/snmp/test_get_varbinds.py index 000b808cf..5b9c637c8 100644 --- a/test/snmp/test_get_varbinds.py +++ b/test/snmp/test_get_varbinds.py @@ -44,7 +44,7 @@ def test_get_varbinds_for_walk_redundant(self): poller.profiles = profiles poller.profiles_collection = ProfileCollection(profiles) poller.profiles_collection.process_profiles() - poller.already_loaded_mibs = {} + poller.already_loaded_mibs = set() poller.load_mibs = Mock() varbinds_get, get_mapping, varbinds_bulk, bulk_mapping = poller.get_varbinds( "192.168.0.1", walk=True, profiles=["test1"] @@ -78,7 +78,7 @@ def test_get_varbinds_for_walk_none(self): poller.profiles = profiles poller.profiles_collection = ProfileCollection(profiles) poller.profiles_collection.process_profiles() - poller.already_loaded_mibs = {} + poller.already_loaded_mibs = set() poller.load_mibs = Mock() varbinds_get, get_mapping, varbinds_bulk, bulk_mapping = poller.get_varbinds( "192.168.0.1", walk=True, profiles=["test1"] @@ -110,7 +110,7 @@ def test_get_varbinds_for_walk_with_three_profiles(self): poller.profiles = profiles poller.profiles_collection = ProfileCollection(profiles) poller.profiles_collection.process_profiles() - poller.already_loaded_mibs = {} + poller.already_loaded_mibs = set() poller.load_mibs = Mock() varbinds_get, get_mapping, varbinds_bulk, bulk_mapping = poller.get_varbinds( "192.168.0.1", walk=True, profiles=["test1"] diff --git a/test/snmp/test_mibs.py b/test/snmp/test_mibs.py index 142592c1f..d71e70e8d 100644 --- a/test/snmp/test_mibs.py +++ b/test/snmp/test_mibs.py @@ -11,7 +11,7 @@ def test_load_mib(self): poller = Poller.__new__(Poller) poller.builder = Mock() poller.load_mibs(["a", "b", "c"]) - calls = poller.builder.loadModules.call_args_list + calls = poller.builder.load_modules.call_args_list self.assertEqual("a", calls[0][0][0]) self.assertEqual("b", calls[1][0][0]) @@ -50,7 +50,7 @@ def test_is_mib_resolved(self): def test_exception_during_loading(self): poller = Poller.__new__(Poller) poller.builder = Mock() - poller.builder.loadModules.side_effect = error.MibLoadError() + poller.builder.load_modules.side_effect = error.MibLoadError() poller.load_mibs(["a"]) def test_find_new_mibs_is_found(self): diff --git a/test/snmp/test_process_snmp_data.py b/test/snmp/test_process_snmp_data.py index b3efe092b..91f129748 100644 --- a/test/snmp/test_process_snmp_data.py +++ b/test/snmp/test_process_snmp_data.py @@ -2,6 +2,9 @@ from unittest import TestCase from unittest.mock import MagicMock, Mock, patch +from pysnmp.entity.engine import SnmpEngine +from pysnmp.smi import view + from splunk_connect_for_snmp.snmp.manager import Poller @@ -11,10 +14,12 @@ class TestProcessSnmpData(TestCase): @patch("splunk_connect_for_snmp.snmp.manager.map_metric_type") @patch("splunk_connect_for_snmp.snmp.manager.extract_index_number") @patch("splunk_connect_for_snmp.snmp.manager.extract_indexes") + @patch("splunk_connect_for_snmp.snmp.manager.ObjectIdentity.resolve_with_mib") @patch("time.time") def test_multiple_metrics_single_group( self, m_time, + m_resolve_with_mib, m_extract_indexes, m_extract_index_number, m_map_metric_type, @@ -22,6 +27,9 @@ def test_multiple_metrics_single_group( m_resolved, ): poller = Poller.__new__(Poller) + poller.snmpEngine = SnmpEngine() + poller.builder = poller.snmpEngine.get_mib_builder() + poller.mib_view_controller = view.MibViewController(poller.builder) m_resolved.return_value = True m_get_group_key.return_value = "QWERTYUIOP" @@ -31,32 +39,33 @@ def test_multiple_metrics_single_group( m_time.return_value = 1640609779.473053 - varbind_mock1_1 = Mock() - varbind_mock1_2 = Mock() - varbind_mock2_1 = Mock() - varbind_mock2_2 = Mock() + resolved1 = Mock() + resolved1.get_mib_symbol.return_value = ("IF-MIB", "some_metric", 1) + resolved1.prettyPrint.return_value = "IF-MIB::some_metric.1" + resolved2 = Mock() - varbind_mock1_1.getMibSymbol.return_value = "IF-MIB", "some_metric", 1 - varbind_mock1_1.prettyPrint.return_value = "some text" - varbind_mock1_1.getOid.return_value = "1.2.3.4.5.6.7" + resolved2.get_mib_symbol.return_value = ("UDP-MIB", "next_metric", 1) + resolved2.prettyPrint.return_value = "UDP-MIB::next_metric.1" + m_resolve_with_mib.side_effect = [resolved1, resolved2] - varbind_mock1_2.prettyPrint.return_value = 65 + v1 = Mock() + v1.get_oid.return_value = "1.2.3.4.5.6.7" - varbind_mock2_1.getMibSymbol.return_value = "UDP-MIB", "next_metric", 1 - varbind_mock2_1.prettyPrint.return_value = "some text2" - varbind_mock2_1.getOid.return_value = "9.8.7.6" + v2 = Mock() + v2.prettyPrint.return_value = 65 - varbind_mock2_2.prettyPrint.return_value = 123 + v3 = Mock() + v3.get_oid.return_value = "9.8.7.6" - varbind_table = [ - (varbind_mock1_1, varbind_mock1_2), - (varbind_mock2_1, varbind_mock2_2), - ] + v4 = Mock() + v4.prettyPrint.return_value = 123 + + varbind_table = [(v1, v2), (v3, v4)] metrics = {} mapping = {} poller.process_snmp_data(varbind_table, metrics, mapping) - + print(f"metric={metrics}") self.assertEqual( { "QWERTYUIOP": { @@ -86,10 +95,12 @@ def test_multiple_metrics_single_group( @patch("splunk_connect_for_snmp.snmp.manager.map_metric_type") @patch("splunk_connect_for_snmp.snmp.manager.extract_index_number") @patch("splunk_connect_for_snmp.snmp.manager.extract_indexes") + @patch("splunk_connect_for_snmp.snmp.manager.ObjectIdentity.resolve_with_mib") @patch("time.time") def test_multiple_metrics_multiple_groups( self, m_time, + m_resolve_with_mib, m_extract_indexes, m_extract_index_number, m_map_metric_type, @@ -97,6 +108,9 @@ def test_multiple_metrics_multiple_groups( m_resolved, ): poller = Poller.__new__(Poller) + poller.snmpEngine = SnmpEngine() + poller.builder = poller.snmpEngine.get_mib_builder() + poller.mib_view_controller = view.MibViewController(poller.builder) m_resolved.return_value = True m_get_group_key.side_effect = ["GROUP1", "GROUP2"] @@ -106,21 +120,24 @@ def test_multiple_metrics_multiple_groups( m_time.return_value = 1640609779.473053 - varbind_mock1_1 = Mock() - varbind_mock1_2 = Mock() - varbind_mock2_1 = Mock() - varbind_mock2_2 = Mock() + resolved1 = Mock() + resolved1.get_mib_symbol.return_value = ("IF-MIB", "some_metric", 1) + resolved1.prettyPrint.return_value = "IF-MIB::some_metric.1" - varbind_mock1_1.getMibSymbol.return_value = "IF-MIB", "some_metric", 1 - varbind_mock1_1.prettyPrint.return_value = "some text" - varbind_mock1_1.getOid.return_value = "1.2.3.4.5.6.7" + resolved2 = Mock() + resolved2.get_mib_symbol.return_value = ("UDP-MIB", "next_metric", 1) + resolved2.prettyPrint.return_value = "UDP-MIB::next_metric.1" - varbind_mock1_2.prettyPrint.return_value = 65 + m_resolve_with_mib.side_effect = [resolved1, resolved2] - varbind_mock2_1.getMibSymbol.return_value = "UDP-MIB", "next_metric", 1 - varbind_mock2_1.prettyPrint.return_value = "some text2" - varbind_mock2_1.getOid.return_value = "9.8.7.6" + varbind_mock1_1 = Mock() + varbind_mock1_1.get_oid.return_value = "1.2.3.4.5.6.7" + varbind_mock1_2 = Mock() + varbind_mock1_2.prettyPrint.return_value = 65 + varbind_mock2_1 = Mock() + varbind_mock2_1.get_oid.return_value = "9.8.7.6" + varbind_mock2_2 = Mock() varbind_mock2_2.prettyPrint.return_value = 123 varbind_table = [ @@ -167,10 +184,12 @@ def test_multiple_metrics_multiple_groups( @patch("splunk_connect_for_snmp.snmp.manager.map_metric_type") @patch("splunk_connect_for_snmp.snmp.manager.extract_index_number") @patch("splunk_connect_for_snmp.snmp.manager.extract_indexes") + @patch("splunk_connect_for_snmp.snmp.manager.ObjectIdentity.resolve_with_mib") @patch("time.time") def test_metrics_and_fields( self, m_time, + m_resolve_with_mib, m_extract_indexes, m_extract_index_number, m_map_metric_type, @@ -178,39 +197,37 @@ def test_metrics_and_fields( m_resolved, ): poller = Poller.__new__(Poller) + poller.snmpEngine = SnmpEngine() + poller.builder = poller.snmpEngine.get_mib_builder() + poller.mib_view_controller = view.MibViewController(poller.builder) m_resolved.return_value = True m_get_group_key.return_value = "GROUP1" m_map_metric_type.side_effect = ["g", "r"] m_extract_index_number.return_value = 1 m_extract_indexes.return_value = [7] - m_time.return_value = 1640609779.473053 - - varbind_mock1_1 = Mock() - varbind_mock1_2 = Mock() - varbind_mock2_1 = Mock() - varbind_mock2_2 = Mock() - - varbind_mock1_1.getMibSymbol.return_value = "IF-MIB", "some_metric", 1 - varbind_mock1_1.prettyPrint.return_value = "some text" - varbind_mock1_1.getOid.return_value = "1.2.3.4.5.6.7" - - varbind_mock1_2.prettyPrint.return_value = 65 - - varbind_mock2_1.getMibSymbol.return_value = "UDP-MIB", "some_field", 1 - varbind_mock2_1.prettyPrint.return_value = "some text2" - varbind_mock2_1.getOid.return_value = "9.8.7.6" - - varbind_mock2_2.prettyPrint.return_value = "up and running" - - varbind_table = [ - (varbind_mock1_1, varbind_mock1_2), - (varbind_mock2_1, varbind_mock2_2), - ] - metrics = {} - mapping = {} - + resolved_metric = Mock() + resolved_metric.get_mib_symbol.return_value = ("IF-MIB", "some_metric", 1) + resolved_metric.prettyPrint.return_value = "IF-MIB::some_metric.1" + + resolved_field = Mock() + resolved_field.get_mib_symbol.return_value = ("UDP-MIB", "some_field", 1) + resolved_field.prettyPrint.return_value = "UDP-MIB::some_field.1" + m_resolve_with_mib.side_effect = [resolved_metric, resolved_field] + + v1 = Mock() + v1.get_oid.return_value = "1.2.3.4.5.6.7" + v2 = Mock() + v2.prettyPrint.return_value = 65 # metric value + + v3 = Mock() + v3.get_oid.return_value = "9.8.7.6" + v4 = Mock() + v4.prettyPrint.return_value = "up and running" + + varbind_table = [(v1, v2), (v3, v4)] + metrics, mapping = {}, {} poller.process_snmp_data(varbind_table, metrics, mapping) self.assertEqual( @@ -243,10 +260,12 @@ def test_metrics_and_fields( @patch("splunk_connect_for_snmp.snmp.manager.map_metric_type") @patch("splunk_connect_for_snmp.snmp.manager.extract_index_number") @patch("splunk_connect_for_snmp.snmp.manager.extract_indexes") + @patch("splunk_connect_for_snmp.snmp.manager.ObjectIdentity.resolve_with_mib") @patch("time.time") def test_metrics_with_profile( self, m_time, + m_resolve_with_mib, m_extract_indexes, m_extract_index_number, m_map_metric_type, @@ -254,36 +273,35 @@ def test_metrics_with_profile( m_resolved, ): poller = Poller.__new__(Poller) + poller.snmpEngine = SnmpEngine() + poller.builder = poller.snmpEngine.get_mib_builder() + poller.mib_view_controller = view.MibViewController(poller.builder) m_resolved.return_value = True m_get_group_key.return_value = "QWERTYUIOP" m_map_metric_type.side_effect = ["g", "g"] m_extract_index_number.return_value = 1 m_extract_indexes.return_value = [6, 7] - m_time.return_value = 1640609779.473053 - varbind_mock1_1 = Mock() - varbind_mock1_2 = Mock() - varbind_mock2_1 = Mock() - varbind_mock2_2 = Mock() - - varbind_mock1_1.getMibSymbol.return_value = "IF-MIB", "some_metric", 1 - varbind_mock1_1.prettyPrint.return_value = "some text" - varbind_mock1_1.getOid.return_value = "1.2.3.4.5.6.7" - - varbind_mock1_2.prettyPrint.return_value = 65 - - varbind_mock2_1.getMibSymbol.return_value = "UDP-MIB", "next_metric", 1 - varbind_mock2_1.prettyPrint.return_value = "some text2" - varbind_mock2_1.getOid.return_value = "9.8.7.6" - - varbind_mock2_2.prettyPrint.return_value = 123 - - varbind_table = [ - (varbind_mock1_1, varbind_mock1_2), - (varbind_mock2_1, varbind_mock2_2), - ] + resolved1 = Mock() + resolved1.get_mib_symbol.return_value = ("IF-MIB", "some_metric", 1) + resolved1.prettyPrint.return_value = "IF-MIB::some_metric.1" + resolved2 = Mock() + resolved2.get_mib_symbol.return_value = ("UDP-MIB", "next_metric", 1) + resolved2.prettyPrint.return_value = "UDP-MIB::next_metric.1" + m_resolve_with_mib.side_effect = [resolved1, resolved2] + + v1 = Mock() + v1.get_oid.return_value = "1.2.3.4.5.6.7" + v2 = Mock() + v2.prettyPrint.return_value = 65 + v3 = Mock() + v3.get_oid.return_value = "9.8.7.6" + v4 = Mock() + v4.prettyPrint.return_value = 123 + + varbind_table = [(v1, v2), (v3, v4)] metrics = {} mapping = { "IF-MIB::some_metric": "profile1", diff --git a/test/snmp/test_tasks.py b/test/snmp/test_tasks.py index 13f641e04..b32582438 100644 --- a/test/snmp/test_tasks.py +++ b/test/snmp/test_tasks.py @@ -1,5 +1,5 @@ from unittest import TestCase -from unittest.mock import MagicMock, patch +from unittest.mock import AsyncMock, MagicMock, patch from pysnmp.smi.error import SmiError @@ -8,7 +8,9 @@ class TestTasks(TestCase): @patch("splunk_connect_for_snmp.snmp.manager.get_inventory") @patch("splunk_connect_for_snmp.snmp.manager.Poller.__init__") - @patch("splunk_connect_for_snmp.snmp.manager.Poller.do_work") + @patch( + "splunk_connect_for_snmp.snmp.manager.Poller.do_work", new_callable=AsyncMock + ) @patch("time.time") def test_walk( self, @@ -42,7 +44,9 @@ def test_walk( @patch("splunk_connect_for_snmp.snmp.manager.get_inventory") @patch("splunk_connect_for_snmp.snmp.manager.Poller.__init__") - @patch("splunk_connect_for_snmp.snmp.manager.Poller.do_work") + @patch( + "splunk_connect_for_snmp.snmp.manager.Poller.do_work", new_callable=AsyncMock + ) @patch("time.time") def test_poll_with_group( self, @@ -81,7 +85,9 @@ def test_poll_with_group( @patch("splunk_connect_for_snmp.snmp.manager.get_inventory") @patch("splunk_connect_for_snmp.snmp.manager.Poller.__init__") - @patch("splunk_connect_for_snmp.snmp.manager.Poller.do_work") + @patch( + "splunk_connect_for_snmp.snmp.manager.Poller.do_work", new_callable=AsyncMock + ) @patch("time.time") def test_walk_with_group( self, @@ -117,7 +123,7 @@ def test_walk_with_group( result, ) - @patch("pysnmp.smi.rfc1902.ObjectType.resolveWithMib") + @patch("pysnmp.smi.rfc1902.ObjectType.resolve_with_mib") @patch("splunk_connect_for_snmp.snmp.manager.Poller.process_snmp_data") @patch("splunk_connect_for_snmp.snmp.manager.Poller.__init__") @patch("time.time") @@ -154,7 +160,7 @@ def test_trap( result, ) - @patch("pysnmp.smi.rfc1902.ObjectType.resolveWithMib") + @patch("pysnmp.smi.rfc1902.ObjectType.resolve_with_mib") @patch("splunk_connect_for_snmp.snmp.manager.Poller.process_snmp_data") @patch("splunk_connect_for_snmp.snmp.manager.Poller.__init__") @patch("time.time") @@ -196,7 +202,7 @@ def test_trap_with_context_engine_id( result, ) - @patch("pysnmp.smi.rfc1902.ObjectType.resolveWithMib") + @patch("pysnmp.smi.rfc1902.ObjectType.resolve_with_mib") @patch("splunk_connect_for_snmp.snmp.manager.Poller.process_snmp_data") @patch("splunk_connect_for_snmp.snmp.manager.Poller.is_mib_known") @patch("splunk_connect_for_snmp.snmp.manager.Poller.load_mibs") @@ -243,7 +249,7 @@ def test_trap_retry_translation( result, ) - @patch("pysnmp.smi.rfc1902.ObjectType.resolveWithMib") + @patch("pysnmp.smi.rfc1902.ObjectType.resolve_with_mib") @patch("splunk_connect_for_snmp.snmp.manager.Poller.process_snmp_data") @patch("splunk_connect_for_snmp.snmp.manager.Poller.is_mib_known") @patch("splunk_connect_for_snmp.snmp.manager.Poller.load_mibs") @@ -293,7 +299,7 @@ def test_trap_retry_translation_failed( @patch("splunk_connect_for_snmp.snmp.tasks.RESOLVE_TRAP_ADDRESS", "true") @patch("splunk_connect_for_snmp.snmp.tasks.resolve_address") - @patch("pysnmp.smi.rfc1902.ObjectType.resolveWithMib") + @patch("pysnmp.smi.rfc1902.ObjectType.resolve_with_mib") @patch("splunk_connect_for_snmp.snmp.manager.Poller.process_snmp_data") @patch("splunk_connect_for_snmp.snmp.manager.Poller.__init__") @patch("time.time") diff --git a/test/snmp/test_utils.py b/test/snmp/test_utils.py index 095f2ddfe..5498c0c4f 100644 --- a/test/snmp/test_utils.py +++ b/test/snmp/test_utils.py @@ -142,7 +142,7 @@ def test_get_context_data(self): result = get_context_data() self.assertIsNone(result.contextEngineId) - self.assertEqual("", result.contextName) + self.assertEqual(b"", result.contextName) def test_return_address_and_port(self): self.assertEqual(return_address_and_port("127.0.0.1"), ("127.0.0.1", 161)) diff --git a/test/test_walk.py b/test/test_walk.py index 87eaceea1..f220e4080 100644 --- a/test/test_walk.py +++ b/test/test_walk.py @@ -1,5 +1,5 @@ -from unittest import TestCase -from unittest.mock import mock_open, patch +from unittest import IsolatedAsyncioTestCase +from unittest.mock import AsyncMock, mock_open, patch from splunk_connect_for_snmp.walk import run_walk @@ -9,18 +9,20 @@ 192.178.0.1,,2c,public,,,1804,test_1,True,False""" -class TestWalk(TestCase): +class TestWalk(IsolatedAsyncioTestCase): @patch("builtins.open", new_callable=mock_open, read_data=mock_inventory) @patch("splunk_connect_for_snmp.snmp.manager.Poller.__init__") - @patch("splunk_connect_for_snmp.snmp.manager.Poller.do_work") + @patch( + "splunk_connect_for_snmp.snmp.manager.Poller.do_work", new_callable=AsyncMock + ) @patch( "splunk_connect_for_snmp.common.collection_manager.ProfilesManager.return_collection" ) - def test_run_walk(self, m_load_profiles, m_do_work, m_init, m_open): + async def test_run_walk(self, m_load_profiles, m_do_work, m_init, m_open): m_init.return_value = None m_do_work.return_value = (False, {}) - run_walk() + await run_walk() calls = m_do_work.call_args_list @@ -34,15 +36,17 @@ def test_run_walk(self, m_load_profiles, m_do_work, m_init, m_open): @patch("builtins.open", new_callable=mock_open, read_data=mock_inventory) @patch("splunk_connect_for_snmp.snmp.manager.Poller.__init__") - @patch("splunk_connect_for_snmp.snmp.manager.Poller.do_work") + @patch( + "splunk_connect_for_snmp.snmp.manager.Poller.do_work", new_callable=AsyncMock + ) @patch( "splunk_connect_for_snmp.common.collection_manager.ProfilesManager.return_collection" ) - def test_run_walk_exception(self, m_load_profiles, m_do_work, m_init, m_open): + async def test_run_walk_exception(self, m_load_profiles, m_do_work, m_init, m_open): m_init.return_value = None m_do_work.side_effect = (Exception("Boom!"), (False, {})) - run_walk() + await run_walk() calls = m_do_work.call_args_list