services.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. from django.contrib.contenttypes.fields import GenericForeignKey
  2. from django.contrib.postgres.fields import ArrayField
  3. from django.core.validators import MaxValueValidator, MinValueValidator
  4. from django.db import models
  5. from django.utils.translation import gettext_lazy as _
  6. from ipam.choices import *
  7. from ipam.constants import *
  8. from netbox.models import PrimaryModel
  9. from netbox.models.features import ContactsMixin
  10. from utilities.data import array_to_string
  11. __all__ = (
  12. 'Service',
  13. 'ServiceTemplate',
  14. )
  15. class ServiceBase(models.Model):
  16. protocol = models.CharField(
  17. verbose_name=_('protocol'),
  18. max_length=50,
  19. choices=ServiceProtocolChoices
  20. )
  21. ports = ArrayField(
  22. base_field=models.PositiveIntegerField(
  23. validators=[
  24. MinValueValidator(SERVICE_PORT_MIN),
  25. MaxValueValidator(SERVICE_PORT_MAX)
  26. ]
  27. ),
  28. verbose_name=_('port numbers')
  29. )
  30. class Meta:
  31. abstract = True
  32. def __str__(self):
  33. return f'{self.name} ({self.get_protocol_display()}/{self.port_list})'
  34. @property
  35. def port_list(self):
  36. return array_to_string(self.ports)
  37. class ServiceTemplate(ServiceBase, PrimaryModel):
  38. """
  39. A template for a Service to be applied to a device or virtual machine.
  40. """
  41. name = models.CharField(
  42. verbose_name=_('name'),
  43. max_length=100,
  44. unique=True
  45. )
  46. class Meta:
  47. ordering = ('name',)
  48. verbose_name = _('application service template')
  49. verbose_name_plural = _('application service templates')
  50. class Service(ContactsMixin, ServiceBase, PrimaryModel):
  51. """
  52. A Service represents a layer-four service (e.g. HTTP or SSH) running on a Device or VirtualMachine. A Service may
  53. optionally be tied to one or more specific IPAddresses belonging to its parent.
  54. """
  55. parent_object_type = models.ForeignKey(
  56. to='contenttypes.ContentType',
  57. on_delete=models.PROTECT,
  58. related_name='+',
  59. )
  60. parent_object_id = models.PositiveBigIntegerField()
  61. parent = GenericForeignKey(
  62. ct_field='parent_object_type',
  63. fk_field='parent_object_id'
  64. )
  65. name = models.CharField(
  66. max_length=100,
  67. verbose_name=_('name')
  68. )
  69. ipaddresses = models.ManyToManyField(
  70. to='ipam.IPAddress',
  71. related_name='services',
  72. blank=True,
  73. verbose_name=_('IP addresses'),
  74. help_text=_("The specific IP addresses (if any) to which this application service is bound")
  75. )
  76. clone_fields = (
  77. 'protocol', 'ports', 'description', 'parent_object_type', 'parent_object_id', 'ipaddresses',
  78. )
  79. class Meta:
  80. indexes = (
  81. models.Index(fields=('parent_object_type', 'parent_object_id')),
  82. )
  83. ordering = ('protocol', 'ports', 'pk') # (protocol, port) may be non-unique
  84. verbose_name = _('application service')
  85. verbose_name_plural = _('application services')