Hopefully fix everything that needs to be fixed
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 5d2343f..8679cbd 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -906,19 +906,6 @@
     def queryset(self, request):
         return TenantPrivilege.select_by_user(request.user)
 
-class TenantAdmin(XOSBaseAdmin):
-    model = Tenant
-    list_display = ('backend_status_icon', 'name', 'kind')
-    list_display_links = ('backend_status_icon', 'name')
-    fieldList = ('backend_status_text', 'name', 'kind', )
-    fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
-    inlines = (TenantPrivilegeInline)
-    readonly_fields = ('backend_status_text', )
-
-    suit_form_tabs =(('general', 'Tenant Details'),
-        ('tenantprivileges','Privileges')
-    )
-
 class ProviderTenantInline(XOSTabularInline):
     model = CoarseTenant
     fields = ['provider_service', 'subscriber_service', 'connect_method']
@@ -2176,7 +2163,6 @@
     admin.site.register(Flavor, FlavorAdmin)
     admin.site.register(TenantRoot, TenantRootAdmin)
     admin.site.register(TenantRootRole, TenantRootRoleAdmin)
-    admin.site.register(Tenant, TenantAdmin)
-    admin.site.register(TenantRole, TenantRootRoleAdmin)
+    admin.site.register(TenantRole, TenantRoleAdmin)
     admin.site.register(TenantAttribute, TenantAttributeAdmin)
 #    admin.site.register(Container, ContainerAdmin)
diff --git a/xos/core/migrations/0001_initial.py b/xos/core/migrations/0001_initial.py
index db7dad0..33af6a6 100644
--- a/xos/core/migrations/0001_initial.py
+++ b/xos/core/migrations/0001_initial.py
@@ -1,15 +1,15 @@
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 
-from django.db import models, migrations
-import timezones.fields
 import core.models.instance
 import core.models.network
-import geoposition.fields
-import encrypted_fields.fields
 import core.models.serviceclass
 import django.utils.timezone
+import encrypted_fields.fields
+import geoposition.fields
+import timezones.fields
 from django.conf import settings
+from django.db import migrations, models
 
 
 class Migration(migrations.Migration):
@@ -1145,6 +1145,39 @@
             },
             bases=(models.Model,),
         ),
+        migrations.CreateModel(
+            name='TenantPrivilege',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('created', models.DateTimeField(default=django.utils.timezone.now, auto_now_add=True)),
+                ('updated', models.DateTimeField(default=django.utils.timezone.now, auto_now=True)),
+                ('enacted', models.DateTimeField(default=None, null=True, blank=True)),
+                ('policed', models.DateTimeField(default=None, null=True, blank=True)),
+                ('backend_status', models.CharField(default=b'Provisioning in progress', max_length=140)),
+                ('deleted', models.BooleanField(default=False)),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=(models.Model,),
+        ),
+        migrations.CreateModel(
+            name='TenantRole',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('created', models.DateTimeField(default=django.utils.timezone.now, auto_now_add=True)),
+                ('updated', models.DateTimeField(default=django.utils.timezone.now, auto_now=True)),
+                ('enacted', models.DateTimeField(default=None, null=True, blank=True)),
+                ('policed', models.DateTimeField(default=None, null=True, blank=True)),
+                ('backend_status', models.CharField(default=b'Provisioning in progress', max_length=140)),
+                ('deleted', models.BooleanField(default=False)),
+                ('role', models.CharField(unique=True, max_length=30, choices=[(b'admin', b'Admin'), (b'access', b'Access')])),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=(models.Model,),
+        ),
         migrations.AddField(
             model_name='sliceprivilege',
             name='role',
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 015564a..86612f8 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -808,9 +808,7 @@
 
 class TenantRole(PlCoreBase):
     ROLE_CHOICES = (('admin','Admin'), ('access','Access'))
-
     role = StrippedCharField(choices=ROLE_CHOICES, unique=True, max_length=30)
-
     def __unicode__(self):  return u'%s' % (self.role)
 
 class TenantPrivilege(PlCoreBase):
diff --git a/xos/core/models/user.py b/xos/core/models/user.py
index a8ed571..43a4e2b 100644
--- a/xos/core/models/user.py
+++ b/xos/core/models/user.py
@@ -1,21 +1,22 @@
-import os
 import datetime
-import sys
 import hashlib
+import os
+import sys
 from collections import defaultdict
-from django.forms.models import model_to_dict
+from operator import attrgetter, itemgetter
+
+import synchronizers.model_policy
+from core.middleware import get_request
+from core.models import DashboardView, PlCoreBase, PlModelMixIn, Site
+from core.models.plcorebase import StrippedCharField
+from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
+from django.core.exceptions import PermissionDenied
+from django.core.mail import EmailMultiAlternatives
 from django.db import models
 from django.db.models import F, Q
+from django.forms.models import model_to_dict
 from django.utils import timezone
-from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
-from django.core.mail import EmailMultiAlternatives
-from django.core.exceptions import PermissionDenied
-from core.models import PlCoreBase,Site, DashboardView, PlModelMixIn
-from core.models.plcorebase import StrippedCharField
 from timezones.fields import TimeZoneField
-from operator import itemgetter, attrgetter
-from core.middleware import get_request
-import synchronizers.model_policy
 
 # ------ from plcorebase.py ------
 try:
diff --git a/xos/services/vpn/admin.py b/xos/services/vpn/admin.py
index 77a411b..9f1ceda 100644
--- a/xos/services/vpn/admin.py
+++ b/xos/services/vpn/admin.py
@@ -1,4 +1,5 @@
 import os
+import shutil
 
 from core.admin import ReadOnlyAwareAdmin, SliceInline, TenantPrivilegeInline
 from core.middleware import get_request
@@ -123,9 +124,11 @@
     vpn_subnet = forms.GenericIPAddressField(protocol="IPv4", required=True)
     is_persistent = forms.BooleanField(required=False)
     clients_can_see_each_other = forms.BooleanField(required=False)
-    failover_servers = forms.ModelMultipleChoiceField(required=False)
+    failover_servers = forms.ModelMultipleChoiceField(required=False, queryset=VPNTenant.get_tenant_objects())
     protocol = forms.ChoiceField(required=True, choices=[
         ("tcp", "tcp"), ("udp", "udp")])
+    use_ca_from = forms.ModelChoiceField(
+        queryset=VPNTenant.get_tenant_objects(), required=False)
 
     def __init__(self, *args, **kwargs):
         super(VPNTenantForm, self).__init__(*args, **kwargs)
@@ -151,6 +154,11 @@
             self.initial['failover_servers'] = self.instance.failover_servers
             self.fields['failover_servers'].queryset = (
                 VPNTenant.get_tenant_objects().exclude(pk=self.instance.pk))
+            self.fields['use_ca_from'].queryset = (
+                VPNTenant.get_tenant_objects().exclude(pk=self.instance.pk))
+            if (self.instance.use_ca_from):
+                self.fields['use_ca_from'].initial = (
+                    self.instance.use_ca_from[0])
 
         if (not self.instance) or (not self.instance.pk):
             self.fields['creator'].initial = get_request().user
@@ -165,6 +173,7 @@
                     VPNService.get_service_objects().all()[0])
 
     def save(self, commit=True):
