|
|
@@ -6,162 +6,23 @@ from django.db.models import Q
|
|
|
from django.db.models.functions import Lower
|
|
|
from django.urls import reverse
|
|
|
|
|
|
-from dcim.models import BaseInterface, Device
|
|
|
+from dcim.models import BaseInterface
|
|
|
from extras.models import ConfigContextModel
|
|
|
from extras.querysets import ConfigContextModelQuerySet
|
|
|
from netbox.config import get_config
|
|
|
-from netbox.models import NetBoxModel, OrganizationalModel, PrimaryModel
|
|
|
+from netbox.models import NetBoxModel, PrimaryModel
|
|
|
from utilities.fields import NaturalOrderingField
|
|
|
from utilities.ordering import naturalize_interface
|
|
|
from utilities.query_functions import CollateAsChar
|
|
|
-from .choices import *
|
|
|
+from virtualization.choices import *
|
|
|
+from .clusters import Cluster
|
|
|
|
|
|
__all__ = (
|
|
|
- 'Cluster',
|
|
|
- 'ClusterGroup',
|
|
|
- 'ClusterType',
|
|
|
'VirtualMachine',
|
|
|
'VMInterface',
|
|
|
)
|
|
|
|
|
|
|
|
|
-#
|
|
|
-# Cluster types
|
|
|
-#
|
|
|
-
|
|
|
-class ClusterType(OrganizationalModel):
|
|
|
- """
|
|
|
- A type of Cluster.
|
|
|
- """
|
|
|
- def get_absolute_url(self):
|
|
|
- return reverse('virtualization:clustertype', args=[self.pk])
|
|
|
-
|
|
|
-
|
|
|
-#
|
|
|
-# Cluster groups
|
|
|
-#
|
|
|
-
|
|
|
-class ClusterGroup(OrganizationalModel):
|
|
|
- """
|
|
|
- An organizational group of Clusters.
|
|
|
- """
|
|
|
- # Generic relations
|
|
|
- vlan_groups = GenericRelation(
|
|
|
- to='ipam.VLANGroup',
|
|
|
- content_type_field='scope_type',
|
|
|
- object_id_field='scope_id',
|
|
|
- related_query_name='cluster_group'
|
|
|
- )
|
|
|
- contacts = GenericRelation(
|
|
|
- to='tenancy.ContactAssignment'
|
|
|
- )
|
|
|
-
|
|
|
- def get_absolute_url(self):
|
|
|
- return reverse('virtualization:clustergroup', args=[self.pk])
|
|
|
-
|
|
|
-
|
|
|
-#
|
|
|
-# Clusters
|
|
|
-#
|
|
|
-
|
|
|
-class Cluster(PrimaryModel):
|
|
|
- """
|
|
|
- A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices.
|
|
|
- """
|
|
|
- name = models.CharField(
|
|
|
- max_length=100
|
|
|
- )
|
|
|
- type = models.ForeignKey(
|
|
|
- to=ClusterType,
|
|
|
- on_delete=models.PROTECT,
|
|
|
- related_name='clusters'
|
|
|
- )
|
|
|
- group = models.ForeignKey(
|
|
|
- to=ClusterGroup,
|
|
|
- on_delete=models.PROTECT,
|
|
|
- related_name='clusters',
|
|
|
- blank=True,
|
|
|
- null=True
|
|
|
- )
|
|
|
- status = models.CharField(
|
|
|
- max_length=50,
|
|
|
- choices=ClusterStatusChoices,
|
|
|
- default=ClusterStatusChoices.STATUS_ACTIVE
|
|
|
- )
|
|
|
- tenant = models.ForeignKey(
|
|
|
- to='tenancy.Tenant',
|
|
|
- on_delete=models.PROTECT,
|
|
|
- related_name='clusters',
|
|
|
- blank=True,
|
|
|
- null=True
|
|
|
- )
|
|
|
- site = models.ForeignKey(
|
|
|
- to='dcim.Site',
|
|
|
- on_delete=models.PROTECT,
|
|
|
- related_name='clusters',
|
|
|
- blank=True,
|
|
|
- null=True
|
|
|
- )
|
|
|
-
|
|
|
- # Generic relations
|
|
|
- vlan_groups = GenericRelation(
|
|
|
- to='ipam.VLANGroup',
|
|
|
- content_type_field='scope_type',
|
|
|
- object_id_field='scope_id',
|
|
|
- related_query_name='cluster'
|
|
|
- )
|
|
|
- contacts = GenericRelation(
|
|
|
- to='tenancy.ContactAssignment'
|
|
|
- )
|
|
|
-
|
|
|
- clone_fields = (
|
|
|
- 'type', 'group', 'status', 'tenant', 'site',
|
|
|
- )
|
|
|
-
|
|
|
- class Meta:
|
|
|
- ordering = ['name']
|
|
|
- constraints = (
|
|
|
- models.UniqueConstraint(
|
|
|
- fields=('group', 'name'),
|
|
|
- name='%(app_label)s_%(class)s_unique_group_name'
|
|
|
- ),
|
|
|
- models.UniqueConstraint(
|
|
|
- fields=('site', 'name'),
|
|
|
- name='%(app_label)s_%(class)s_unique_site_name'
|
|
|
- ),
|
|
|
- )
|
|
|
-
|
|
|
- def __str__(self):
|
|
|
- return self.name
|
|
|
-
|
|
|
- @classmethod
|
|
|
- def get_prerequisite_models(cls):
|
|
|
- return [ClusterType, ]
|
|
|
-
|
|
|
- def get_absolute_url(self):
|
|
|
- return reverse('virtualization:cluster', args=[self.pk])
|
|
|
-
|
|
|
- def get_status_color(self):
|
|
|
- return ClusterStatusChoices.colors.get(self.status)
|
|
|
-
|
|
|
- def clean(self):
|
|
|
- super().clean()
|
|
|
-
|
|
|
- # If the Cluster is assigned to a Site, verify that all host Devices belong to that Site.
|
|
|
- if self.pk and self.site:
|
|
|
- nonsite_devices = Device.objects.filter(cluster=self).exclude(site=self.site).count()
|
|
|
- if nonsite_devices:
|
|
|
- raise ValidationError({
|
|
|
- 'site': "{} devices are assigned as hosts for this cluster but are not in site {}".format(
|
|
|
- nonsite_devices, self.site
|
|
|
- )
|
|
|
- })
|
|
|
-
|
|
|
-
|
|
|
-#
|
|
|
-# Virtual machines
|
|
|
-#
|
|
|
-
|
|
|
class VirtualMachine(PrimaryModel, ConfigContextModel):
|
|
|
"""
|
|
|
A virtual machine which runs inside a Cluster.
|
|
|
@@ -357,10 +218,6 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
|
|
|
return None
|
|
|
|
|
|
|
|
|
-#
|
|
|
-# Interfaces
|
|
|
-#
|
|
|
-
|
|
|
class VMInterface(NetBoxModel, BaseInterface):
|
|
|
virtual_machine = models.ForeignKey(
|
|
|
to='virtualization.VirtualMachine',
|