123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- from __future__ import unicode_literals
- from django.contrib.contenttypes.fields import GenericRelation
- from django.core.exceptions import ValidationError
- from django.db import models
- from django.urls import reverse
- from django.utils.encoding import python_2_unicode_compatible
- from dcim.models import Device
- from extras.models import CustomFieldModel, CustomFieldValue
- from utilities.models import CreatedUpdatedModel
- from utilities.utils import csv_format
- from .constants import STATUS_ACTIVE, STATUS_CHOICES, VM_STATUS_CLASSES
- #
- # Cluster types
- #
- @python_2_unicode_compatible
- class ClusterType(models.Model):
- """
- A type of Cluster.
- """
- name = models.CharField(
- max_length=50,
- unique=True
- )
- slug = models.SlugField(
- unique=True
- )
- class Meta:
- ordering = ['name']
- def __str__(self):
- return self.name
- def get_absolute_url(self):
- return "{}?type={}".format(reverse('virtualization:cluster_list'), self.slug)
- #
- # Cluster groups
- #
- @python_2_unicode_compatible
- class ClusterGroup(models.Model):
- """
- An organizational group of Clusters.
- """
- name = models.CharField(
- max_length=50,
- unique=True
- )
- slug = models.SlugField(
- unique=True
- )
- class Meta:
- ordering = ['name']
- def __str__(self):
- return self.name
- def get_absolute_url(self):
- return "{}?group={}".format(reverse('virtualization:cluster_list'), self.slug)
- #
- # Clusters
- #
- @python_2_unicode_compatible
- class Cluster(CreatedUpdatedModel, CustomFieldModel):
- """
- A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices.
- """
- name = models.CharField(
- max_length=100,
- unique=True
- )
- 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
- )
- site = models.ForeignKey(
- to='dcim.Site',
- on_delete=models.PROTECT,
- related_name='clusters',
- blank=True,
- null=True
- )
- comments = models.TextField(
- blank=True
- )
- custom_field_values = GenericRelation(
- to=CustomFieldValue,
- content_type_field='obj_type',
- object_id_field='obj_id'
- )
- csv_headers = [
- 'name', 'type', 'group', 'site', 'comments',
- ]
- class Meta:
- ordering = ['name']
- def __str__(self):
- return self.name
- def get_absolute_url(self):
- return reverse('virtualization:cluster', args=[self.pk])
- def clean(self):
- # 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
- )
- })
- def to_csv(self):
- return csv_format([
- self.name,
- self.type.name,
- self.group.name if self.group else None,
- self.comments,
- ])
- #
- # Virtual machines
- #
- @python_2_unicode_compatible
- class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
- """
- A virtual machine which runs inside a Cluster.
- """
- cluster = models.ForeignKey(
- to=Cluster,
- on_delete=models.PROTECT,
- related_name='virtual_machines'
- )
- tenant = models.ForeignKey(
- to='tenancy.Tenant',
- on_delete=models.PROTECT,
- related_name='virtual_machines',
- blank=True,
- null=True
- )
- platform = models.ForeignKey(
- to='dcim.Platform',
- on_delete=models.SET_NULL,
- related_name='virtual_machines',
- blank=True,
- null=True
- )
- name = models.CharField(
- max_length=64,
- unique=True
- )
- status = models.PositiveSmallIntegerField(
- choices=STATUS_CHOICES,
- default=STATUS_ACTIVE,
- verbose_name='Status'
- )
- primary_ip4 = models.OneToOneField(
- to='ipam.IPAddress',
- on_delete=models.SET_NULL,
- related_name='+',
- blank=True,
- null=True,
- verbose_name='Primary IPv4'
- )
- primary_ip6 = models.OneToOneField(
- to='ipam.IPAddress',
- on_delete=models.SET_NULL,
- related_name='+',
- blank=True,
- null=True,
- verbose_name='Primary IPv6'
- )
- vcpus = models.PositiveSmallIntegerField(
- blank=True,
- null=True,
- verbose_name='vCPUs'
- )
- memory = models.PositiveIntegerField(
- blank=True,
- null=True,
- verbose_name='Memory (MB)'
- )
- disk = models.PositiveIntegerField(
- blank=True,
- null=True,
- verbose_name='Disk (GB)'
- )
- comments = models.TextField(
- blank=True
- )
- custom_field_values = GenericRelation(
- to=CustomFieldValue,
- content_type_field='obj_type',
- object_id_field='obj_id'
- )
- csv_headers = [
- 'name', 'status', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
- ]
- class Meta:
- ordering = ['name']
- def __str__(self):
- return self.name
- def get_absolute_url(self):
- return reverse('virtualization:virtualmachine', args=[self.pk])
- def to_csv(self):
- return csv_format([
- self.name,
- self.get_status_display(),
- self.cluster.name,
- self.tenant.name if self.tenant else None,
- self.platform.name if self.platform else None,
- self.vcpus,
- self.memory,
- self.disk,
- self.comments,
- ])
- def get_status_class(self):
- return VM_STATUS_CLASSES[self.status]
|