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.
- sfacts-2.3.0/.env.template +11 -0
- sfacts-2.3.0/.gitignore +144 -0
- sfacts-2.3.0/.gitlab-ci-simple.yml +85 -0
- sfacts-2.3.0/.gitlab-ci.yml +153 -0
- sfacts-2.3.0/CHANGELOG.md +208 -0
- sfacts-2.3.0/LICENSE +21 -0
- sfacts-2.3.0/PKG-INFO +414 -0
- sfacts-2.3.0/README.md +367 -0
- sfacts-2.3.0/bandit-report.json +488 -0
- sfacts-2.3.0/deploy.py +64 -0
- sfacts-2.3.0/pyproject.toml +177 -0
- sfacts-2.3.0/pytest.ini +39 -0
- sfacts-2.3.0/requirements.txt +25 -0
- sfacts-2.3.0/scripts/test-ci-locally.sh +86 -0
- sfacts-2.3.0/serve.py +79 -0
- sfacts-2.3.0/sfacts/__init__.py +17 -0
- sfacts-2.3.0/sfacts/cli/__init__.py +8 -0
- sfacts-2.3.0/sfacts/cli/discover.py +182 -0
- sfacts-2.3.0/sfacts/cli/export.py +177 -0
- sfacts-2.3.0/sfacts/cli/facts.py +219 -0
- sfacts-2.3.0/sfacts/cli/main.py +39 -0
- sfacts-2.3.0/sfacts/cli/netbox.py +705 -0
- sfacts-2.3.0/sfacts/core/__init__.py +9 -0
- sfacts-2.3.0/sfacts/core/collector.py +342 -0
- sfacts-2.3.0/sfacts/core/discovery.py +300 -0
- sfacts-2.3.0/sfacts/core/inventory.py +337 -0
- sfacts-2.3.0/sfacts/core/netbox/__init__.py +18 -0
- sfacts-2.3.0/sfacts/core/netbox/base_objects.py +194 -0
- sfacts-2.3.0/sfacts/core/netbox/bulk_helpers.py +164 -0
- sfacts-2.3.0/sfacts/core/netbox/bulk_sync.py +858 -0
- sfacts-2.3.0/sfacts/core/netbox/client.py +102 -0
- sfacts-2.3.0/sfacts/core/netbox/device_sync.py +361 -0
- sfacts-2.3.0/sfacts/core/netbox/interface_sync.py +199 -0
- sfacts-2.3.0/sfacts/core/netbox/ipam_sync.py +516 -0
- sfacts-2.3.0/sfacts/core/netbox/mac_sync.py +183 -0
- sfacts-2.3.0/sfacts/core/netbox/primary_mac_assignment.py +75 -0
- sfacts-2.3.0/sfacts/core/netbox/vrf_sync.py +171 -0
- sfacts-2.3.0/sfacts/core/scanner.py +389 -0
- sfacts-2.3.0/sfacts/deployments.py +228 -0
- sfacts-2.3.0/sfacts/flows/__init__.py +22 -0
- sfacts-2.3.0/sfacts/flows/discovery_only.py +198 -0
- sfacts-2.3.0/sfacts/flows/full_inventory.py +245 -0
- sfacts-2.3.0/sfacts/flows/modular_flows.py +1036 -0
- sfacts-2.3.0/sfacts/tasks/__init__.py +23 -0
- sfacts-2.3.0/sfacts/tasks/collect.py +43 -0
- sfacts-2.3.0/sfacts/tasks/discover.py +88 -0
- sfacts-2.3.0/sfacts/tasks/export.py +41 -0
- sfacts-2.3.0/sfacts/tasks/netbox.py +285 -0
- sfacts-2.3.0/sfacts/utils/__init__.py +7 -0
- sfacts-2.3.0/sfacts/utils/auth.py +242 -0
- sfacts-2.3.0/sfacts/utils/logger.py +100 -0
- 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
|
sfacts-2.3.0/.gitignore
ADDED
|
@@ -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.
|