content: from django.core.exceptions import ValidationError
  from django.db import models
  from django.utils.translation import gettext_lazy as _
  
  from netbox.models import PrimaryModel
  from vpn.choices import *
  
  __all__ = (
      'IKEPolicy',
      'IKEProposal',
      'IPSecPolicy',
      'IPSecProfile',
      'IPSecProposal',
  )
  
  
  #
  # IKE
  #
  
  class IKEProposal(PrimaryModel):
      name = models.CharField(
          verbose_name=_('name'),
          max_length=100,
          unique=True,
          db_collation="natural_sort"
      )
      authentication_method = models.CharField(
          verbose_name=('authentication method'),
          choices=AuthenticationMethodChoices
      )
      encryption_algorithm = models.CharField(
          verbose_name=_('encryption algorithm'),
          choices=EncryptionAlgorithmChoices
      )
      authentication_algorithm = models.CharField(
          verbose_name=_('authentication algorithm'),
          choices=AuthenticationAlgorithmChoices,
          blank=True,
          null=True
      )
      group = models.PositiveSmallIntegerField(
          verbose_name=_('group'),
          choices=DHGroupChoices,
          help_text=_('Diffie-Hellman group ID')
      )
      sa_lifetime = models.PositiveIntegerField(
          verbose_name=_('SA lifetime'),
          blank=True,
          null=True,
          help_text=_('Security association lifetime (in seconds)')
      )
  
      clone_fields = (
          'authentication_method', 'encryption_algorithm', 'authentication_algorithm', 'group', 'sa_lifetime',
      )
  
      class Meta:
          ordering = ('name',)
          verbose_name = _('IKE proposal')
          verbose_name_plural = _('IKE proposals')
  
      def __str__(self):
          return self.name
  
  
  class IKEPolicy(PrimaryModel):
      name = models.CharField(
          verbose_name=_('name'),
          max_length=100,
          unique=True,
          db_collation="natural_sort"
      )
      version = models.PositiveSmallIntegerField(
          verbose_name=_('version'),
          choices=IKEVersionChoices,
          default=IKEVersionChoices.VERSION_2
      )
      mode = models.CharField(
          verbose_name=_('mode'),
          choices=IKEModeChoices,
          blank=True,
          null=True
      )
      proposals = models.ManyToManyField(
          to='vpn.IKEProposal',
          related_name='ike_policies',
          verbose_name=_('proposals')
      )
      preshared_key = models.TextField(
          verbose_name=_('pre-shared key'),
          blank=True
      )
  
      clone_fields = (
          'version', 'mode', 'proposals',
      )
      prerequisite_models = (
          'vpn.IKEProposal',
      )
  
      class Meta:
          ordering = ('name',)
          verbose_name = _('IKE policy')
          verbose_name_plural = _('IKE policies')
  
      def __str__(self):
          return self.name
  
      def clean(self):
          super().clean()
  
          # Mode is required
          if self.version == IKEVersionChoices.VERSION_1 and not self.mode:
              raise ValidationError(_("Mode is required for selected IKE version"))
  
          # Mode cannot be used
          if self.version == IKEVersionChoices.VERSION_2 and self.mode:
              raise ValidationError(_("Mode cannot be used for selected IKE version"))
  
  
  #
  # IPSec
  #
  
  class IPSecProposal(PrimaryModel):
      name = models.CharField(
          verbose_name=_('name'),
          max_length=100,
          unique=True,
          db_collation="natural_sort"
      )
      encryption_algorithm = models.CharField(
          verbose_name=_('encryption'),
          choices=EncryptionAlgorithmChoices,
          blank=True,
          null=True
      )
      authentication_algorithm = models.CharField(
          verbose_name=_('authentication'),
          choices=AuthenticationAlgorithmChoices,
          blank=True,
          null=True
      )
      sa_lifetime_seconds = models.PositiveIntegerField(
          verbose_name=_('SA lifetime (seconds)'),
          blank=True,
          null=True,
          help_text=_('Security association lifetime (seconds)')
      )
      sa_lifetime_data = models.PositiveIntegerField(
          verbose_name=_('SA lifetime (KB)'),
          blank=True,
          null=True,
          help_text=_('Security association lifetime (in kilobytes)')
      )
  
      clone_fields = (
          'encryption_algorithm', 'authentication_algorithm', 'sa_lifetime_seconds', 'sa_lifetime_data',
      )
  
      class Meta:
          ordering = ('name',)
          verbose_name = _('IPSec proposal')
          verbose_name_plural = _('IPSec proposals')
  
      def __str__(self):
          return self.name
  
      def clean(self):
          super().clean()
  
          # Encryption and/or authentication algorithm must be defined
          if not self.encryption_algorithm and not self.authentication_algorithm:
              raise ValidationError(_("Encryption and/or authentication algorithm must be defined"))
  
  
  class IPSecPolicy(PrimaryModel):
      name = models.CharField(
          verbose_name=_('name'),
          max_length=100,
          unique=True,
          db_collation="natural_sort"
      )
      proposals = models.ManyToManyField(
          to='vpn.IPSecProposal',
          related_name='ipsec_policies',
          verbose_name=_('proposals')
      )
      pfs_group = models.PositiveSmallIntegerField(
          verbose_name=_('PFS group'),
          choices=DHGroupChoices,
          blank=True,
          null=True,
          help_text=_('Diffie-Hellman group for Perfect Forward Secrecy')
      )
  
      clone_fields = (
          'proposals', 'pfs_group',
      )
      prerequisite_models = (
          'vpn.IPSecProposal',
      )
  
      class Meta:
          ordering = ('name',)
          verbose_name = _('IPSec policy')
          verbose_name_plural = _('IPSec policies')
  
      def __str__(self):
          return self.name
  
  
  class IPSecProfile(PrimaryModel):
      name = models.CharField(
          verbose_name=_('name'),
          max_length=100,
          unique=True,
          db_collation="natural_sort"
      )
      mode = models.CharField(
          verbose_name=_('mode'),
          choices=IPSecModeChoices
      )
      ike_policy = models.ForeignKey(
          to='vpn.IKEPolicy',
          on_delete=models.PROTECT,
          related_name='ipsec_profiles'
      )
      ipsec_policy = models.ForeignKey(
          to='vpn.IPSecPolicy',
          on_delete=models.PROTECT,
          related_name='ipsec_profiles'
      )
  
      clone_fields = (
          'mode', 'ike_policy', 'ipsec_policy',
      )
      prerequisite_models = (
          'vpn.IKEPolicy',
          'vpn.IPSecPolicy',
      )
  
      class Meta:
          ordering = ('name',)
          verbose_name = _('IPSec profile')
          verbose_name_plural = _('IPSec profiles')
  
      def __str__(self):
          return self.name
  