diff --git a/uncloud_django_based/uncloud/requirements.txt b/uncloud_django_based/uncloud/requirements.txt index c7ebc65..c77db20 100644 --- a/uncloud_django_based/uncloud/requirements.txt +++ b/uncloud_django_based/uncloud/requirements.txt @@ -11,3 +11,6 @@ parsedatetime pyparsing pydot django-extensions + +# PDF creating +django-hardcopy diff --git a/uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py b/uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py index c63e5a0..6d717b8 100644 --- a/uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py +++ b/uncloud_django_based/uncloud/uncloud_net/management/commands/vpn.py @@ -13,32 +13,52 @@ log = logging.getLogger(__name__) wireguard_template=""" - [Interface] ListenPort = 51820 PrivateKey = {privatekey} +""" -# Nico, 2019-01-23, Switzerland -#[Peer] -#PublicKey = kL1S/Ipq6NkFf1MAsNRou4b9VoUsnnb4ZxgiBrH0zA8= -#AllowedIPs = 2a0a:e5c1:101::/48 +peer_template=""" +# {username} +[Peer] +PublicKey = {public_key} +AllowedIPs = {vpnnetwork} """ class Command(BaseCommand): help = 'General uncloud commands' def add_arguments(self, parser): - parser.add_argument('--hostname', action='store_true', help='Name of this VPN Host', + parser.add_argument('--hostname', + action='store_true', + help='Name of this VPN Host', required=True) def handle(self, *args, **options): -# for net if options['bootstrap']: self.bootstrap() self.create_vpn_config(options['hostname']) def create_vpn_config(self, hostname): - for pool in VPNPool.objects.filter(vpn_hostname - default_cluster = VPNNetwork.objects.get_or_create(name="default") -# local_host = + configs = [] + + for pool in VPNPool.objects.filter(vpn_hostname=hostname): + pool_config = { + 'private_key': pool.wireguard_private_key, + 'subnetwork_size': pool.subnetwork_size, + 'config_file': '/etc/wireguard/{}.conf'.format(pool.network), + 'peers': [] + } + + for vpnnetwork in VPNNetworkReservation.objects.filter(vpnpool=pool): + pool_config['peers'].append({ + 'vpnnetwork': "{}/{}".format(vpnnetwork.address, + pool_config['subnetwork_size']), + 'public_key': vpnnetwork.wireguard_public_key, + } + ) + + configs.append(pool_config) + + print(configs) diff --git a/uncloud_django_based/uncloud/uncloud_net/models.py b/uncloud_django_based/uncloud/uncloud_net/models.py index a3939ee..2eaf92d 100644 --- a/uncloud_django_based/uncloud/uncloud_net/models.py +++ b/uncloud_django_based/uncloud/uncloud_net/models.py @@ -23,30 +23,96 @@ class VPNPool(UncloudModel): network_size = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(128)]) - subnetwork_size = models.IntegerField(validators=[MinValueValidator(0), - MaxValueValidator(128)]) + subnetwork_size = models.IntegerField(validators=[ + MinValueValidator(0), + MaxValueValidator(128) + ]) vpn_hostname = models.CharField(max_length=256) wireguard_private_key = models.CharField(max_length=48) + @property + def num_maximum_networks(self): + """ + sample: + network_size = 40 + subnetwork_size = 48 + maximum_networks = 2^(48-40) + + 2nd sample: + network_size = 8 + subnetwork_size = 24 + maximum_networks = 2^(24-8) + """ + + return 2**(subnetwork_size - network_size) + + @property + def used_networks(self): + return self.vpnnetworkreservation_set.objects.filter(vpnpool=self, status='used') + + @property + def num_used_networks(self): + return len(self.used_networks) + + @property + def num_free_networks(self): + return self.num_maximum_networks - self.num_used_networks + + @property + def next_free_network(self): + free_net = self.vpnnetworkreservation_set.objects.filter(vpnpool=self, + status='free') + + last_net = self.vpnnetworkreservation_set.objects.filter(vpnpool=self, + status='used') + + if num_free_networks == 0: + raise Exception("No free networks") + + if len(free_net) > 0: + return free_net[0].address + + if len(used_net) > 0: + """ + sample: + + pool = 2a0a:e5c1:200::/40 + last_used = 2a0a:e5c1:204::/48 + + next: + """ + + last_ip = last_net.address +# next_ip = + + + + class VPNNetworkReservation(UncloudModel): """ - This class tracks the used VPN networks. It will be deleted, when the product is cancelled. - """ + This class tracks the used VPN networks. It will be deleted, when the product is cancelled. + """ vpnpool = models.ForeignKey(VPNPool, - on_delete=models.CASCADE) - + on_delete=models.CASCADE) address = models.GenericIPAddressField(primary_key=True) + status = models.CharField(max_length=256, + choices = ( + ('used', 'used'), + ('free', 'free') + ) + ) + class VPNNetwork(Product): """ A selected network. Used for tracking reservations / used networks """ network = models.ForeignKey(VPNNetworkReservation, - on_delete=models.CASCADE, - editable=False) + on_delete=models.CASCADE, + editable=False) wireguard_public_key = models.CharField(max_length=48) diff --git a/uncloud_django_based/uncloud/uncloud_net/serializers.py b/uncloud_django_based/uncloud/uncloud_net/serializers.py index 2c54b4f..7c7b4a2 100644 --- a/uncloud_django_based/uncloud/uncloud_net/serializers.py +++ b/uncloud_django_based/uncloud/uncloud_net/serializers.py @@ -12,7 +12,6 @@ class VPNPoolSerializer(serializers.ModelSerializer): fields = '__all__' class VPNNetworkSerializer(serializers.ModelSerializer): - class Meta: model = VPNNetwork fields = '__all__' @@ -53,8 +52,24 @@ class VPNNetworkSerializer(serializers.ModelSerializer): msg = _("No pool available for networks with size = {}. Available are: {}".format(data['network_size'], sizes)) raise serializers.ValidationError(msg) - return data def create(self, validated_data): - from_pool = + """ + Creating a new vpnnetwork - there are a couple of race conditions, + especially when run in parallel. + """ + pools = VPNPool.objects.filter(subnetwork_size=data['network_size']) + + found_pool = False + for pool in pools: + if pool.num_free_networks > 0: + found_pool = True +# address = pool. +# reservation = VPNNetworkReservation(vpnpool=pool, + + + pool = VPNPool.objects.first(subnetwork_size=data['network_size']) + + + return VPNNetwork(**validated_data)