blob: 28e778ddceadcf875d98b4d2e3acf2786698313d [file] [log] [blame]
Jeremy Mowery0ee0bfc2016-04-15 16:13:45 -07001from django import forms
2from django.contrib import admin
3
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -07004from core.admin import ReadOnlyAwareAdmin, SliceInline, TenantPrivilegeInline
Jeremy Mowery3c5277f2016-02-29 21:09:04 -07005from core.middleware import get_request
Jeremy Moweryfba5a842016-04-13 15:10:23 -07006from core.models import User
Jeremy Mowery81e84cc2016-04-19 17:01:48 -07007from services.openvpn.models import OPENVPN_KIND, OpenVPNService, OpenVPNTenant
Jeremy Moweryb3c735b2016-04-02 10:16:21 -07008from xos.exceptions import XOSValidationError
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -07009
10
Jeremy Mowery81e84cc2016-04-19 17:01:48 -070011class OpenVPNServiceForm(forms.ModelForm):
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -070012
Jeremy Mowery9ddca292016-03-27 20:32:04 -070013 exposed_ports = forms.CharField(required=True)
14
Jeremy Mowery10be54f2016-03-27 21:35:58 -070015 def __init__(self, *args, **kwargs):
Jeremy Mowery81e84cc2016-04-19 17:01:48 -070016 super(OpenVPNServiceForm, self).__init__(*args, **kwargs)
Jeremy Mowery10be54f2016-03-27 21:35:58 -070017
18 if self.instance:
Jeremy Moweryee71fce2016-04-01 21:54:29 -070019 self.fields['exposed_ports'].initial = (
20 self.instance.exposed_ports_str)
Jeremy Mowery10be54f2016-03-27 21:35:58 -070021
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -070022 def save(self, commit=True):
Jeremy Mowery9ddca292016-03-27 20:32:04 -070023 self.instance.exposed_ports = self.cleaned_data['exposed_ports']
Jeremy Mowery81e84cc2016-04-19 17:01:48 -070024 return super(OpenVPNServiceForm, self).save(commit=commit)
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -070025
Jeremy Mowery9ddca292016-03-27 20:32:04 -070026 def clean_exposed_ports(self):
27 exposed_ports = self.cleaned_data['exposed_ports']
Jeremy Moweryee71fce2016-04-01 21:54:29 -070028 self.instance.exposed_ports_str = exposed_ports
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -070029 port_mapping = {"udp": [], "tcp": []}
30 parts = exposed_ports.split(",")
31 for part in parts:
32 part = part.strip()
33 if "/" in part:
34 (protocol, ports) = part.split("/", 1)
35 elif " " in part:
36 (protocol, ports) = part.split(None, 1)
37 else:
Jeremy Moweryee71fce2016-04-01 21:54:29 -070038 raise XOSValidationError(
39 'malformed port specifier %s, format example: ' +
40 '"tcp 123, tcp 201:206, udp 333"' % part)
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -070041
42 protocol = protocol.strip()
43 ports = ports.strip()
44
45 if not (protocol in ["udp", "tcp"]):
46 raise XOSValidationError('unknown protocol %s' % protocol)
47
48 if "-" in ports:
Jeremy Moweryee71fce2016-04-01 21:54:29 -070049 port_mapping[protocol].extend(
50 self.parse_port_range(ports, "-"))
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -070051 elif ":" in ports:
Jeremy Moweryee71fce2016-04-01 21:54:29 -070052 port_mapping[protocol].extend(
53 self.parse_port_range(ports, ":"))
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -070054 else:
55 port_mapping[protocol].append(int(ports))
56
57 return port_mapping
58
59 def parse_port_range(self, port_str, split_str):
60 (first, last) = port_str.split(split_str)
61 first = int(first.strip())
62 last = int(last.strip())
63 return list(range(first, last))
64
65 class Meta:
Jeremy Mowery81e84cc2016-04-19 17:01:48 -070066 model = OpenVPNService
Jeremy Mowery1f3f9f62016-03-21 17:56:25 -070067
Jeremy Mowery3c5277f2016-02-29 21:09:04 -070068
Jeremy Mowery81e84cc2016-04-19 17:01:48 -070069class OpenVPNServiceAdmin(ReadOnlyAwareAdmin):
70 """Defines the admin for the OpenVPNService."""
71 model = OpenVPNService
72 form = OpenVPNServiceForm
73 verbose_name = "OpenVPN Service"
Jeremy Mowery3c5277f2016-02-29 21:09:04 -070074
75 list_display = ("backend_status_icon", "name", "enabled")
76
77 list_display_links = ('backend_status_icon', 'name', )
78
79 fieldsets = [(None, {'fields': ['backend_status_text', 'name', 'enabled',
Jeremy Moweryee71fce2016-04-01 21:54:29 -070080 'versionNumber', 'description', "view_url",
81 'exposed_ports'],
Jeremy Mowery3c5277f2016-02-29 21:09:04 -070082 'classes':['suit-tab suit-tab-general']})]
83
84 readonly_fields = ('backend_status_text', )
85
86 inlines = [SliceInline]
87
88 extracontext_registered_admins = True
89
90 user_readonly_fields = ["name", "enabled", "versionNumber", "description"]
91
92 suit_form_tabs = (('general', 'VPN Service Details'),
Jeremy Mowery3c5277f2016-02-29 21:09:04 -070093 ('slices', 'Slices'),)
94
Jeremy Mowery3c5277f2016-02-29 21:09:04 -070095 def queryset(self, request):
Jeremy Mowery81e84cc2016-04-19 17:01:48 -070096 return OpenVPNService.get_service_objects_by_user(request.user)
Jeremy Mowery3c5277f2016-02-29 21:09:04 -070097
98
Jeremy Mowery81e84cc2016-04-19 17:01:48 -070099class OpenVPNTenantForm(forms.ModelForm):
100 """The form used to create and edit a OpenVPNTenant.
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700101
102 Attributes:
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700103 creator (forms.ModelChoiceField): The XOS user that created this
104 tenant.
Jeremy Mowery98e97d72016-04-15 00:05:27 -0700105 server_network (forms.GenericIPAddressField): The IP address of the VPN network.
106 vpn_subnet (forms.GenericIPAddressField): The subnet used by the VPN network.
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700107 is_persistent (forms.BooleanField): Determines if this Tenant keeps
108 this connection alive through failures.
Jeremy Mowery98e97d72016-04-15 00:05:27 -0700109 clients_can_see_each_other (forms.BooleanField): Determines if the clients on the VPN can
110 communicate with each other.
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700111 failover_servers (forms.ModelMultipleChoiceField): The other OpenVPNTenants to use as failover
Jeremy Mowery98e97d72016-04-15 00:05:27 -0700112 servers.
113 protocol (forms.ChoiceField): The protocol to use.
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700114 use_ca_from (forms.ModelChoiceField): Another OpenVPNTenant to use the CA of, this is a very
Jeremy Mowery98e97d72016-04-15 00:05:27 -0700115 hacky way to let VPNs have the same clients.
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700116 """
117 creator = forms.ModelChoiceField(queryset=User.objects.all())
118 server_network = forms.GenericIPAddressField(
119 protocol="IPv4", required=True)
120 vpn_subnet = forms.GenericIPAddressField(protocol="IPv4", required=True)
121 is_persistent = forms.BooleanField(required=False)
122 clients_can_see_each_other = forms.BooleanField(required=False)
Jeremy Mowery86aafe32016-04-10 21:04:08 -0700123 failover_servers = forms.ModelMultipleChoiceField(
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700124 required=False, queryset=OpenVPNTenant.get_tenant_objects())
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700125 protocol = forms.ChoiceField(required=True, choices=[
Jeremy Mowery5d06a232016-04-04 22:30:44 -0700126 ("tcp", "tcp"), ("udp", "udp")])
Jeremy Mowery36fb9e52016-04-05 23:32:10 -0700127 use_ca_from = forms.ModelChoiceField(
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700128 queryset=OpenVPNTenant.get_tenant_objects(), required=False)
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700129
130 def __init__(self, *args, **kwargs):
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700131 super(OpenVPNTenantForm, self).__init__(*args, **kwargs)
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700132 self.fields['kind'].widget.attrs['readonly'] = True
Jeremy Mowery18d054e2016-04-13 22:18:50 -0700133 self.fields['failover_servers'].widget.attrs['rows'] = 300
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700134 self.fields[
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700135 'provider_service'].queryset = (
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700136 OpenVPNService.get_service_objects().all())
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700137
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700138 self.fields['kind'].initial = OPENVPN_KIND
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700139
140 if self.instance:
141 self.fields['creator'].initial = self.instance.creator
142 self.fields['vpn_subnet'].initial = self.instance.vpn_subnet
143 self.fields[
144 'server_network'].initial = self.instance.server_network
145 self.fields[
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700146 'clients_can_see_each_other'].initial = (
147 self.instance.clients_can_see_each_other)
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700148 self.fields['is_persistent'].initial = self.instance.is_persistent
Jeremy Mowery26eb2fe2016-03-29 23:52:46 -0700149 self.initial['protocol'] = self.instance.protocol
Jeremy Mowery5d06a232016-04-04 22:30:44 -0700150 self.fields['failover_servers'].queryset = (
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700151 OpenVPNTenant.get_tenant_objects().exclude(pk=self.instance.pk))
152 self.initial['failover_servers'] = OpenVPNTenant.get_tenant_objects().filter(
Jeremy Mowery18d054e2016-04-13 22:18:50 -0700153 pk__in=self.instance.failover_server_ids)
Jeremy Mowery36fb9e52016-04-05 23:32:10 -0700154 self.fields['use_ca_from'].queryset = (
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700155 OpenVPNTenant.get_tenant_objects().exclude(pk=self.instance.pk))
Jeremy Mowery0fce7232016-04-12 22:03:55 -0700156 if (self.instance.use_ca_from_id):
Jeremy Mowery18d054e2016-04-13 22:18:50 -0700157 self.initial['use_ca_from'] = (
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700158 OpenVPNTenant.get_tenant_objects().filter(pk=self.instance.use_ca_from_id)[0])
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700159
160 if (not self.instance) or (not self.instance.pk):
161 self.fields['creator'].initial = get_request().user
162 self.fields['vpn_subnet'].initial = "255.255.255.0"
163 self.fields['server_network'].initial = "10.66.77.0"
164 self.fields['clients_can_see_each_other'].initial = True
165 self.fields['is_persistent'].initial = True
Jeremy Mowery5d06a232016-04-04 22:30:44 -0700166 self.fields['failover_servers'].queryset = (
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700167 OpenVPNTenant.get_tenant_objects())
168 if OpenVPNService.get_service_objects().exists():
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700169 self.fields["provider_service"].initial = (
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700170 OpenVPNService.get_service_objects().all()[0])
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700171
172 def save(self, commit=True):
173 self.instance.creator = self.cleaned_data.get("creator")
174 self.instance.is_persistent = self.cleaned_data.get('is_persistent')
175 self.instance.vpn_subnet = self.cleaned_data.get("vpn_subnet")
176 self.instance.server_network = self.cleaned_data.get('server_network')
177 self.instance.clients_can_see_each_other = self.cleaned_data.get(
178 'clients_can_see_each_other')
Jeremy Mowery5d06a232016-04-04 22:30:44 -0700179
Jeremy Mowery18d054e2016-04-13 22:18:50 -0700180 self.instance.failover_server_ids = [
181 tenant.id for tenant in self.cleaned_data.get('failover_servers')]
Jeremy Mowery324caf52016-03-16 11:22:02 -0700182
Jeremy Mowery9c6b9f32016-04-10 22:10:24 -0700183 # Do not aquire a new port number if the protocol hasn't changed
184 if ((not self.instance.protocol) or
185 (self.instance.protocol != self.cleaned_data.get("protocol"))):
186 self.instance.protocol = self.cleaned_data.get("protocol")
187 self.instance.port_number = (
188 self.instance.provider_service.get_next_available_port(
189 self.instance.protocol))
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700190
Jeremy Mowery3d8f11f2016-04-12 22:54:26 -0700191 if (self.cleaned_data.get('use_ca_from')):
Jeremy Mowery5acf7b92016-04-12 23:31:51 -0700192 self.instance.use_ca_from_id = self.cleaned_data.get(
193 'use_ca_from').id
Jeremy Mowery18d054e2016-04-13 22:18:50 -0700194 else:
195 self.instance.use_ca_from_id = None
Jeremy Mowery3d8f11f2016-04-12 22:54:26 -0700196
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700197 return super(OpenVPNTenantForm, self).save(commit=commit)
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700198
199 class Meta:
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700200 model = OpenVPNTenant
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700201
202
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700203class OpenVPNTenantAdmin(ReadOnlyAwareAdmin):
204 verbose_name = "OpenVPN Tenant Admin"
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700205 list_display = ('id', 'backend_status_icon', 'instance',
206 'server_network', 'vpn_subnet')
207 list_display_links = ('id', 'backend_status_icon',
208 'instance', 'server_network', 'vpn_subnet')
209 fieldsets = [(None, {'fields': ['backend_status_text', 'kind',
210 'provider_service', 'instance', 'creator',
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700211 'server_network', 'vpn_subnet',
Jeremy Mowery36fb9e52016-04-05 23:32:10 -0700212 'is_persistent', 'use_ca_from',
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700213 'clients_can_see_each_other',
214 'failover_servers', "protocol"],
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700215 'classes': ['suit-tab suit-tab-general']})]
216 readonly_fields = ('backend_status_text', 'instance')
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700217 form = OpenVPNTenantForm
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700218 inlines = [TenantPrivilegeInline]
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700219
Jeremy Moweryee71fce2016-04-01 21:54:29 -0700220 suit_form_tabs = (('general', 'Details'),
221 ('tenantprivileges', 'Privileges'))
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700222
223 def queryset(self, request):
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700224 return OpenVPNTenant.get_tenant_objects_by_user(request.user)
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700225
Jeremy Moweryb31bd9e2016-03-14 23:59:11 -0700226
Jeremy Mowery3c5277f2016-02-29 21:09:04 -0700227# Associate the admin forms with the models.
Jeremy Mowery81e84cc2016-04-19 17:01:48 -0700228admin.site.register(OpenVPNService, OpenVPNServiceAdmin)
229admin.site.register(OpenVPNTenant, OpenVPNTenantAdmin)