models.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. from __future__ import unicode_literals
  2. from django.contrib.contenttypes.fields import GenericRelation
  3. from django.core.exceptions import ValidationError
  4. from django.db import models
  5. from django.urls import reverse
  6. from django.utils.encoding import python_2_unicode_compatible
  7. from dcim.models import Device
  8. from extras.models import CustomFieldModel, CustomFieldValue
  9. from utilities.models import CreatedUpdatedModel
  10. from utilities.utils import csv_format
  11. from .constants import STATUS_ACTIVE, STATUS_CHOICES, VM_STATUS_CLASSES
  12. #
  13. # Cluster types
  14. #
  15. @python_2_unicode_compatible
  16. class ClusterType(models.Model):
  17. """
  18. A type of Cluster.
  19. """
  20. name = models.CharField(
  21. max_length=50,
  22. unique=True
  23. )
  24. slug = models.SlugField(
  25. unique=True
  26. )
  27. class Meta:
  28. ordering = ['name']
  29. def __str__(self):
  30. return self.name
  31. def get_absolute_url(self):
  32. return "{}?type={}".format(reverse('virtualization:cluster_list'), self.slug)
  33. #
  34. # Cluster groups
  35. #
  36. @python_2_unicode_compatible
  37. class ClusterGroup(models.Model):
  38. """
  39. An organizational group of Clusters.
  40. """
  41. name = models.CharField(
  42. max_length=50,
  43. unique=True
  44. )
  45. slug = models.SlugField(
  46. unique=True
  47. )
  48. class Meta:
  49. ordering = ['name']
  50. def __str__(self):
  51. return self.name
  52. def get_absolute_url(self):
  53. return "{}?group={}".format(reverse('virtualization:cluster_list'), self.slug)
  54. #
  55. # Clusters
  56. #
  57. @python_2_unicode_compatible
  58. class Cluster(CreatedUpdatedModel, CustomFieldModel):
  59. """
  60. A cluster of VirtualMachines. Each Cluster may optionally be associated with one or more Devices.
  61. """
  62. name = models.CharField(
  63. max_length=100,
  64. unique=True
  65. )
  66. type = models.ForeignKey(
  67. to=ClusterType,
  68. on_delete=models.PROTECT,
  69. related_name='clusters'
  70. )
  71. group = models.ForeignKey(
  72. to=ClusterGroup,
  73. on_delete=models.PROTECT,
  74. related_name='clusters',
  75. blank=True,
  76. null=True
  77. )
  78. site = models.ForeignKey(
  79. to='dcim.Site',
  80. on_delete=models.PROTECT,
  81. related_name='clusters',
  82. blank=True,
  83. null=True
  84. )
  85. comments = models.TextField(
  86. blank=True
  87. )
  88. custom_field_values = GenericRelation(
  89. to=CustomFieldValue,
  90. content_type_field='obj_type',
  91. object_id_field='obj_id'
  92. )
  93. csv_headers = [
  94. 'name', 'type', 'group', 'site', 'comments',
  95. ]
  96. class Meta:
  97. ordering = ['name']
  98. def __str__(self):
  99. return self.name
  100. def get_absolute_url(self):
  101. return reverse('virtualization:cluster', args=[self.pk])
  102. def clean(self):
  103. # If the Cluster is assigned to a Site, verify that all host Devices belong to that Site.
  104. if self.pk and self.site:
  105. nonsite_devices = Device.objects.filter(cluster=self).exclude(site=self.site).count()
  106. if nonsite_devices:
  107. raise ValidationError({
  108. 'site': "{} devices are assigned as hosts for this cluster but are not in site {}".format(
  109. nonsite_devices, self.site
  110. )
  111. })
  112. def to_csv(self):
  113. return csv_format([
  114. self.name,
  115. self.type.name,
  116. self.group.name if self.group else None,
  117. self.comments,
  118. ])
  119. #
  120. # Virtual machines
  121. #
  122. @python_2_unicode_compatible
  123. class VirtualMachine(CreatedUpdatedModel, CustomFieldModel):
  124. """
  125. A virtual machine which runs inside a Cluster.
  126. """
  127. cluster = models.ForeignKey(
  128. to=Cluster,
  129. on_delete=models.PROTECT,
  130. related_name='virtual_machines'
  131. )
  132. tenant = models.ForeignKey(
  133. to='tenancy.Tenant',
  134. on_delete=models.PROTECT,
  135. related_name='virtual_machines',
  136. blank=True,
  137. null=True
  138. )
  139. platform = models.ForeignKey(
  140. to='dcim.Platform',
  141. on_delete=models.SET_NULL,
  142. related_name='virtual_machines',
  143. blank=True,
  144. null=True
  145. )
  146. name = models.CharField(
  147. max_length=64,
  148. unique=True
  149. )
  150. status = models.PositiveSmallIntegerField(
  151. choices=STATUS_CHOICES,
  152. default=STATUS_ACTIVE,
  153. verbose_name='Status'
  154. )
  155. primary_ip4 = models.OneToOneField(
  156. to='ipam.IPAddress',
  157. on_delete=models.SET_NULL,
  158. related_name='+',
  159. blank=True,
  160. null=True,
  161. verbose_name='Primary IPv4'
  162. )
  163. primary_ip6 = models.OneToOneField(
  164. to='ipam.IPAddress',
  165. on_delete=models.SET_NULL,
  166. related_name='+',
  167. blank=True,
  168. null=True,
  169. verbose_name='Primary IPv6'
  170. )
  171. vcpus = models.PositiveSmallIntegerField(
  172. blank=True,
  173. null=True,
  174. verbose_name='vCPUs'
  175. )
  176. memory = models.PositiveIntegerField(
  177. blank=True,
  178. null=True,
  179. verbose_name='Memory (MB)'
  180. )
  181. disk = models.PositiveIntegerField(
  182. blank=True,
  183. null=True,
  184. verbose_name='Disk (GB)'
  185. )
  186. comments = models.TextField(
  187. blank=True
  188. )
  189. custom_field_values = GenericRelation(
  190. to=CustomFieldValue,
  191. content_type_field='obj_type',
  192. object_id_field='obj_id'
  193. )
  194. csv_headers = [
  195. 'name', 'status', 'cluster', 'tenant', 'platform', 'vcpus', 'memory', 'disk', 'comments',
  196. ]
  197. class Meta:
  198. ordering = ['name']
  199. def __str__(self):
  200. return self.name
  201. def get_absolute_url(self):
  202. return reverse('virtualization:virtualmachine', args=[self.pk])
  203. def to_csv(self):
  204. return csv_format([
  205. self.name,
  206. self.get_status_display(),
  207. self.cluster.name,
  208. self.tenant.name if self.tenant else None,
  209. self.platform.name if self.platform else None,
  210. self.vcpus,
  211. self.memory,
  212. self.disk,
  213. self.comments,
  214. ])
  215. def get_status_class(self):
  216. return VM_STATUS_CLASSES[self.status]