sfacts 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. sfacts-2.3.0/.env.template +11 -0
  2. sfacts-2.3.0/.gitignore +144 -0
  3. sfacts-2.3.0/.gitlab-ci-simple.yml +85 -0
  4. sfacts-2.3.0/.gitlab-ci.yml +153 -0
  5. sfacts-2.3.0/CHANGELOG.md +208 -0
  6. sfacts-2.3.0/LICENSE +21 -0
  7. sfacts-2.3.0/PKG-INFO +414 -0
  8. sfacts-2.3.0/README.md +367 -0
  9. sfacts-2.3.0/bandit-report.json +488 -0
  10. sfacts-2.3.0/deploy.py +64 -0
  11. sfacts-2.3.0/pyproject.toml +177 -0
  12. sfacts-2.3.0/pytest.ini +39 -0
  13. sfacts-2.3.0/requirements.txt +25 -0
  14. sfacts-2.3.0/scripts/test-ci-locally.sh +86 -0
  15. sfacts-2.3.0/serve.py +79 -0
  16. sfacts-2.3.0/sfacts/__init__.py +17 -0
  17. sfacts-2.3.0/sfacts/cli/__init__.py +8 -0
  18. sfacts-2.3.0/sfacts/cli/discover.py +182 -0
  19. sfacts-2.3.0/sfacts/cli/export.py +177 -0
  20. sfacts-2.3.0/sfacts/cli/facts.py +219 -0
  21. sfacts-2.3.0/sfacts/cli/main.py +39 -0
  22. sfacts-2.3.0/sfacts/cli/netbox.py +705 -0
  23. sfacts-2.3.0/sfacts/core/__init__.py +9 -0
  24. sfacts-2.3.0/sfacts/core/collector.py +342 -0
  25. sfacts-2.3.0/sfacts/core/discovery.py +300 -0
  26. sfacts-2.3.0/sfacts/core/inventory.py +337 -0
  27. sfacts-2.3.0/sfacts/core/netbox/__init__.py +18 -0
  28. sfacts-2.3.0/sfacts/core/netbox/base_objects.py +194 -0
  29. sfacts-2.3.0/sfacts/core/netbox/bulk_helpers.py +164 -0
  30. sfacts-2.3.0/sfacts/core/netbox/bulk_sync.py +858 -0
  31. sfacts-2.3.0/sfacts/core/netbox/client.py +102 -0
  32. sfacts-2.3.0/sfacts/core/netbox/device_sync.py +361 -0
  33. sfacts-2.3.0/sfacts/core/netbox/interface_sync.py +199 -0
  34. sfacts-2.3.0/sfacts/core/netbox/ipam_sync.py +516 -0
  35. sfacts-2.3.0/sfacts/core/netbox/mac_sync.py +183 -0
  36. sfacts-2.3.0/sfacts/core/netbox/primary_mac_assignment.py +75 -0
  37. sfacts-2.3.0/sfacts/core/netbox/vrf_sync.py +171 -0
  38. sfacts-2.3.0/sfacts/core/scanner.py +389 -0
  39. sfacts-2.3.0/sfacts/deployments.py +228 -0
  40. sfacts-2.3.0/sfacts/flows/__init__.py +22 -0
  41. sfacts-2.3.0/sfacts/flows/discovery_only.py +198 -0
  42. sfacts-2.3.0/sfacts/flows/full_inventory.py +245 -0
  43. sfacts-2.3.0/sfacts/flows/modular_flows.py +1036 -0
  44. sfacts-2.3.0/sfacts/tasks/__init__.py +23 -0
  45. sfacts-2.3.0/sfacts/tasks/collect.py +43 -0
  46. sfacts-2.3.0/sfacts/tasks/discover.py +88 -0
  47. sfacts-2.3.0/sfacts/tasks/export.py +41 -0
  48. sfacts-2.3.0/sfacts/tasks/netbox.py +285 -0
  49. sfacts-2.3.0/sfacts/utils/__init__.py +7 -0
  50. sfacts-2.3.0/sfacts/utils/auth.py +242 -0
  51. sfacts-2.3.0/sfacts/utils/logger.py +100 -0
  52. sfacts-2.3.0/uv.lock +6270 -0
