Merge branch 'AddVPNService' of github.com:jermowery/xos into AddVPNService
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 675b8c2..ad49cec 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -270,6 +270,9 @@
     def backend_status_text(self, obj):
         return mark_safe(backend_text(obj))
 
+    def script_link(self, obj):
+        return mark_safe('<a href="%s" target="_blank">Script link</a>' % obj.file_name)
+
     def backend_status_icon(self, obj):
         return mark_safe(backend_icon(obj))
     backend_status_icon.short_description = ""
@@ -494,7 +497,7 @@
 
 class SiteHostsUsersInline(SiteInline):
     def queryset(self, request):
-        return Site.select_by_user(request.user).filter(hosts_users=True)        
+        return Site.select_by_user(request.user).filter(hosts_users=True)
 
 class UserInline(XOSTabularInline):
     model = User
@@ -571,7 +574,7 @@
             kwargs['queryset'] = Service.select_by_user(request.user)
         if db_field.name == 'user':
             kwargs['queryset'] = User.select_by_user(request.user)
-        return super(ServicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)         
+        return super(ServicePrivilegeInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
 
     def queryset(self, request):
         return ServicePrivilege.select_by_user(request.user)
@@ -803,12 +806,12 @@
     def save_model(self, request, obj, form, change):
         # update openstack connection to use this site/tenant
         obj.save_by_user(request.user)
-                    
+
     def delete_model(self, request, obj):
         obj.delete_by_user(request.user)
 
     def queryset(self, request):
-        return Controller.select_by_user(request.user)    
+        return Controller.select_by_user(request.user)
 
     @property
     def suit_form_tabs(self):
@@ -1001,11 +1004,11 @@
 
     def save_model(self, request, obj, form, change):
         # update openstack connection to use this site/tenant
-        obj.save_by_user(request.user) 
+        obj.save_by_user(request.user)
 
     def delete_model(self, request, obj):
         obj.delete_by_user(request.user)
-        
+
 
 class SitePrivilegeAdmin(XOSBaseAdmin):
     fieldList = ['backend_status_text', 'user', 'site', 'role']
@@ -1103,7 +1106,7 @@
           ('slicenetworks','Networks'),
           ('sliceprivileges','Privileges'),
           ('instances','Instances'),
-          #('reservations','Reservations'), 
+          #('reservations','Reservations'),
           ('tags','Tags'),
           ]
 
@@ -1112,7 +1115,7 @@
             tabs.append( ('admin-only', 'Admin-Only') )
 
         return tabs
-    
+
     def add_view(self, request, form_url='', extra_context=None):
         # Ugly hack for CORD
         self.inlines = self.normal_inlines
@@ -1209,7 +1212,7 @@
     def formfield_for_foreignkey(self, db_field, request, **kwargs):
         if db_field.name == 'slice':
             kwargs['queryset'] = Slice.select_by_user(request.user)
-        
+
         if db_field.name == 'user':
             kwargs['queryset'] = User.select_by_user(request.user)
 
@@ -1589,12 +1592,12 @@
                 login_details_fields.remove('profile')
             #if len(request.user.siteprivileges.filter(role__role = 'pi')) > 0:
                 # only admins and pis can change a user's site
-            #    self.readonly_fields = ('backend_status_text', 'site') 
+            #    self.readonly_fields = ('backend_status_text', 'site')
         self.fieldsets = (
             ('Login Details', {'fields': login_details_fields, 'classes':['suit-tab suit-tab-general']}),
             ('Contact Information', {'fields': self.fieldListContactInfo, 'classes':['suit-tab suit-tab-contact']}),
         )
-        return super(UserAdmin, self).get_form(request, obj, **kwargs)     
+        return super(UserAdmin, self).get_form(request, obj, **kwargs)
 
 class ControllerDashboardViewInline(XOSTabularInline):
     model = ControllerDashboardView
@@ -2067,7 +2070,7 @@
 # unregister the Group model from admin.
 #admin.site.unregister(Group)
 
-# When debugging it is often easier to see all the classes, but for regular use 
+# When debugging it is often easier to see all the classes, but for regular use
 # only the top-levels should be displayed
 showAll = False
 
@@ -2103,4 +2106,3 @@
     admin.site.register(TenantRootRole, TenantRootRoleAdmin)
     admin.site.register(TenantAttribute, TenantAttributeAdmin)
 #    admin.site.register(Container, ContainerAdmin)
-
diff --git a/xos/observers/vpn/steps/sync_vpntenant.py b/xos/observers/vpn/steps/sync_vpntenant.py
index c7c3c9d..31500d3 100644
--- a/xos/observers/vpn/steps/sync_vpntenant.py
+++ b/xos/observers/vpn/steps/sync_vpntenant.py
@@ -23,7 +23,7 @@
             objs = VPNTenant.get_tenant_objects().filter(
                 Q(enacted__lt=F('updated')) | Q(enacted=None), Q(lazy_blocked=False))
             for tenant in objs:
-                tenant.client_conf = self.generate_client_conf(tenant)
+                self.create_client_script(tenant)
         else:
             objs = VPNTenant.get_deleted_tenant_objects()
 
@@ -36,6 +36,25 @@
                 "server_address": o.server_address,
                 "client_address": o.client_address}
 
