mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Reorganize virtualization models
This commit is contained in:
2
netbox/virtualization/models/__init__.py
Normal file
2
netbox/virtualization/models/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from .clusters import *
|
||||||
|
from .virtualmachines import *
|
135
netbox/virtualization/models/clusters.py
Normal file
135
netbox/virtualization/models/clusters.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
from django.contrib.contenttypes.fields import GenericRelation
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db import models
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from dcim.models import Device
|
||||||
|
from netbox.models import OrganizationalModel, PrimaryModel
|
||||||
|
from virtualization.choices import *
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'Cluster',
|
||||||
|
'ClusterGroup',
|
||||||
|
'ClusterType',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ClusterType(OrganizationalModel):
|
||||||
|
"""
|
||||||
|
A type of Cluster.
|
||||||
|
"""
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('virtualization:clustertype', args=[self.pk])
|
||||||
|
|
||||||
|
|
||||||
|
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])
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
|
})
|
@ -6,162 +6,23 @@ from django.db.models import Q
|
|||||||
from django.db.models.functions import Lower
|
from django.db.models.functions import Lower
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from dcim.models import BaseInterface, Device
|
from dcim.models import BaseInterface
|
||||||
from extras.models import ConfigContextModel
|
from extras.models import ConfigContextModel
|
||||||
from extras.querysets import ConfigContextModelQuerySet
|
from extras.querysets import ConfigContextModelQuerySet
|
||||||
from netbox.config import get_config
|
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.fields import NaturalOrderingField
|
||||||
from utilities.ordering import naturalize_interface
|
from utilities.ordering import naturalize_interface
|
||||||
from utilities.query_functions import CollateAsChar
|
from utilities.query_functions import CollateAsChar
|
||||||
from .choices import *
|
from virtualization.choices import *
|
||||||
|
from .clusters import Cluster
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'Cluster',
|
|
||||||
'ClusterGroup',
|
|
||||||
'ClusterType',
|
|
||||||
'VirtualMachine',
|
'VirtualMachine',
|
||||||
'VMInterface',
|
'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):
|
class VirtualMachine(PrimaryModel, ConfigContextModel):
|
||||||
"""
|
"""
|
||||||
A virtual machine which runs inside a Cluster.
|
A virtual machine which runs inside a Cluster.
|
||||||
@ -357,10 +218,6 @@ class VirtualMachine(PrimaryModel, ConfigContextModel):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# Interfaces
|
|
||||||
#
|
|
||||||
|
|
||||||
class VMInterface(NetBoxModel, BaseInterface):
|
class VMInterface(NetBoxModel, BaseInterface):
|
||||||
virtual_machine = models.ForeignKey(
|
virtual_machine = models.ForeignKey(
|
||||||
to='virtualization.VirtualMachine',
|
to='virtualization.VirtualMachine',
|
Reference in New Issue
Block a user