diff --git a/contrib/netbox-housekeeping.service b/contrib/netbox-housekeeping.service deleted file mode 100644 index 4b0361fcb..000000000 --- a/contrib/netbox-housekeeping.service +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=NetBox Housekeeping Service -Documentation=https://docs.netbox.dev/ -After=network-online.target -Wants=network-online.target - -[Service] -Type=simple - -User=netbox -Group=netbox -WorkingDirectory=/opt/netbox - -ExecStart=/opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py housekeeping - -[Install] -WantedBy=multi-user.target diff --git a/contrib/netbox-housekeeping.sh b/contrib/netbox-housekeeping.sh deleted file mode 100755 index 5b1c46c5e..000000000 --- a/contrib/netbox-housekeeping.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# This shell script invokes NetBox's housekeeping management command, which -# intended to be run nightly. This script can be copied into your system's -# daily cron directory (e.g. /etc/cron.daily), or referenced directly from -# within the cron configuration file. -# -# If NetBox has been installed into a nonstandard location, update the paths -# below. -/opt/netbox/venv/bin/python /opt/netbox/netbox/manage.py housekeeping diff --git a/contrib/netbox-housekeeping.timer b/contrib/netbox-housekeeping.timer deleted file mode 100644 index 16facb05c..000000000 --- a/contrib/netbox-housekeeping.timer +++ /dev/null @@ -1,13 +0,0 @@ -[Unit] -Description=NetBox Housekeeping Timer -Documentation=https://docs.netbox.dev/ -After=network-online.target -Wants=network-online.target - -[Timer] -OnCalendar=daily -AccuracySec=1h -Persistent=true - -[Install] -WantedBy=multi-user.target diff --git a/docs/administration/housekeeping.md b/docs/administration/housekeeping.md deleted file mode 100644 index 674ceb312..000000000 --- a/docs/administration/housekeeping.md +++ /dev/null @@ -1,49 +0,0 @@ -# Housekeeping - -NetBox includes a `housekeeping` management command that should be run nightly. This command handles: - -* Clearing expired authentication sessions from the database -* Deleting changelog records older than the configured [retention time](../configuration/miscellaneous.md#changelog_retention) -* Deleting job result records older than the configured [retention time](../configuration/miscellaneous.md#job_retention) -* Check for new NetBox releases (if [`RELEASE_CHECK_URL`](../configuration/miscellaneous.md#release_check_url) is set) - -This command can be invoked directly, or by using the shell script provided at `/opt/netbox/contrib/netbox-housekeeping.sh`. - -## Scheduling - -### Using Cron - -This script can be linked from your cron scheduler's daily jobs directory (e.g. `/etc/cron.daily`) or referenced directly within the cron configuration file. - -```shell -sudo ln -s /opt/netbox/contrib/netbox-housekeeping.sh /etc/cron.daily/netbox-housekeeping -``` - -!!! note - On Debian-based systems, be sure to omit the `.sh` file extension when linking to the script from within a cron directory. Otherwise, the task may not run. - -### Using Systemd - -First, create symbolic links for the systemd service and timer files. Link the existing service and timer files from the `/opt/netbox/contrib/` directory to the `/etc/systemd/system/` directory: - -```bash -sudo ln -s /opt/netbox/contrib/netbox-housekeeping.service /etc/systemd/system/netbox-housekeeping.service -sudo ln -s /opt/netbox/contrib/netbox-housekeeping.timer /etc/systemd/system/netbox-housekeeping.timer -``` - -Then, reload the systemd configuration and enable the timer to start automatically at boot: - -```bash -sudo systemctl daemon-reload -sudo systemctl enable --now netbox-housekeeping.timer -``` - -Check the status of your timer by running: - -```bash -sudo systemctl list-timers --all -``` - -This command will show a list of all timers, including your `netbox-housekeeping.timer`. Make sure the timer is active and properly scheduled. - -That's it! Your NetBox housekeeping service is now configured to run daily using systemd. diff --git a/docs/installation/3-netbox.md b/docs/installation/3-netbox.md index 60a118977..994b2a0a0 100644 --- a/docs/installation/3-netbox.md +++ b/docs/installation/3-netbox.md @@ -264,18 +264,6 @@ cd /opt/netbox/netbox python3 manage.py createsuperuser ``` -## Schedule the Housekeeping Task - -NetBox includes a `housekeeping` management command that handles some recurring cleanup tasks, such as clearing out old sessions and expired change records. Although this command may be run manually, it is recommended to configure a scheduled job using the system's `cron` daemon or a similar utility. - -A shell script which invokes this command is included at `contrib/netbox-housekeeping.sh`. It can be copied to or linked from your system's daily cron task directory, or included within the crontab directly. (If installing NetBox into a nonstandard path, be sure to update the system paths within this script first.) - -```shell -sudo ln -s /opt/netbox/contrib/netbox-housekeeping.sh /etc/cron.daily/netbox-housekeeping -``` - -See the [housekeeping documentation](../administration/housekeeping.md) for further details. - ## Test the Application At this point, we should be able to run NetBox's development server for testing. We can check by starting a development instance locally. diff --git a/docs/installation/upgrading.md b/docs/installation/upgrading.md index 21ffa9766..c3a1c0d0d 100644 --- a/docs/installation/upgrading.md +++ b/docs/installation/upgrading.md @@ -183,13 +183,3 @@ Finally, restart the gunicorn and RQ services: ```no-highlight sudo systemctl restart netbox netbox-rq ``` - -## 6. Verify Housekeeping Scheduling - -If upgrading from a release prior to NetBox v3.0, check that a cron task (or similar scheduled process) has been configured to run NetBox's nightly housekeeping command. A shell script which invokes this command is included at `contrib/netbox-housekeeping.sh`. It can be linked from your system's daily cron task directory, or included within the crontab directly. (If NetBox has been installed in a nonstandard path, be sure to update the system paths within this script first.) - -```shell -sudo ln -s /opt/netbox/contrib/netbox-housekeeping.sh /etc/cron.daily/netbox-housekeeping -``` - -See the [housekeeping documentation](../administration/housekeeping.md) for further details. diff --git a/docs/release-notes/version-3.0.md b/docs/release-notes/version-3.0.md index 06b889c22..a7003eedf 100644 --- a/docs/release-notes/version-3.0.md +++ b/docs/release-notes/version-3.0.md @@ -434,7 +434,7 @@ A new management command has been added: `manage.py housekeeping`. This command * Delete change log records which have surpassed the configured retention period (if configured) * Check for new NetBox releases (if enabled) -A convenience script for calling this command via an automated scheduler has been included at `/contrib/netbox-housekeeping.sh`. Please see the [housekeeping documentation](../administration/housekeeping.md) for further details. +A convenience script for calling this command via an automated scheduler has been included at `/contrib/netbox-housekeeping.sh`. Please see the housekeeping documentation for further details. #### Custom Queue Support for Plugins ([#6651](https://github.com/netbox-community/netbox/issues/6651)) diff --git a/mkdocs.yml b/mkdocs.yml index 27526bd26..25ac8e3b8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -158,7 +158,6 @@ nav: - Okta: 'administration/authentication/okta.md' - Permissions: 'administration/permissions.md' - Error Reporting: 'administration/error-reporting.md' - - Housekeeping: 'administration/housekeeping.md' - Replicating NetBox: 'administration/replicating-netbox.md' - NetBox Shell: 'administration/netbox-shell.md' - Data Model: diff --git a/netbox/core/jobs.py b/netbox/core/jobs.py index b3dfaf1e7..3bb2427f0 100644 --- a/netbox/core/jobs.py +++ b/netbox/core/jobs.py @@ -1,8 +1,16 @@ import logging -import requests import sys +from datetime import timedelta +from importlib import import_module +import requests from django.conf import settings +from django.core.cache import cache +from django.utils import timezone +from packaging import version + +from core.models import Job, ObjectChange +from netbox.config import Config from netbox.jobs import JobRunner, system_job from netbox.search.backends import search_backend from utilities.proxy import resolve_proxies @@ -50,16 +58,23 @@ class SystemHousekeepingJob(JobRunner): if settings.DEBUG or 'test' in sys.argv: return - # TODO: Migrate other housekeeping functions from the `housekeeping` management command. self.send_census_report() + self.clear_expired_sessions() + self.prune_changelog() + self.delete_expired_jobs() + self.check_for_new_releases() @staticmethod def send_census_report(): """ Send a census report (if enabled). """ - # Skip if census reporting is disabled - if settings.ISOLATED_DEPLOYMENT or not settings.CENSUS_REPORTING_ENABLED: + logging.info("Reporting census data...") + if settings.ISOLATED_DEPLOYMENT: + logging.info("ISOLATED_DEPLOYMENT is enabled; skipping") + return + if not settings.CENSUS_REPORTING_ENABLED: + logging.info("CENSUS_REPORTING_ENABLED is disabled; skipping") return census_data = { @@ -76,3 +91,94 @@ class SystemHousekeepingJob(JobRunner): ) except requests.exceptions.RequestException: pass + + @staticmethod + def clear_expired_sessions(): + """ + Clear any expired sessions from the database. + """ + logging.info("Clearing expired sessions...") + engine = import_module(settings.SESSION_ENGINE) + try: + engine.SessionStore.clear_expired() + logging.info("Sessions cleared.") + except NotImplementedError: + logging.warning( + f"The configured session engine ({settings.SESSION_ENGINE}) does not support " + f"clearing sessions; skipping." + ) + + @staticmethod + def prune_changelog(): + """ + Delete any ObjectChange records older than the configured changelog retention time (if any). + """ + logging.info("Pruning old changelog entries...") + config = Config() + if not config.CHANGELOG_RETENTION: + logging.info("No retention period specified; skipping.") + return + + cutoff = timezone.now() - timedelta(days=config.CHANGELOG_RETENTION) + logging.debug(f"Retention period: {config.CHANGELOG_RETENTION} days") + logging.debug(f"Cut-off time: {cutoff}") + + count = ObjectChange.objects.filter(time__lt=cutoff).delete()[0] + logging.info(f"Deleted {count} expired records") + + @staticmethod + def delete_expired_jobs(): + """ + Delete any jobs older than the configured retention period (if any). + """ + logging.info("Deleting expired jobs...") + config = Config() + if not config.JOB_RETENTION: + logging.info("No retention period specified; skipping.") + return + + cutoff = timezone.now() - timedelta(days=config.JOB_RETENTION) + logging.debug(f"Retention period: {config.CHANGELOG_RETENTION} days") + logging.debug(f"Cut-off time: {cutoff}") + + count = Job.objects.filter(created__lt=cutoff).delete()[0] + logging.info(f"Deleted {count} expired records") + + @staticmethod + def check_for_new_releases(): + """ + Check for new releases and cache the latest release. + """ + logging.info("Checking for new releases...") + if settings.ISOLATED_DEPLOYMENT: + logging.info("ISOLATED_DEPLOYMENT is enabled; skipping") + return + if not settings.RELEASE_CHECK_URL: + logging.info("RELEASE_CHECK_URL is not set; skipping") + return + + # Fetch the latest releases + logging.debug(f"Release check URL: {settings.RELEASE_CHECK_URL}") + try: + response = requests.get( + url=settings.RELEASE_CHECK_URL, + headers={'Accept': 'application/vnd.github.v3+json'}, + proxies=resolve_proxies(url=settings.RELEASE_CHECK_URL) + ) + response.raise_for_status() + except requests.exceptions.RequestException as exc: + logging.error(f"Error fetching release: {exc}") + return + + # Determine the most recent stable release + releases = [] + for release in response.json(): + if 'tag_name' not in release or release.get('devrelease') or release.get('prerelease'): + continue + releases.append((version.parse(release['tag_name']), release.get('html_url'))) + latest_release = max(releases) + logging.debug(f"Found {len(response.json())} releases; {len(releases)} usable") + logging.info(f"Latest release: {latest_release[0]}") + + # Cache the most recent release + cache.set('latest_release', latest_release, None) diff --git a/netbox/extras/management/commands/housekeeping.py b/netbox/extras/management/commands/housekeeping.py index b8c7eab7d..0d8a7e0b9 100644 --- a/netbox/extras/management/commands/housekeeping.py +++ b/netbox/extras/management/commands/housekeeping.py @@ -14,9 +14,16 @@ from utilities.proxy import resolve_proxies class Command(BaseCommand): - help = "Perform nightly housekeeping tasks. (This command can be run at any time.)" + help = "Perform nightly housekeeping tasks [DEPRECATED]" def handle(self, *args, **options): + self.stdout.write( + "Running this command is no longer necessary: All housekeeping tasks\n" + "are addressed automatically via NetBox's built-in job scheduler. It\n" + "will be removed in a future release.", + self.style.WARNING + ) + config = Config() # Clear expired authentication sessions (essentially replicating the `clearsessions` command)