from core.choices import JobStatusChoices from core.exceptions import JobFailed from django.utils import timezone from netbox.jobs import JobRunner, system_job from .choices import EndpointProviderChoices, SyncStatusChoices from .models import VCenterEndpoint from .sync import VMwareImporter def clear_endpoint_sync_schedule(endpoint): return SyncVCenterEndpointJob.get_jobs(endpoint).filter( status__in=JobStatusChoices.ENQUEUED_STATE_CHOICES, interval__isnull=False, ).delete() def schedule_endpoint_sync(endpoint): if endpoint.provider != EndpointProviderChoices.PROVIDER_VMWARE: clear_endpoint_sync_schedule(endpoint) return None if not endpoint.enabled or not endpoint.sync_interval_minutes: clear_endpoint_sync_schedule(endpoint) return None if not endpoint.next_sync_at: endpoint.next_sync_at = timezone.now() VCenterEndpoint.objects.filter(pk=endpoint.pk).update(next_sync_at=endpoint.next_sync_at) return SyncVCenterEndpointJob.enqueue_once( instance=endpoint, schedule_at=endpoint.next_sync_at, interval=endpoint.sync_interval_minutes, ) class SyncVCenterEndpointJob(JobRunner): class Meta: name = "VM synchronization" def run(self, *args, **kwargs): endpoint = self.job.object if endpoint is None: endpoint_pk = kwargs.get("endpoint_pk") endpoint = VCenterEndpoint.objects.get(pk=endpoint_pk) if not endpoint.enabled: endpoint.mark_failure("Endpoint is disabled.") raise JobFailed("Endpoint is disabled.") if endpoint.provider != EndpointProviderChoices.PROVIDER_VMWARE: message = "Legacy Proxmox endpoints are no longer supported. Delete this endpoint." clear_endpoint_sync_schedule(endpoint) endpoint.mark_failure(message) raise JobFailed(message) self.logger.info("Starting VM sync for %s (%s)", endpoint.name, endpoint.host) endpoint.mark_running() try: result = VMwareImporter(endpoint, self.logger).sync() except Exception as exc: endpoint.mark_failure(exc) raise if result.errors: endpoint.last_sync_at = timezone.now() endpoint.last_status = SyncStatusChoices.STATUS_FAILED endpoint.last_vm_count = result.seen endpoint.last_created_count = result.created endpoint.last_updated_count = result.updated endpoint.last_error_count = result.errors endpoint.last_status = SyncStatusChoices.STATUS_FAILED endpoint.last_message = f"Finished with {result.errors} VM error(s). Check the job log." endpoint.set_next_sync(endpoint.last_sync_at) endpoint.save( update_fields=( "last_sync_at", "last_status", "last_vm_count", "last_created_count", "last_updated_count", "last_error_count", "last_message", "next_sync_at", "last_updated", ) ) raise JobFailed(endpoint.last_message) endpoint.mark_success(result) self.logger.info( ( "VM sync finished for %s: %s VM(s), %s created, %s updated, %s skipped; " "virtual disks: %s created, %s updated, %s deleted; " "MAC addresses: %s created, %s updated, %s deleted." ), endpoint.name, result.synced, result.created, result.updated, result.skipped, result.virtual_disks_created, result.virtual_disks_updated, result.virtual_disks_deleted, result.mac_addresses_created, result.mac_addresses_updated, result.mac_addresses_deleted, ) @system_job(interval=5) class ScheduleDueVCenterSyncsJob(JobRunner): class Meta: name = "Schedule due VM synchronizations" def run(self, *args, **kwargs): now = timezone.now() legacy_endpoints = VCenterEndpoint.objects.exclude(provider=EndpointProviderChoices.PROVIDER_VMWARE) cleared = 0 for endpoint in legacy_endpoints: deleted_count, _ = clear_endpoint_sync_schedule(endpoint) cleared += deleted_count enabled_endpoints = VCenterEndpoint.objects.filter( enabled=True, provider=EndpointProviderChoices.PROVIDER_VMWARE, sync_interval_minutes__isnull=False, ) scheduled = 0 for endpoint in enabled_endpoints: if endpoint.next_sync_at is None: endpoint.next_sync_at = now endpoint.save(update_fields=("next_sync_at", "last_updated")) if schedule_endpoint_sync(endpoint): scheduled += 1 self.logger.info("Ensured %s VMware endpoint sync schedule(s); cleared %s legacy schedule(s).", scheduled, cleared)