{"id":50557077,"url":"https://github.com/networktocode/nautobot-job-upgrade-assessment","last_synced_at":"2026-06-04T08:01:44.647Z","repository":{"id":361251607,"uuid":"1229375004","full_name":"networktocode/nautobot-job-upgrade-assessment","owner":"networktocode","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-29T19:03:43.000Z","size":251,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-29T21:06:06.273Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/networktocode.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-05T01:26:02.000Z","updated_at":"2026-05-29T19:03:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/networktocode/nautobot-job-upgrade-assessment","commit_stats":null,"previous_names":["networktocode/nautobot-job-upgrade-assessment"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/networktocode/nautobot-job-upgrade-assessment","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networktocode%2Fnautobot-job-upgrade-assessment","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networktocode%2Fnautobot-job-upgrade-assessment/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networktocode%2Fnautobot-job-upgrade-assessment/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networktocode%2Fnautobot-job-upgrade-assessment/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/networktocode","download_url":"https://codeload.github.com/networktocode/nautobot-job-upgrade-assessment/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/networktocode%2Fnautobot-job-upgrade-assessment/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33895175,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-04T02:00:06.755Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-04T08:01:43.706Z","updated_at":"2026-06-04T08:01:44.636Z","avatar_url":"https://github.com/networktocode.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nautobot Upgrade Readiness Assessment Job\n\nA version-agnostic Nautobot Job that inspects a running Nautobot instance and\nproduces a structured JSON report for upgrade planning. The same file runs on\nNautobot **1.x, 2.x, and 3.x** — feature detection is dynamic, so checks that\ndon't apply to the running version emit `null`/`skipped` rather than error.\n\nThe JSON output is designed to be consumed by the companion web app in\n[networktocode-llc/nautobot-upgrade-assessment-server](https://github.com/networktocode-llc/nautobot-upgrade-assessment-server),\nwhich produces an interactive, PDF/Word-exportable report from it.\n\n---\n\n## What it reports\n\n| Area | Details |\n|------|---------|\n| Environment | Nautobot, Python, Django, database engine \u0026 version, queue backend (Celery vs RQ) |\n| Settings hygiene | Removed/deprecated settings still present, `STRICT_FILTERING`, pre-1.5 Celery queue pinning, debug mode, auth backends |\n| Compatibility matrix | Runtime Python / Django / DB versions vs the target's requirements, with `ok` / `runtime_too_old` / `runtime_too_new` / `db_too_old` / `mismatch` verdicts per axis |\n| Installed apps | Runtime introspection (models, views, API, filtersets, nav) + per-app source-code scan using pylint-nautobot catalogs; Python code, HTML templates, JS/CSS, and HTML-embedded-in-Python all scanned for deprecated patterns |\n| Bootstrap 3 → 5 migration | Per-app scan for the classes, `data-bs-*` attributes, grid changes (`col-xs-*`, `col-*-offset-*`, push/pull), Nautobot-specific class renames (`nb-*`), and jQuery usage covered by the upstream [Bootstrap v3→v5 guide](https://docs.nautobot.com/projects/core/en/stable/development/apps/migration/from-v2/upgrading-from-bootstrap-v3-to-v5/). Each finding carries its Bootstrap-5/Nautobot replacement hint |\n| App compatibility | `Requires-Dist` constraints from each app's metadata vs the chosen target version (reports `blocks_upgrade_to_v2`, `blocks_upgrade_to_v3`, `blocks_target_version`) |\n| Jobs | Registered jobs with per-job code-complexity analysis; scheduled-jobs detail (interval, enabled, last-run) |\n| Job approval readiness | Jobs and ScheduledJobs still using the `approval_required` flag (removed in 3.0 / 3.1 in favor of the ApprovalWorkflow model) |\n| Task-queue migration (2.4) | ScheduledJobs on the legacy `queue` CharField vs the new `job_queue` FK; Jobs still declaring `task_queues` |\n| Data-model inventory | Object counts for DCIM, IPAM, Extras, Tenancy, Circuits, Virtualization — including `DeviceRedundancyGroup` (1.5+) and `InterfaceRedundancyGroup` (1.6+) |\n| IPAM Namespace migration (1.x → 2.x) | VRFs with `enforce_unique`, prefixes and IPs that would collapse into the default Global namespace, duplicate-prefix / duplicate-IP candidates that will land in the Cleanup namespace, and — on 2.x+ — the realized per-namespace distribution |\n| Field-state deltas | Row counts where legacy single-FK fields still carry data: `Prefix.location` / `VLAN.location` (2.2+ became M2M), `Device.cluster` (3.0 became M2M) |\n| UI Component Framework impact (2.4) | Third-party `TemplateExtension` subclasses whose target model's detail view migrated to the new framework |\n| Integrations | SSoT adapters, webhooks, git repositories (with credential-style classification: inline creds vs SecretsGroup), secrets groups |\n| API consumers (inbound) | Tokens, recent API-driven changes by user, external auth backends, **writes against removed-in-target models** (strong signal of callers still using deprecated endpoints) |\n| Feature audits | Dynamic groups, saved views, permission constraints, GraphQL queries (with per-query deprecated-token hit counts) |\n| Deprecated API URLs | Webhook `payload_url`s, ConfigContext JSON, ExportTemplate bodies, and Job source scanned for pre-2.0 REST paths (`/api/dcim/sites/`, etc.) |\n| Content-type feature usage | CustomField / Relationship / Status / Tag / Webhook / CustomLink / ExportTemplate / ComputedField / JobHook / JobButton / Note rows whose `content_types` M2M or `content_type` FK points at a model removed in 2.0 |\n| Read-traffic signals | Opportunistic detection via the in-process `django-prometheus` registry (aggregated across workers when `PROMETHEUS_MULTIPROC_DIR` is set); emits a drop-in middleware snippet for full read-attribution when the registry isn't useful |\n| Migrations | Applied per-app counts and any pending migrations |\n| Retention | ObjectChange / JobResult / JobLogEntry / admin-log row counts (pre-upgrade pruning candidates) |\n| Pre-migrate audit | Captured output from Nautobot's `pre_migrate`, `audit_dynamic_groups`, and `audit_graphql_queries` management commands (when available on the running version) |\n\n### Data safety\n\nThe output contains **no device credentials, passwords, custom-field values, or\nuser-generated content**. Only schema shape, object counts, import paths, class\nnames, and deprecated-pattern evidence are collected. See the module docstring\nin [jobs/nautobot_upgrade_readiness.py](jobs/nautobot_upgrade_readiness.py) for\nthe full data-handling rationale.\n\n---\n\n## Requirements\n\n- Nautobot 1.3+, 2.x, or 3.x (verified patterns for 1.5 through 3.1)\n- Read-only access to the Nautobot database (the Job is marked `read_only = True`)\n- A user with permission to run Jobs and view Job Results\n\nNo extra Python dependencies beyond Nautobot itself. Optional but nice-to-have:\n\n- **`django-prometheus`** (or the `nautobot_capacity_metrics` app) — enables the\n  \"Read-traffic signals\" section. If absent, the Job emits a ready-to-install\n  middleware snippet in the output instead.\n- **`PROMETHEUS_MULTIPROC_DIR`** configured and shared between uWSGI + Celery —\n  lets the Prometheus probe see aggregated counts across all Nautobot workers\n  instead of only its own process.\n\n---\n\n## Deploying the Job\n\nPick one of the three standard ways Nautobot loads custom Jobs.\n\n### Option A — Git repository (recommended)\n\nThis repo is already laid out for Nautobot's Git Repository sync — the Job\nlives at `jobs/nautobot_upgrade_readiness.py`, which is exactly where Nautobot\nexpects to find it.\n\n1. In Nautobot: **Extensibility → Git Repositories → Add**, point it at this\n   repository (or your fork), enable the **jobs** provided content type, and\n   **Sync**.\n2. Nautobot auto-discovers the Job after sync — no service restart required.\n\n### Option B — `JOBS_ROOT`\n\n1. On your Nautobot host, identify the value of `JOBS_ROOT` in\n   `nautobot_config.py` (default: `$NAUTOBOT_ROOT/jobs`).\n2. Copy the file:\n   ```bash\n   cp jobs/nautobot_upgrade_readiness.py $JOBS_ROOT/\n   chown nautobot:nautobot $JOBS_ROOT/nautobot_upgrade_readiness.py\n   ```\n3. Restart the Nautobot web and worker services so they pick up the new file:\n   ```bash\n   sudo systemctl restart nautobot nautobot-worker\n   # or, for docker-compose based installs:\n   docker compose restart nautobot nautobot-worker\n   ```\n\n### Option C — Container-based Nautobot\n\nIf your Nautobot runs in a container and you own the image, bake the file in:\n\n```dockerfile\nCOPY jobs/nautobot_upgrade_readiness.py /opt/nautobot/jobs/\n```\n\nOr bind-mount it from the host in your compose file:\n\n```yaml\nservices:\n  nautobot:\n    volumes:\n      - ./jobs/nautobot_upgrade_readiness.py:/opt/nautobot/jobs/nautobot_upgrade_readiness.py:ro\n```\n\nThen `docker compose restart nautobot nautobot-worker`.\n\n---\n\n## Enabling and running the Job\n\nAfter deploy, enable and run the Job once:\n\n1. **Jobs → Jobs** — find **Upgrade Readiness Assessment**.\n2. Click **Edit Job** and set **Enabled = true** (first-time deploys only).\n3. Click **Run Job Now**.\n4. On the Job form, pick a **Target Nautobot Version** from the dropdown.\n   Supported targets: `2.3`, `2.4`, `3.0`, `3.1` (default: `3.1`). The chosen\n   target drives the compatibility-matrix check and the `blocks_target_version`\n   flag on each installed app. Add more targets by editing\n   `TARGET_VERSION_REQUIREMENTS` at the top of the Job file.\n5. Submit. The Job typically finishes in under a minute on a medium-sized\n   install; large installs with many plugins may take a few minutes.\n\n### Retrieving the JSON output\n\nOn the **Job Result** page, the structured JSON is attached as a file artifact\nnamed something like `assessment_job_output.json`. Click **Download**.\n\n**A note on Nautobot 1.x output shape.** Nautobot 1.x stringifies whatever\n`run()` returns, so on 1.x the file looks like:\n\n```json\n{\"output\": \"{\\\"assessment_metadata\\\": {...}}\"}\n```\n\nThe Job pre-serializes to compact JSON on 1.x (no embedded newlines) so\n`json.loads(data[\"output\"])` gives you a clean object. On 2.x+ the\n`JobResult.result` field stores the dict natively, with no outer wrapping.\n\n**The companion webapp unwraps either shape transparently on upload**, so\nin normal use you don't need to clean the file first.\n\nYou can also fetch it via the REST API:\n\n```bash\ncurl -sSf \\\n    -H \"Authorization: Token $NAUTOBOT_TOKEN\" \\\n    \"https://nautobot.example.com/api/extras/job-results/$RESULT_ID/\" \\\n    | jq -r '.data' \u003e assessment_job_output.json\n```\n\n---\n\n## Companion report app\n\nThe JSON output is consumed by the companion web app in\n[networktocode-llc/nautobot-upgrade-assessment-server](https://github.com/networktocode-llc/nautobot-upgrade-assessment-server),\nwhich renders it as an interactive report with PDF/Word export. See that\nrepository's README for setup and usage.\n\n---\n\n## Troubleshooting\n\n| Symptom | Likely cause |\n|---------|--------------|\n| Job not visible under **Jobs → Jobs** | `nautobot` and `nautobot-worker` processes weren't restarted; or file permissions block import. Check Nautobot logs for `JobError`. |\n| Job runs but output is mostly `null` / `skipped` | Expected on older Nautobot versions — the Job reports what is available and marks the rest as not present. `null` for a model means \"doesn't exist on this version\"; `0` means \"exists but empty\". |\n| Target-version dropdown missing a version | Add the version to `TARGET_VERSION_REQUIREMENTS` near the top of the Job file; the dropdown rebuilds from that dict. |\n| \"Permission denied\" reading app source | The Nautobot worker user must be able to read the Python files of installed apps in its `site-packages`. |\n| `read_traffic_signals.prometheus_counters.legacy_view_hits` always empty | The Job's Celery worker has its own per-process Prometheus registry. Set `PROMETHEUS_MULTIPROC_DIR` (see the `prometheus_client` docs) and share it between uWSGI and Celery workers — then the probe aggregates across all processes. |\n| `pre_migrate_report` says `not_available` | The `pre_migrate` / `audit_dynamic_groups` / `audit_graphql_queries` management commands were added in Nautobot 2.x. On 1.x they simply don't exist — this is expected. |\n| Output on 1.x has `\\n`-escaped content | Nautobot 1.x wraps the Job's string return in `{\"output\": \"...\"}`. The webapp auto-unwraps this; to read the raw file by hand, run `jq -r .output assessment_job_output.json \\| jq`. |\n\n---\n\n## Development\n\nThe Job is a single self-contained file. Edits take effect after running\n**Sync Now** on the Git Repository (Option A) or restarting the Nautobot\nworker (Option B).\n\nFor syntax validation without a live Nautobot:\n\n```bash\npython -m py_compile jobs/nautobot_upgrade_readiness.py\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetworktocode%2Fnautobot-job-upgrade-assessment","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnetworktocode%2Fnautobot-job-upgrade-assessment","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnetworktocode%2Fnautobot-job-upgrade-assessment/lists"}