+    def create_client_script(self, tenant):
+        script = open("/opt/xos/core/" + tenant.file_name, 'w')
+        # write the key portion
+        script.write("printf \"")
+        for line in tenant.server_key.splitlines():
+            script.write(line + r"\n")
+        script.write("\" > static.key\n")
+        # write the configuration portion
+        script.write("printf \"")
+        for line in self.generate_client_conf(tenant).splitlines():
+            script.write(line + r"\n")
+        script.write("\" > client.conf\n")
+        # make sure openvpn is installed
+        script.write("apt-get update\n")
+        script.write("apt-get install openvpn\n")
+        script.write("openvpn client.conf &")
+        # close the script
+        script.close()
+
     def generate_client_conf(self, tenant):
         """str: Generates the client configuration to use to connect to this VPN server.
 
diff --git a/xos/services/vpn/admin.py b/xos/services/vpn/admin.py
index ceb59dc..67943b8 100644
--- a/xos/services/vpn/admin.py
+++ b/xos/services/vpn/admin.py
@@ -1,4 +1,4 @@
-
+import time
 from subprocess import PIPE, Popen
 
 from core.admin import ReadOnlyAwareAdmin, SliceInline
@@ -56,20 +56,20 @@
 
     """
     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_key = forms.CharField(required=True, widget=forms.Textarea)
     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)
+    file_name = forms.CharField(required=True)
 
     def __init__(self, *args, **kwargs):
         super(VPNTenantForm, self).__init__(*args, **kwargs)
         self.fields['kind'].widget.attrs['readonly'] = True
         self.fields['server_key'].widget.attrs['readonly'] = True
-        self.fields['client_conf'].widget.attrs['readonly'] = True
+        self.fields['file_name'].widget.attrs['readonly'] = True
         self.fields[
             'provider_service'].queryset = VPNService.get_service_objects().all()
 
@@ -78,7 +78,6 @@
         if self.instance:
             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[
@@ -86,6 +85,7 @@
             self.fields['is_persistent'].initial = self.instance.is_persistent
             self.fields[
                 'can_view_subnet'].initial = self.instance.can_view_subnet
+            self.fields['file_name'].initial = self.instance.file_name
 
         if (not self.instance) or (not self.instance.pk):
             self.fields['creator'].initial = get_request().user
@@ -94,6 +94,7 @@
             self.fields['client_address'].initial = "10.8.0.2"
             self.fields['is_persistent'].initial = True
             self.fields['can_view_subnet'].initial = False
+            self.fields['file_name'].initial = "/static/vpn/" + time.time() + ".vpn"
             if VPNService.get_service_objects().exists():
                 self.fields["provider_service"].initial = VPNService.get_service_objects().all()[
                     0]
@@ -104,10 +105,12 @@
         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.file_name = self.cleaned_data.get('file_name')
         self.instance.can_view_subnet = self.cleaned_data.get(
             'can_view_subnet')
         return super(VPNTenantForm, self).save(commit=commit)
 
+
     def generate_VPN_key(self):
         """str: Generates a VPN key using the openvpn command."""
         proc = Popen("openvpn --genkey --secret /dev/stdout",
@@ -125,11 +128,11 @@
     list_display_links = ('id', 'backend_status_icon', 'instance')
     fieldsets = [(None, {'fields': ['backend_status_text', 'kind',
                                     'provider_service', 'instance', 'creator',
-                                    'server_key', 'client_conf',
+                                    'server_key', 'file_name', 'script_link',
                                     'server_address', 'client_address',
                                     'is_persistent', 'can_view_subnet'],
                          'classes': ['suit-tab suit-tab-general']})]
-    readonly_fields = ('backend_status_text', 'instance')
+    readonly_fields = ('backend_status_text', 'instance', 'script_link')
     form = VPNTenantForm
 
     suit_form_tabs = (('general', 'Details'),)
diff --git a/xos/services/vpn/models.py b/xos/services/vpn/models.py
index 8b2c5d8..2cdb5b9 100644
--- a/xos/services/vpn/models.py
+++ b/xos/services/vpn/models.py
@@ -27,11 +27,11 @@
     sync_attributes = ("nat_ip", "nat_mac",)
 
     default_attributes = {'server_key': 'Error key not found',
-                          'client_conf': 'Configuration not found',
                           'server_address': '10.8.0.1',
                           'client_address': '10.8.0.2',
                           'can_view_subnet': False,
-                          'is_persistent': True}
+                          'is_persistent': True,
+                          'file_name': 'Not found'}
 
     def __init__(self, *args, **kwargs):
         vpn_services = VPNService.get_service_objects().all()
@@ -115,17 +115,6 @@
         self.set_attribute("client_address", value)
 
     @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'])
-
-    @client_conf.setter
-    def client_conf(self, value):
-        self.set_attribute("client_conf", value)
-
-    @property
     def is_persistent(self):
         """bool: True if the VPN connection is persistence, false otherwise."""
         return self.get_attribute(
@@ -147,6 +136,14 @@
     def can_view_subnet(self, value):
         self.set_attribute("can_view_subnet", value)
 
+    @property
+    def file_name(self):
+        self.get_attribute("file_name", self.default_attributes['file_name'])
+
+    @file_name.setter
+    def file_name(self, value):
+        self.set_attribute('file_name', value)
+
 
 def model_policy_vpn_tenant(pk):
     """Manages the contain for the VPN Tenant."""