+        result = super(VPNTenantForm, self).save(commit=commit)
         self.instance.creator = self.cleaned_data.get("creator")
         self.instance.is_persistent = self.cleaned_data.get('is_persistent')
         self.instance.vpn_subnet = self.cleaned_data.get("vpn_subnet")
@@ -172,6 +181,7 @@
         self.instance.clients_can_see_each_other = self.cleaned_data.get(
             'clients_can_see_each_other')
 
+        self.instance.failover_servers.clear()
         for tenant in self.cleaned_data['failover_servers']:
             self.instance.failover_servers.add(tenant)
 
@@ -180,20 +190,35 @@
             self.instance.provider_service.get_next_available_port(
                 self.instance.protocol))
 
-        result = super(VPNTenantForm, self).save(commit=commit)
-        result.save()
-        pki_dir = VPNService.OPENVPN_PREFIX + "server-" + str(result.id)
-        if (not os.path.isdir(pki_dir)):
-            VPNService.execute_easyrsa_command(pki_dir, "init-pki")
+        self.instance.use_ca_from.clear()
+        self.instance.use_ca_from.append(self.cleaned_data.get('use_ca_from'))
+        result.save()  # Need to do this so that we know the ID
+
+        self.instance.pki_dir = (
+            VPNService.OPENVPN_PREFIX + "server-" + str(result.id))
+
+        if (not os.path.isdir(self.instance.pki_dir)):
             VPNService.execute_easyrsa_command(
-                pki_dir, "--req-cn=XOS build-ca nopass")
-            result.ca_crt = self.generate_ca_crt(pki_dir)
-            result.save()
+                self.instance.pki_dir, "init-pki")
+            if (self.instance.use_ca_from):
+                shutil.copy2(
+                    self.instance.use_ca_from.pki_dir + "/ca.crt",
+                    self.instance.pki_dir)
+            else:
+                VPNService.execute_easyrsa_command(
+                    self.instance.pki_dir, "--req-cn=XOS build-ca nopass")
+        elif (self.instance.use_ca_from):
+            shutil.copy2(
+                self.instance.use_ca_from.pki_dir + "/ca.crt",
+                self.instance.pki_dir)
+
+        result.ca_crt = self.generate_ca_crt()
+
         return result
 
-    def generate_ca_crt(self, pki_dir):
+    def generate_ca_crt(self):
         """str: Generates the ca cert by reading from the ca file"""
-        with open(pki_dir + "/ca.crt") as crt:
+        with open(self.instance.pki_dir + "/ca.crt") as crt:
             return crt.readlines()
 
     class Meta:
