2021-06-11 10:44:17 -04:00
|
|
|
from datetime import timedelta
|
|
|
|
from importlib import import_module
|
|
|
|
|
2021-07-07 21:37:35 -04:00
|
|
|
import requests
|
2021-06-11 10:44:17 -04:00
|
|
|
from django.conf import settings
|
2021-07-07 21:37:35 -04:00
|
|
|
from django.core.cache import cache
|
2021-06-11 10:44:17 -04:00
|
|
|
from django.core.management.base import BaseCommand
|
|
|
|
from django.db import DEFAULT_DB_ALIAS
|
|
|
|
from django.utils import timezone
|
2021-07-07 21:37:35 -04:00
|
|
|
from packaging import version
|
2021-06-11 10:44:17 -04:00
|
|
|
|
2022-03-25 09:00:02 +01:00
|
|
|
from extras.models import JobResult
|
2021-06-11 10:44:17 -04:00
|
|
|
from extras.models import ObjectChange
|
2021-11-08 15:07:58 -05:00
|
|
|
from netbox.config import Config
|
2021-06-11 10:44:17 -04:00
|
|
|
|
|
|
|
|
|
|
|
class Command(BaseCommand):
|
|
|
|
help = "Perform nightly housekeeping tasks. (This command can be run at any time.)"
|
|
|
|
|
|
|
|
def handle(self, *args, **options):
|
2021-11-08 15:07:58 -05:00
|
|
|
config = Config()
|
2021-06-11 10:44:17 -04:00
|
|
|
|
|
|
|
# Clear expired authentication sessions (essentially replicating the `clearsessions` command)
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write("[*] Clearing expired authentication sessions")
|
|
|
|
if options['verbosity'] >= 2:
|
|
|
|
self.stdout.write(f"\tConfigured session engine: {settings.SESSION_ENGINE}")
|
2021-06-11 10:44:17 -04:00
|
|
|
engine = import_module(settings.SESSION_ENGINE)
|
|
|
|
try:
|
|
|
|
engine.SessionStore.clear_expired()
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write("\tSessions cleared.", self.style.SUCCESS)
|
2021-06-11 10:44:17 -04:00
|
|
|
except NotImplementedError:
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write(
|
|
|
|
f"\tThe configured session engine ({settings.SESSION_ENGINE}) does not support "
|
|
|
|
f"clearing sessions; skipping."
|
|
|
|
)
|
2021-06-11 10:44:17 -04:00
|
|
|
|
|
|
|
# Delete expired ObjectRecords
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write("[*] Checking for expired changelog records")
|
2021-11-08 15:07:58 -05:00
|
|
|
if config.CHANGELOG_RETENTION:
|
|
|
|
cutoff = timezone.now() - timedelta(days=config.CHANGELOG_RETENTION)
|
2021-06-11 10:44:17 -04:00
|
|
|
if options['verbosity'] >= 2:
|
2021-11-08 15:07:58 -05:00
|
|
|
self.stdout.write(f"\tRetention period: {config.CHANGELOG_RETENTION} days")
|
2021-06-11 10:44:17 -04:00
|
|
|
self.stdout.write(f"\tCut-off time: {cutoff}")
|
|
|
|
expired_records = ObjectChange.objects.filter(time__lt=cutoff).count()
|
|
|
|
if expired_records:
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write(
|
|
|
|
f"\tDeleting {expired_records} expired records... ",
|
|
|
|
self.style.WARNING,
|
|
|
|
ending=""
|
|
|
|
)
|
|
|
|
self.stdout.flush()
|
2021-06-11 10:44:17 -04:00
|
|
|
ObjectChange.objects.filter(time__lt=cutoff)._raw_delete(using=DEFAULT_DB_ALIAS)
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write("Done.", self.style.SUCCESS)
|
|
|
|
elif options['verbosity']:
|
|
|
|
self.stdout.write("\tNo expired records found.", self.style.SUCCESS)
|
|
|
|
elif options['verbosity']:
|
2021-06-11 10:44:17 -04:00
|
|
|
self.stdout.write(
|
2021-11-08 15:07:58 -05:00
|
|
|
f"\tSkipping: No retention period specified (CHANGELOG_RETENTION = {config.CHANGELOG_RETENTION})"
|
2021-06-11 10:44:17 -04:00
|
|
|
)
|
|
|
|
|
2022-03-25 09:00:02 +01:00
|
|
|
# Delete expired JobResults
|
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write("[*] Checking for expired jobresult records")
|
|
|
|
if config.JOBRESULT_RETENTION:
|
|
|
|
cutoff = timezone.now() - timedelta(days=config.JOBRESULT_RETENTION)
|
|
|
|
if options['verbosity'] >= 2:
|
|
|
|
self.stdout.write(f"\tRetention period: {config.JOBRESULT_RETENTION} days")
|
|
|
|
self.stdout.write(f"\tCut-off time: {cutoff}")
|
|
|
|
expired_records = JobResult.objects.filter(created__lt=cutoff).count()
|
|
|
|
if expired_records:
|
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write(
|
|
|
|
f"\tDeleting {expired_records} expired records... ",
|
|
|
|
self.style.WARNING,
|
|
|
|
ending=""
|
|
|
|
)
|
|
|
|
self.stdout.flush()
|
2022-10-09 21:05:31 +02:00
|
|
|
JobResult.objects.filter(created__lt=cutoff).delete(using=DEFAULT_DB_ALIAS)
|
2022-03-25 09:00:02 +01:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write("Done.", self.style.SUCCESS)
|
|
|
|
elif options['verbosity']:
|
|
|
|
self.stdout.write("\tNo expired records found.", self.style.SUCCESS)
|
|
|
|
elif options['verbosity']:
|
|
|
|
self.stdout.write(
|
|
|
|
f"\tSkipping: No retention period specified (JOBRESULT_RETENTION = {config.JOBRESULT_RETENTION})"
|
|
|
|
)
|
|
|
|
|
2021-07-07 21:37:35 -04:00
|
|
|
# Check for new releases (if enabled)
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write("[*] Checking for latest release")
|
2021-07-07 21:37:35 -04:00
|
|
|
if settings.RELEASE_CHECK_URL:
|
|
|
|
headers = {
|
|
|
|
'Accept': 'application/vnd.github.v3+json',
|
|
|
|
}
|
|
|
|
|
|
|
|
try:
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity'] >= 2:
|
|
|
|
self.stdout.write(f"\tFetching {settings.RELEASE_CHECK_URL}")
|
2021-07-07 21:37:35 -04:00
|
|
|
response = requests.get(
|
|
|
|
url=settings.RELEASE_CHECK_URL,
|
|
|
|
headers=headers,
|
|
|
|
proxies=settings.HTTP_PROXIES
|
|
|
|
)
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
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)
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity'] >= 2:
|
|
|
|
self.stdout.write(f"\tFound {len(response.json())} releases; {len(releases)} usable")
|
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write(f"\tLatest release: {latest_release[0]}", self.style.SUCCESS)
|
2021-07-07 21:37:35 -04:00
|
|
|
|
|
|
|
# Cache the most recent release
|
|
|
|
cache.set('latest_release', latest_release, None)
|
|
|
|
|
|
|
|
except requests.exceptions.RequestException as exc:
|
2021-10-01 15:29:22 -04:00
|
|
|
self.stdout.write(f"\tRequest error: {exc}", self.style.ERROR)
|
2021-07-07 21:37:35 -04:00
|
|
|
else:
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write(f"\tSkipping: RELEASE_CHECK_URL not set")
|
2021-07-07 21:37:35 -04:00
|
|
|
|
2021-10-01 15:29:22 -04:00
|
|
|
if options['verbosity']:
|
|
|
|
self.stdout.write("Finished.", self.style.SUCCESS)
|