@@ -0,0 +1,11 @@
1
+ # NetBox Integration Configuration
2
+ # Copy this file to .env and fill in your actual values
3
+
4
+ # NetBox instance URL (include http:// or https://)
5
+ NETBOX_URL=https://your-netbox-instance.com
6
+
7
+ # NetBox API token (generate from NetBox Admin > API Tokens)
8
+ NETBOX_TOKEN=your-api-token-here
9
+
10
+ # Optional: Disable SSL verification for testing (not recommended for production)
11
+ # NETBOX_VERIFY_SSL=false
@@ -0,0 +1,144 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+ MANIFEST
27
+
28
+ # PyInstaller
29
+ # Usually these files are written by a python script from a template
30
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
31
+ *.manifest
32
+ *.spec
33
+
34
+ # Ruff cache
35
+ .ruff_cache/
36
+
37
+ # Installer logs
38
+
39
+ # IDE specific files
40
+ .idea/
41
+ .vscode/
42
+ pip-log.txt
43
+ pip-delete-this-directory.txt
44
+
45
+ # Unit test / coverage reports
46
+ htmlcov/
47
+ .tox/
48
+ .nox/
49
+ .coverage
50
+ .coverage.*
51
+ .cache
52
+ nosetests.xml
53
+ coverage.xml
54
+ *.cover
55
+ .hypothesis/
56
+ .pytest_cache/
57
+
58
+ # Translations
59
+ *.mo
60
+ *.pot
61
+
62
+ # Django stuff:
63
+ *.log
64
+ local_settings.py
65
+ db.sqlite3
66
+
67
+ # Flask stuff:
68
+ instance/
69
+ .webassets-cache
70
+
71
+ # Scrapy stuff:
72
+ .scrapy
73
+
74
+ # Sphinx documentation
75
+ docs/_build/
76
+
77
+ # PyBuilder
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ .python-version
89
+
90
+ # celery beat schedule file
91
+ celerybeat-schedule
92
+
93
+ # SageMath parsed files
94
+ *.sage.py
95
+
96
+ # Environments
97
+ .env
98
+ .venv
99
+ env/
100
+ venv/
101
+ ENV/
102
+ env.bak/
103
+ venv.bak/
104
+
105
+ # Spyder project settings
106
+ .spyderproject
107
+ .spyproject
108
+
109
+ # Rope project settings
110
+ .ropeproject
111
+
112
+ # mkdocs documentation & blueprints
113
+ /site
114
+ blueprints/
115
+
116
+ # mypy
117
+ .mypy_cache/
118
+ .dmypy.json
119
+ dmypy.json
120
+
121
+ # Pyre type checker
122
+ .pyre/
123
+
124
+ # Project specific
125
+ *.swp
126
+ .DS_Store
127
+
128
+ # uv
129
+ .venv/
130
+
131
+ # Output files
132
+ output/
133
+
134
+ # Claude Code worktrees and project data
135
+ .claude/
136
+
137
+ # Benchmark results
138
+ .benchmarks/
139
+
140
+ # GitLab CI/CD local runner cache
141
+ .gitlab/
142
+
143
+ # Netlab labs
144
+ labs/
@@ -0,0 +1,85 @@
1
+ # Simplified GitLab CI/CD Pipeline for SFacts
2
+ # This version is tested and working
3
+
4
+ image: python:3.11-slim
5
+
6
+ stages:
7
+ - lint
8
+ - test
9
+ - build
10
+
11
+ # Cache pip packages
12
+ cache:
13
+ paths:
14
+ - .cache/pip
15
+ key: ${CI_COMMIT_REF_SLUG}
16
+
17
+ variables:
18
+ PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
19
+
20
+ # Install dependencies before each job
21
+ .install_deps: &install_deps
22
+ before_script:
23
+ - pip install --upgrade pip
24
+ - pip install -e .
25
+ - pip install ruff pytest pytest-cov pytest-mock mypy
26
+
27
+ # ============================================================================
28
+ # LINT STAGE
29
+ # ============================================================================
30
+
31
+ ruff-lint:
32
+ stage: lint
33
+ <<: *install_deps
34
+ script:
35
+ - echo "Running Ruff linter..."
36
+ - ruff check sfacts/ tests/ --statistics
37
+ allow_failure: false
38
+
39
+ ruff-format:
40
+ stage: lint
41
+ <<: *install_deps
42
+ script:
43
+ - echo "Checking code formatting..."
44
+ - ruff format --check sfacts/ tests/
45
+ allow_failure: false
46
+
47
+ # ============================================================================
48
+ # TEST STAGE
49
+ # ============================================================================
50
+
51
+ unit-tests:
52
+ stage: test
53
+ <<: *install_deps
54
+ script:
55
+ - echo "Running unit tests..."
56
+ - pytest tests/unit/ -v --ignore=tests/integration --ignore=tests/e2e --ignore=tests/unit/test_netbox_sync.py --ignore=tests/unit/test_netbox_bulk_sync.py --ignore=tests/unit/test_tasks/test_netbox_task.py --cov=sfacts --cov-report=term --cov-report=xml
57
+ coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
58
+ artifacts:
59
+ reports:
60
+ coverage_report:
61
+ coverage_format: cobertura
62
+ path: coverage.xml
63
+ paths:
64
+ - htmlcov/
65
+ expire_in: 30 days
66
+
67
+ # ============================================================================
68
+ # BUILD STAGE (only on main/tags)
69
+ # ============================================================================
70
+
71
+ build-package:
72
+ stage: build
73
+ before_script:
74
+ - pip install build
75
+ script:
76
+ - echo "Building Python package..."
77
+ - python -m build
78
+ - ls -lh dist/
79
+ artifacts:
80
+ paths:
81
+ - dist/
82
+ expire_in: 30 days
83
+ only:
84
+ - main
85
+ - tags
@@ -0,0 +1,153 @@
1
+ # GitLab CI/CD Pipeline for SFacts
2
+ # Modern CI/CD with uv: fast, reproducible Python builds
3
+
4
+ image: ghcr.io/astral-sh/uv:python3.11-bookworm-slim
5
+
6
+ stages:
7
+ - lint
8
+ - test
9
+ - security
10
+ - build
11
+ - deploy
12
+
13
+ # Cache uv packages and virtual environment
14
+ cache:
15
+ key:
16
+ files:
17
+ - uv.lock
18
+ paths:
19
+ - .uv-cache/
20
+ - .venv/
21
+
22
+ variables:
23
+ UV_CACHE_DIR: "$CI_PROJECT_DIR/.uv-cache"
24
+ UV_LINK_MODE: "copy" # Required for Docker builds
25
+
26
+ # Template for uv-based jobs
27
+ .uv_sync: &uv_sync
28
+ before_script:
29
+ - echo "Syncing dependencies with uv..."
30
+ - uv sync --frozen --all-groups
31
+
32
+ # ============================================================================
33
+ # LINT STAGE: Code quality checks
34
+ # ============================================================================
35
+
36
+ ruff-lint:
37
+ stage: lint
38
+ <<: *uv_sync
39
+ script:
40
+ - echo "Running Ruff linter..."
41
+ - uv run ruff check sfacts/ tests/ --statistics || true
42
+ - echo "Checking for critical errors only..."
43
+ - uv run ruff check sfacts/ tests/ --select=E9,F63,F7,F82 --statistics
44
+ allow_failure: false
45
+
46
+ ruff-format-check:
47
+ stage: lint
48
+ <<: *uv_sync
49
+ script:
50
+ - echo "Checking code formatting with Ruff..."
51
+ - uv run ruff format --check sfacts/ tests/
52
+ allow_failure: false
53
+
54
+ mypy-type-check:
55
+ stage: lint
56
+ <<: *uv_sync
57
+ script:
58
+ - echo "Running MyPy type checker..."
59
+ - uv run mypy sfacts/ --ignore-missing-imports
60
+ allow_failure: true # Allow failure initially as type hints are being added
61
+
62
+ # ============================================================================
63
+ # TEST STAGE: Run test suites
64
+ # ============================================================================
65
+
66
+ unit-tests:
67
+ stage: test
68
+ <<: *uv_sync
69
+ script:
70
+ - echo "Running unit tests..."
71
+ - uv run pytest tests/unit/ -v --ignore=tests/integration --ignore=tests/e2e --ignore=tests/unit/test_netbox_sync.py --ignore=tests/unit/test_netbox_bulk_sync.py --ignore=tests/unit/test_tasks/test_netbox_task.py --cov=sfacts --cov-report=term --cov-report=xml --cov-report=html
72
+ coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
73
+ artifacts:
74
+ reports:
75
+ coverage_report:
76
+ coverage_format: cobertura
77
+ path: coverage.xml
78
+ paths:
79
+ - htmlcov/
80
+ - coverage.xml
81
+ expire_in: 30 days
82
+ allow_failure: false
83
+
84
+ # ============================================================================
85
+ # SECURITY STAGE: Security scanning
86
+ # ============================================================================
87
+
88
+ dependency-scan:
89
+ stage: security
90
+ <<: *uv_sync
91
+ script:
92
+ - echo "Scanning dependencies for known vulnerabilities..."
93
+ - uv run safety check --json || true # Don't fail pipeline on vulnerabilities yet
94
+ allow_failure: true
95
+ artifacts:
96
+ reports:
97
+ dependency_scanning: gl-dependency-scanning-report.json
98
+ expire_in: 30 days
99
+ only:
100
+ - main
101
+ - develop
102
+
103
+ bandit-security-scan:
104
+ stage: security
105
+ <<: *uv_sync
106
+ script:
107
+ - echo "Running Bandit security linter..."
108
+ - uv run bandit -r sfacts/ -f json -o bandit-report.json --severity-level medium --confidence-level high || true
109
+ - uv run bandit -r sfacts/ -f screen --severity-level medium --confidence-level high
110
+ allow_failure: false
111
+ artifacts:
112
+ paths:
113
+ - bandit-report.json
114
+ expire_in: 30 days
115
+
116
+ # ============================================================================
117
+ # QUALITY STAGE: Code quality metrics
118
+ # ============================================================================
119
+
120
+ code-quality:
121
+ stage: security
122
+ <<: *uv_sync
123
+ script:
124
+ - echo "Generating code quality report..."
125
+ - echo "=== Cyclomatic Complexity ==="
126
+ - uv run radon cc sfacts/ -a -s
127
+ - echo "=== Maintainability Index ==="
128
+ - uv run radon mi sfacts/ -s
129
+ allow_failure: true
130
+ artifacts:
131
+ paths:
132
+ - htmlcov/
133
+ expire_in: 30 days
134
+
135
+ # ============================================================================
136
+ # DEPLOY STAGE: Publish to PyPI via Trusted Publisher (OIDC)
137
+ # ============================================================================
138
+
139
+ publish_pypi:
140
+ stage: deploy
141
+ image: python:3.11
142
+ environment: release # Must match the Environment defined in GitLab & PyPI
143
+ cache: [] # Do not restore uv cache / .venv into build context
144
+ id_tokens:
145
+ PYPI_ID_TOKEN:
146
+ aud: pypi
147
+ script:
148
+ - rm -rf .uv-cache .venv dist build
149
+ - pip install uv
150
+ - uv build
151
+ - uv publish --trusted-publishing always
152
+ rules:
153
+ - if: $CI_COMMIT_TAG
@@ -0,0 +1,208 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.3.0] — 2026-04-13
9
+
10
+ ### Fixed
11
+
12
+ - **VRF assignment bug in bulk sync** (`sfacts/core/netbox/bulk_sync.py`)
13
+ - `_collect_all_objects` was traversing the wrong nesting level (`vrf_info["interfaces"]`
14
+ instead of `vrf_info["interfaces"]["interface"]`), causing every interface to fall back
15
+ to the `default` VRF regardless of actual assignment in NAPALM facts
16
+ - Also limited the search to VLAN interfaces only; now all interface types are checked
17
+ - Non-default VRFs now correctly take priority over `default`/`global`/`master`
18
+ - **VRF assignment bug in standard sync** (`sfacts/core/netbox/vrf_sync.py`)
19
+ - `_get_interface_vrf_from_facts` and `_sync_device_vrf_assignments` updated with the same
20
+ non-default priority logic
21
+ - **`purge-netbox` did not remove device types and device roles**
22
+ - Full purge now deletes `device_types` and `device_roles` after devices, respecting FK
23
+ constraints
24
+ - **Discovery drops devices with unrecognised device types**
25
+ - `SSHDetect.autodetect()` returning `None` (e.g. Ruijie SGE300) previously caused the
26
+ device to be silently discarded
27
+ - Now falls back to `device_type=generic` and still extracts hostname, keeping the device
28
+ in the discovery artifact
29
+ - **ANSI escape sequences in hostname** (`sfacts/core/scanner.py`)
30
+ - `find_prompt()` output could contain ANSI codes (e.g. `\x1b[K`) that polluted the
31
+ extracted hostname; stripped with a regex before storing
32
+ - **`collect-facts` error on `generic` devices** (`sfacts/core/collector.py`)
33
+ - Previously raised `Unsupported device type: generic`, failing the entire flow run
34
+ - Now returns partial facts (hostname, empty interfaces/IPs/VRFs) with
35
+ `connection_successful=True` and a descriptive note; pipeline continues
36
+
37
+ ### Added
38
+
39
+ - **`facts-list` table artifact in `collect-facts`**
40
+ - Mirrors the `devices-list` table from `discover-network`
41
+ - Columns: Hostname, IP, Device Type, Status (✅/❌), Notes (model+version or error)
42
+ - `facts-summary` markdown also updated to list per-device hostname, IP, type, status, notes
43
+ - **VRF & interface test coverage** (`tests/unit/`)
44
+ - `TestVrfSyncMixin` (8 tests) — covers `_get_interface_vrf_from_facts` and
45
+ `_sync_device_vrf_assignments` including the overlap regression case (interface in both
46
+ default and explicit VRF → explicit wins)
47
+ - `TestBulkVrfCollection` (6 tests) — covers `_collect_all_objects` bulk path for Vlan,
48
+ Ethernet, fallback-to-default, overlap priority, VRF name collection, fixture end-to-end
49
+ - `conftest.py` `sample_facts_data` VRF structure updated to correct NAPALM
50
+ `interfaces.interface` nested format; `switch01` gets a `data` VRF for `Vlan10`
51
+ - **Blueprint: generic device re-identification**
52
+ - `docs/blueprint-generic-device-reidentification.md` — full design for optional secondary
53
+ probe-based re-identification of `generic` devices, NAPALM handoff, and Netmiko-only
54
+ collector fallback
55
+
56
+ ### Changed
57
+
58
+ - **NetBox sync code split into focused modules** (`sfacts/core/netbox/` package)
59
+ - `sfacts/core/netbox_sync.py` and `sfacts/core/netbox_bulk_sync.py` (God classes, ~1500
60
+ and ~1015 lines) replaced by a structured package:
61
+ - `client.py` — `NetBoxClient`, connection management
62
+ - `base_objects.py` — `BaseObjectsMixin`, sites / roles / manufacturers / device types
63
+ - `interface_sync.py` — `InterfaceSyncMixin`
64
+ - `vrf_sync.py` — `VrfSyncMixin`
65
+ - `ipam_sync.py` — `IpamSyncMixin`, VLANs / prefixes / IPs
66
+ - `mac_sync.py` — `MacSyncMixin`
67
+ - `device_sync.py` — `NetBoxSyncManager` (orchestrator, inherits all mixins)
68
+ - `bulk_helpers.py` — `BulkHelpersMixin`, adaptive batching
69
+ - `bulk_sync.py` — `NetBoxBulkSyncManager`
70
+ - Old module paths re-exported for backward compatibility then removed
71
+
72
+ ---
73
+
74
+ ## [2.2.0] — 2026-04-12
75
+
76
+ ### Fixed
77
+
78
+ - **Resolved all ruff/mypy lint errors** across new Prefect files (flows, tasks, deployments)
79
+ - Fixed E501, F841, B007, B904, SIM102, E722, S112, C401, W291 violations
80
+ - Applied ruff auto-fixes and unsafe-fixes for auto-fixable issues
81
+ - Added `type: ignore` for Prefect library false positives
82
+ - Updated mypy config: `python_version` 3.8 → 3.9, exclude `.venv`, skip `docket`
83
+ - Added `S107`/`S110`/`S112` to ruff per-file-ignores for flows and tests
84
+ - **Fixed `__init__.py` version** from `1.0.0` to `2.1.0` to match `pyproject.toml`
85
+ - **NetBox bulk sync now handles existing manufacturers, device roles, and device types**
86
+ - Fixed issue where bulk sync would create 0 devices when objects already existed in NetBox
87
+ - Added fallback logic to fetch existing manufacturers, device roles, and device types from NetBox
88
+ - Gracefully handles 400 Bad Request errors for duplicate objects during bulk creation
89
+ - Ensures cache is properly populated with existing objects for successful device creation
90
+ - Tested with 5 devices: successfully creates devices, interfaces, IPs, and MACs
91
+
92
+ ### Added
93
+
94
+ - **Modular Prefect deployments with automatic artifact fetching**
95
+ - `sfacts/flows/modular_flows.py` — 3 independent deployments replacing 4 monolithic ones:
96
+ - `discover-network` — Auto-detects subnet vs tunnel mode from target format
97
+ - `collect-facts` — Gathers device facts with auto-fetch from latest discovery
98
+ - `sync-to-netbox` — Syncs to NetBox with auto-fetch from latest facts
99
+ - Automatic artifact retrieval using Prefect API:
100
+ - Auto-fetches latest artifacts by key (`devices-json`, `facts-json`)
101
+ - Optional `flow_run_id` parameter for specific artifact selection
102
+ - Proper Prefect filter objects for reliable artifact queries
103
+ - Enhanced artifact creation for all deployments:
104
+ - Discovery: summary markdown with hostnames, device table, JSON for next step
105
+ - Facts: summary markdown with hostnames and software versions, full JSON for NetBox sync
106
+ - NetBox sync: results summary with statistics
107
+ - `serve.py` — Deployment server script for running all flows
108
+ - `docs/prefect-modular-workflows.md` — Complete workflow documentation
109
+ - Benefits:
110
+ - Zero manual JSON copying between deployments
111
+ - Flexible workflows: run steps independently or skip discovery
112
+ - Clean separation of concerns
113
+ - Data persistence via Prefect artifacts
114
+ - Beautiful UI visualization with tables and markdown
115
+
116
+ - **GitLab CI/CD pipeline** (`.gitlab-ci.yml`, `.gitlab-ci-simple.yml`)
117
+ - Four stages: lint, test, security, build
118
+ - Ruff linting and format checking, MyPy type checking
119
+ - Unit tests with coverage reporting (Cobertura XML + HTML)
120
+ - Dependency vulnerability scanning (Safety), Bandit security analysis
121
+ - Code quality metrics (Radon cyclomatic complexity, maintainability index)
122
+ - Python package building on main branch and tags
123
+ - Added `bandit>=1.7.10` to dev-dependencies
124
+ - Local CI runner script: `scripts/test-ci-locally.sh`
125
+ - GitLab merge request template: `.gitlab/merge_request_templates/default.md`
126
+ - Documentation: `docs/CI-CD-SETUP.md`
127
+
128
+ - **Hostname extraction during discovery**
129
+ - `sfacts/core/scanner.py` — Enhanced device identification to extract hostnames
130
+ - Uses Netmiko's `base_prompt` attribute for robust hostname extraction
131
+ - Handles various prompt formats: `user@hostname`, `hostname/vsys1`, `hostname#`, etc.
132
+ - Hostnames available immediately in discovery artifacts (no need to wait for fact collection)
133
+ - Falls back to IP-based or port-based names if extraction fails
134
+
135
+ - **Prefect Secret block support for credentials**
136
+ - Secure credential storage using Prefect Secret blocks
137
+ - Separate blocks for username (`lab-username`) and password (`lab-password`)
138
+ - Automatic credential loading from Secret blocks (default behavior)
139
+ - Manual credential override option via `use_secret_block=False`
140
+ - No hardcoded credentials in deployment parameters
141
+ - Credentials not visible in Prefect UI or logs
142
+ - Easy credential rotation without code changes
143
+
144
+ - **Prefect workflow integration** (`sfacts[workflows]` optional extra)
145
+ - `sfacts/tasks/` — Prefect tasks wrapping the core sfacts API:
146
+ - `discover_devices` — subnet scan with automatic credential injection and retries
147
+ - `discover_tunnel_devices` — SSH tunnel scan (containerlab / remote labs)
148
+ - `collect_facts` — parallel NAPALM fact collection with retry logic
149
+ - `sync_to_netbox` — NetBox IPAM sync (standard and bulk modes) with retries
150
+ - `test_netbox_connection` — NetBox connectivity check
151
+ - `export_nornir_inventory` — Nornir inventory YAML export
152
+ - `sfacts/flows/` — Legacy flows (maintained for compatibility):
153
+ - `full_inventory_flow` — complete pipeline: discover → collect → optional export → optional NetBox sync
154
+ - `tunnel_inventory_flow` — same pipeline over SSH tunnel
155
+ - `discovery_flow` — lightweight subnet scan (no fact collection)
156
+ - `tunnel_discovery_flow` — lightweight scan over SSH tunnel
157
+ - Discovery results are credential-safe when saved to disk (credentials stripped before write)
158
+ - Unit tests for all tasks and flows (`tests/unit/test_tasks/`, `tests/unit/test_flows/`)
159
+ - Documentation: `docs/prefect-integration.md`, `docs/prefect-modular-workflows.md`
160
+
161
+ ### Changed
162
+
163
+ - **Prefect deployment architecture**:
164
+ - Default `export_inventory=False` for Prefect deployments (artifacts handle persistence)
165
+ - All artifact creation functions are async and properly awaited
166
+ - Deployments now use artifact-based data pipeline instead of in-memory state
167
+ - Credentials now use Prefect Secret blocks instead of dict parameters
168
+ - Device keys in fact collection use hostnames instead of `host:port` format
169
+ - **Discovery improvements**:
170
+ - Scanner now extracts hostnames during device identification
171
+ - Artifacts display actual device hostnames instead of IP addresses
172
+ - **Fact collection improvements**:
173
+ - Summary artifacts now show software versions from NAPALM `get_facts`
174
+ - Uses `software_version` field instead of deprecated `version` field
175
+ - `pyproject.toml`: added `workflows` optional dependency group (`prefect>=3.0.0`)
176
+ - `pyproject.toml`: added `prefect>=3.0.0` to dev dependencies for test execution
177
+ - `pyproject.toml`: extended per-file ruff ignores for tests (`S105`, `S106`) to cover
178
+ credential fixtures used in unit tests
179
+
180
+ ---
181
+
182
+ ## [2.1.0] — 2025-09-11
183
+
184
+ ### Added
185
+
186
+ - Comprehensive test suite (unit, integration, e2e, performance)
187
+ - Ruff code-quality improvements across the entire codebase
188
+
189
+ ## [2.0.0]
190
+
191
+ ### Added
192
+
193
+ - NetBox bulk synchronization (`NetBoxBulkSyncManager`) with 10-50x performance improvement
194
+ - `sfacts netbox sync --bulk` CLI flag
195
+ - `sfacts netbox validate` subcommand
196
+ - Primary MAC address assignment (`primary_mac_assignment.py`)
197
+
198
+ ## [1.0.0]
199
+
200
+ ### Added
201
+
202
+ - Initial release
203
+ - Network device discovery via subnet scanning (`SubnetScanner`, `NetworkDiscovery`)
204
+ - Device fact collection via NAPALM (`BasicFactCollector`)
205
+ - NetBox IPAM synchronization (`NetBoxSyncManager`)
206
+ - Nornir inventory export (`NornirInventoryExporter`)
207
+ - Secure credential management (`CredentialManager`)
208
+ - CLI: `sfacts discover`, `sfacts facts`, `sfacts export`, `sfacts netbox`
sfacts-2.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Netodata Labs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.