Add docstrings for VPN service
diff --git a/xos/observers/vpn/steps/sync_vpntenant.py b/xos/observers/vpn/steps/sync_vpntenant.py
index b579f12..d6bc664 100644
--- a/xos/observers/vpn/steps/sync_vpntenant.py
+++ b/xos/observers/vpn/steps/sync_vpntenant.py
@@ -8,6 +8,7 @@
sys.path.insert(0, parentdir)
class SyncVPNTenant(SyncInstanceUsingAnsible):
+ """Class for syncing a VPNTenant using Ansible."""
provides = [VPNTenant]
observes = VPNTenant
requested_interval = 0
@@ -36,7 +37,12 @@
"client_address": o.client_address}
def generate_client_conf(self, tenant):
- # tenant.nat_ip maybe None when the first line executes
+ """str: Generates the client configuration to use to connect to this VPN server.
+
+ Args:
+ tenant (VPNTenant): The tenant to generate the client configuration for.
+
+ """
conf = "remote " + str(tenant.nat_ip) + "\n"
conf += "dev tun\n"
conf += "ifconfig " + tenant.client_address + " " + tenant.server_address + "\n"
diff --git a/xos/services/vpn/admin.py b/xos/services/vpn/admin.py
index f7e78f8..a7096be 100644
--- a/xos/services/vpn/admin.py
+++ b/xos/services/vpn/admin.py
@@ -1,13 +1,16 @@
+from subprocess import PIPE, Popen
+
from core.admin import ReadOnlyAwareAdmin, SliceInline
from core.middleware import get_request
from core.models import User
from django import forms
from django.contrib import admin
-from subprocess import Popen, PIPE
-from services.vpn.models import VPNService, VPNTenant, VPN_KIND
+from services.vpn.models import VPN_KIND, VPNService, VPNTenant
+
class VPNServiceAdmin(ReadOnlyAwareAdmin):
+ """Defines the admin for the VPNService."""
model = VPNService
verbose_name = "VPN Service"
@@ -38,12 +41,27 @@
def queryset(self, request):
return VPNService.get_service_objects_by_user(request.user)
+
class VPNTenantForm(forms.ModelForm):
+ """The form used to create and edit a VPNTenant.
+
+ Attributes:
+ creator (forms.ModelChoiceField): The XOS user that created this tenant.
+ server_key (forms.CharField): The readonly static key used to the connect to this Tenant.
+ client_conf (forms.CharField): The readonly configuration used on the client to connect to this Tenant.
+ server_address (forms.GenericIPAddressField): The ip address on the VPN of this Tenant.
+ client_address (forms.GenericIPAddressField): The ip address on the VPN of the client.
+ is_persistent (forms.BooleanField): Determines if this Tenant keeps this connection alive through failures.
+ can_view_subnet (forms.BooleanField): Determins if this Tenant makes it's subnet available to the client.
+
+ """
creator = forms.ModelChoiceField(queryset=User.objects.all())
server_key = forms.CharField(required=False, widget=forms.Textarea)
client_conf = forms.CharField(required=False, widget=forms.Textarea)
- server_address = forms.GenericIPAddressField(protocol='IPv4', required=True)
- client_address = forms.GenericIPAddressField(protocol='IPv4', required=True)
+ server_address = forms.GenericIPAddressField(
+ protocol='IPv4', required=True)
+ client_address = forms.GenericIPAddressField(
+ protocol='IPv4', required=True)
is_persistent = forms.BooleanField(required=False)
can_view_subnet = forms.BooleanField(required=False)
@@ -61,10 +79,13 @@
self.fields['creator'].initial = self.instance.creator
self.fields['server_key'].initial = self.instance.server_key
self.fields['client_conf'].initial = self.instance.client_conf
- self.fields['server_address'].initial = self.instance.server_address
- self.fields['client_address'].initial = self.instance.client_address
+ self.fields[
+ 'server_address'].initial = self.instance.server_address
+ self.fields[
+ 'client_address'].initial = self.instance.client_address
self.fields['is_persistent'].initial = self.instance.is_persistent
- self.fields['can_view_subnet'].initial = self.instance.can_view_subnet
+ self.fields[
+ 'can_view_subnet'].initial = self.instance.can_view_subnet
if (not self.instance) or (not self.instance.pk):
self.fields['creator'].initial = get_request().user
@@ -74,7 +95,8 @@
self.fields['is_persistent'].initial = True
self.fields['can_view_subnet'].initial = False
if VPNService.get_service_objects().exists():
- self.fields["provider_service"].initial = VPNService.get_service_objects().all()[0]
+ self.fields["provider_service"].initial = VPNService.get_service_objects().all()[
+ 0]
def save(self, commit=True):
self.instance.creator = self.cleaned_data.get("creator")
@@ -82,17 +104,21 @@
self.instance.server_address = self.cleaned_data.get("server_address")
self.instance.client_address = self.cleaned_data.get("client_address")
self.instance.is_persistent = self.cleaned_data.get('is_persistent')
- self.instance.can_view_subnet = self.cleaned_data.get('can_view_subnet')
+ self.instance.can_view_subnet = self.cleaned_data.get(
+ 'can_view_subnet')
return super(VPNTenantForm, self).save(commit=commit)
def generate_VPN_key(self):
- proc = Popen("openvpn --genkey --secret /dev/stdout", shell=True, stdout=PIPE)
+ """Generates a VPN key using the openvpn command."""
+ proc = Popen("openvpn --genkey --secret /dev/stdout",
+ shell=True, stdout=PIPE)
(stdout, stderr) = proc.communicate()
return stdout
class Meta:
model = VPNTenant
+
class VPNTenantAdmin(ReadOnlyAwareAdmin):
verbose_name = "VPN Tenant Admin"
list_display = ('id', 'backend_status_icon', 'instance')
diff --git a/xos/services/vpn/models.py b/xos/services/vpn/models.py
index b3d2768..313fcc6 100644
--- a/xos/services/vpn/models.py
+++ b/xos/services/vpn/models.py
@@ -1,9 +1,12 @@
from core.models import Service, TenantWithContainer
from django.db import transaction
+from typing import Mapping, tuple
VPN_KIND = "vpn"
+
class VPNService(Service):
+ """Defines the Service for creating VPN servers."""
KIND = VPN_KIND
class Meta:
@@ -12,7 +15,9 @@
app_label = "vpn"
verbose_name = "VPN Service"
+
class VPNTenant(TenantWithContainer):
+ """Defines the Tenant for creating VPN servers."""
class Meta:
proxy = True
@@ -46,6 +51,7 @@
@property
def server_key(self):
+ """str: The server_key used to connect to the VPN server."""
return self.get_attribute(
"server_key",
self.default_attributes['server_key'])
@@ -56,6 +62,7 @@
@property
def addresses(self):
+ """Mapping[str, Tuple[str, str, str]]: The ip, mac address, and subnet of networks of this Tenant."""
if (not self.id) or (not self.instance):
return {}
@@ -71,19 +78,23 @@
# This getter is necessary because nat_ip is a sync_attribute
@property
def nat_ip(self):
+ """str: The IP of this Tenant on the NAT network."""
return self.addresses.get("nat", (None, None, None))[0]
# This getter is necessary because nat_mac is a sync_attribute
@property
def nat_mac(self):
+ """str: The MAC address of this Tenant on the NAT network."""
return self.addresses.get("nat", (None, None, None))[1]
@property
def subnet(self):
+ """str: The subnet of this Tenant on the NAT network."""
return self.addresses.get("nat", (None, None, None))[2]
@property
def server_address(self):
+ """str: The IP address of the server on the VPN."""
return self.get_attribute(
'server_address',
self.default_attributes['server_address'])
@@ -94,6 +105,7 @@
@property
def client_address(self):
+ """str: The IP address of the client on the VPN."""
return self.get_attribute(
'client_address',
self.default_attributes['client_address'])
@@ -104,6 +116,7 @@
@property
def client_conf(self):
+ """str: The client configuration for the client to connect to this server."""
return self.get_attribute(
"client_conf",
self.default_attributes['client_conf'])
@@ -114,6 +127,7 @@
@property
def is_persistent(self):
+ """bool: True if the VPN connection is persistence, false otherwise."""
return self.get_attribute(
"is_persistent",
self.default_attributes['is_persistent'])
@@ -124,6 +138,7 @@
@property
def can_view_subnet(self):
+ """bool: True if the client can see the subnet of the server, false otherwise."""
return self.get_attribute(
"can_view_subnet",
self.default_attributes['can_view_subnet'])
@@ -134,6 +149,7 @@
def model_policy_vpn_tenant(pk):
+ """Manages the contain for the VPN Tenant."""
# This section of code is atomic to prevent race conditions
with transaction.atomic():
# We find all of the tenants that are waiting to update