@@ -209,7 +234,7 @@
     fieldsets = [(None, {'fields': ['backend_status_text', 'kind',
                                     'provider_service', 'instance', 'creator',
                                     'server_network', 'vpn_subnet',
-                                    'is_persistent',
+                                    'is_persistent', 'use_ca_from',
                                     'clients_can_see_each_other',
                                     'failover_servers', "protocol"],
                          'classes': ['suit-tab suit-tab-general']})]
@@ -235,10 +260,8 @@
             # certificate
             if type(obj) is TenantPrivilege:
                 certificate = self.certificate_name(obj)
-                pki_dir = (
-                    VPNService.OPENVPN_PREFIX + "server-" + str(obj.id))
                 VPNService.execute_easyrsa_command(
-                    pki_dir, "revoke " + certificate)
+                    obj.tenant.pki_dir, "revoke " + certificate)
             # TODO(jermowery): determine if this is necessary.
             # if type(obj) is VPNTenant:
                 # if the tenant was deleted revoke all certs assoicated
@@ -248,10 +271,9 @@
             # If there were any new TenantPrivlege objects then create certs
             if type(obj) is TenantPrivilege:
                 certificate = self.certificate_name(obj)
-                pki_dir = (
-                    VPNService.OPENVPN_PREFIX + "server-" + str(obj.id))
                 VPNService.execute_easyrsa_command(
-                    pki_dir, "build-client-full " + certificate + " nopass")
+                    obj.tenant.pki_dir,
+                    "build-client-full " + certificate + " nopass")
 
 # Associate the admin forms with the models.
 admin.site.register(VPNService, VPNServiceAdmin)
diff --git a/xos/services/vpn/models.py b/xos/services/vpn/models.py
index dfae1e8..b4ff12f 100644
--- a/xos/services/vpn/models.py
+++ b/xos/services/vpn/models.py
@@ -92,6 +92,8 @@
                           'ca_crt': None,
                           'port': None,
                           'script_text': None,
+                          'pki_dir': None,
+                          'use_ca_from': list(),
                           'failover_servers': set(),
                           'protocol': None}
 
@@ -120,6 +122,24 @@
         self.set_attribute("protocol", value)
 
     @property
+    def use_ca_from(self):
+        return self.get_attribute(
+            "use_ca_from", self.default_attributes["use_ca_from"])
+
+    @use_ca_from.setter
+    def use_ca_from(self, value):
+        self.set_attribute("use_ca_from", value)
+
+    @property
+    def pki_dir(self):
+        return self.get_attribute(
+            "pki_dir", self.default_attributes["pki_dir"])
+
+    @pki_dir.setter
+    def pki_dir(self, value):
+        self.set_attribute("pki_dir", value)
+
+    @property
     def addresses(self):
         """Mapping[str, str]: The ip, mac address, and subnet of the NAT
             network of this Tenant."""
@@ -253,14 +273,12 @@
         return script
 
     def get_client_cert(self, client_name):
-        with open(VPNService.OPENVPN_PREFIX + "server-" + str(self.id) +
-                  "/issued/" + client_name + ".crt", 'r') as f:
-                    return f.readlines()
+        with open(self.pki_dir + "/issued/" + client_name + ".crt", 'r') as f:
+            return f.readlines()
 
     def get_client_key(self, client_name):
-        with open(VPNService.OPENVPN_PREFIX + "server-" + str(self.id) +
-                  "/private/" + client_name + ".key", 'r') as f:
-                    return f.readlines()
+        with open(self.pki_dir + "/private/" + client_name + ".key", 'r') as f:
+            return f.readlines()
 
     def generate_client_conf(self, client_name):
         """str: Generates the client configuration to use to connect to this
diff --git a/xos/synchronizers/vpn/steps/sync_vpntenant.py b/xos/synchronizers/vpn/steps/sync_vpntenant.py
index d066237..58a9287 100644
--- a/xos/synchronizers/vpn/steps/sync_vpntenant.py
+++ b/xos/synchronizers/vpn/steps/sync_vpntenant.py
@@ -39,15 +39,13 @@
                     tenant.clients_can_see_each_other),
                 "port_number": tenant.port_number,
                 "protocol": tenant.protocol,
-                "pki_dir": (
-                    VPNService.OPENVPN_PREFIX + "server-" + str(tenant.id))
+                "pki_dir": tenant.pki_dir
                 }
 
     def run_playbook(self, o, fields):
         # Generate the server files
-        pki_dir = VPNService.OPENVPN_PREFIX + "server-" + str(o.id)
-        if (not os.path.isfile(pki_dir + "/issued/server.crt")):
+        if (not os.path.isfile(o.pki_dir + "/issued/server.crt")):
             VPNService.execute_easyrsa_command(
-                pki_dir, "build-server-full server nopass")
-            VPNService.execute_easyrsa_command(pki_dir, "gen-crl")
+                o.pki_dir, "build-server-full server nopass")
+            VPNService.execute_easyrsa_command(o.pki_dir, "gen-crl")
         super(SyncVPNTenant, self).run_playbook(o, fields)