Add documentation and remove some temporary changes
diff --git a/containers/xos/Dockerfile b/containers/xos/Dockerfile
index b5064ae..d17b2a2 100644
--- a/containers/xos/Dockerfile
+++ b/containers/xos/Dockerfile
@@ -93,3 +93,11 @@
 
 # Define default command.
 CMD update-ca-certificates && python /opt/xos/manage.py runserver 0.0.0.0:8000 --insecure --makemigrations
+
+# for OpenVPN
+RUN mkdir -p /opt/openvpn
+RUN chmod 777 /opt/openvpn
+RUN git clone https://github.com/OpenVPN/easy-rsa.git /opt/openvpn
+RUN git -C /opt/openvpn pull origin master
+RUN echo 'set_var EASYRSA	"/opt/openvpn/easyrsa3"' | tee /opt/openvpn/vars
+RUN echo 'set_var EASYRSA_BATCH	"true"' | tee -a /opt/openvpn/vars
diff --git a/xos/configurations/devel/Makefile b/xos/configurations/devel/Makefile
index 2aaec54..1e650f3 100644
--- a/xos/configurations/devel/Makefile
+++ b/xos/configurations/devel/Makefile
@@ -46,10 +46,3 @@
 
 rebuild_synchronizer:
 	make -C ../../../containers/synchronizer
-
-cleanup_docker: rm
-	sudo docker rm -v $(docker ps -a -q -f status=exited) || true
-	docker rm -v $(docker ps -a -q -f status=exited) || true
-	sudo docker rmi $(docker images -qf "dangling=true") || true
-	docker rmi $(docker images -qf "dangling=true") || true
-	sudo docker run -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker:/var/lib/docker --rm martin/docker-cleanup-volumes || true
diff --git a/xos/configurations/devel/docker-compose.yml b/xos/configurations/devel/docker-compose.yml
index d7d3b2a..018193d 100644
--- a/xos/configurations/devel/docker-compose.yml
+++ b/xos/configurations/devel/docker-compose.yml
@@ -17,32 +17,6 @@
         - ../common/xos_common_config:/opt/xos/xos_configuration/xos_common_config:ro
         - ./images:/opt/xos/images:ro
 
-xos_synchronizer_vpn:
-    image: xosproject/xos-synchronizer-openstack
-    command: bash -c "sleep 120 ; python /opt/xos/synchronizers/vpn/vpn-synchronizer.py -C /opt/xos/synchronizers/vpn/vpn_config"
-    labels:
-        org.xosproject.kind: synchronizer
-        org.xosproject.target: vpn
-    links:
-        - xos_db
-    extra_hosts:
-        - ctl:${MYIP}
-    volumes:
-        - ../setup/id_rsa:/opt/xos/synchronizers/vpn/vpn_private_key:ro  # private key
-    volumes_from:
-        - xos_synchronizer_vpn_data:rw
-
-xos_synchronizer_vpn_data:
-    image: xosproject/xos-synchronizer-openstack
-    labels:
-        org.xosproject.kind: synchronizer
-        org.xosproject.target: vpn
-    links:
-        - xos_db
-    extra_hosts:
-        - ctl:${MYIP}
-    volumes:
-        - /opt/openvpn
 # FUTURE
 #xos_swarm_synchronizer:
 #    image: xosproject/xos-swarm-synchronizer
diff --git a/xos/configurations/frontend/Makefile b/xos/configurations/frontend/Makefile
index 3de63b8..2b46441 100644
--- a/xos/configurations/frontend/Makefile
+++ b/xos/configurations/frontend/Makefile
@@ -32,8 +32,3 @@
 	sudo docker-compose run xos python /opt/xos/tosca/run.py padmin@vicci.org /opt/xos/configurations/frontend/mocks/cord.yaml
 	sudo docker exec frontend_xos_1 cp /opt/xos/configurations/cord/xos_cord_config /opt/xos/xos_configuration/
 	sudo docker exec frontend_xos_1 touch /opt/xos/xos/settings.py
-
-cleanup_docker: rm
-	sudo docker rm -v $(docker ps -a -q -f status=exited) || true
-	sudo docker rmi $(docker images -f "dangling=true" -q) || true
-	sudo docker run -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker:/var/lib/docker --rm martin/docker-cleanup-volumes || true
diff --git a/xos/core/admin.py b/xos/core/admin.py
index e7c9002..5beb30b 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -893,10 +893,12 @@
     )
 
 class TenantRoleAdmin(XOSBaseAdmin):
+    """Admin for TenantRoles."""
     model = TenantRole
     fields = ('role',)
 
 class TenantPrivilegeInline(XOSTabularInline):
+    """Inline for adding a TenantPrivilege to a Tenant.""";
     model = TenantPrivilege
     extra = 0
     suit_classes = 'suit-tab suit-tab-tenantprivileges'
diff --git a/xos/core/models/service.py b/xos/core/models/service.py
index 428dca4..f366991 100644
--- a/xos/core/models/service.py
+++ b/xos/core/models/service.py
@@ -824,11 +824,20 @@
             return cls.objects.filter(id__in=trp_ids)
 
 class TenantRole(PlCoreBase):
+    """A TenantRole option."""
     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):
+    """"A TenantPrivilege which defines how users can access a particular Tenant.
+
+    Attributes:
+        id (models.AutoField): The ID of the privilege.
+        user (models.ForeignKey): A Foreign Key to the a User.
+        tenant (models.ForeignKey): A ForeignKey to the Tenant.
+        role (models.ForeignKey): A ForeignKey to the TenantRole.
+    """
     id = models.AutoField(primary_key=True)
     user = models.ForeignKey('User', related_name="tenantprivileges")
     tenant = models.ForeignKey('Tenant', related_name="tenantprivileges")
diff --git a/xos/core/xoslib/methods/vpnview.py b/xos/core/xoslib/methods/vpnview.py
index e0fbea0..8cb745c 100644
--- a/xos/core/xoslib/methods/vpnview.py
+++ b/xos/core/xoslib/methods/vpnview.py
@@ -1,7 +1,7 @@
 from core.models import TenantPrivilege
 from plus import PlusSerializerMixin
 from rest_framework import serializers
-from services.vpn.models import VPNService, VPNTenant, VPN_KIND
+from services.vpn.models import VPNService, VPNTenant
 from xos.apibase import XOSListCreateAPIView
 
 if hasattr(serializers, "ReadOnlyField"):
@@ -20,22 +20,40 @@
 
 
 class VPNTenantSerializer(serializers.ModelSerializer, PlusSerializerMixin):
-        id = ReadOnlyField()
-        server_network = ReadOnlyField()
-        vpn_subnet = ReadOnlyField()
-        script_text = serializers.SerializerMethodField()
+    """A Serializer for the VPNTenant that has the minimum information required for clients.
 
-        class Meta:
-            model = VPNTenant
-            fields = ('id', 'service_specific_attribute', 'vpn_subnet',
-                      'server_network', 'script_text')
+    Attributes:
+        id (ReadOnlyField): The ID of VPNTenant.
+        server_network (ReadOnlyField): The network of the VPN.
+        vpn_subnet (ReadOnlyField): The subnet of the VPN.
+        script_text (SerializerMethodField): The text of the script for the client to use to
+            connect.
+    """
+    id = ReadOnlyField()
+    server_network = ReadOnlyField()
+    vpn_subnet = ReadOnlyField()
+    script_text = serializers.SerializerMethodField()
 
-        def get_script_text(self, obj):
-            return obj.create_client_script(
-                self.context['request'].user.email + "-" + str(obj.id))
+    class Meta:
+        model = VPNTenant
+        fields = ('id', 'service_specific_attribute', 'vpn_subnet',
+                  'server_network', 'script_text')
+
+    def get_script_text(self, obj):
+        """Gets the text of the client script for the requesting user.
+
+        Parameters:
+            obj (services.vpn.models.VPNTenant): The VPNTenant to connect to.
+
+        Returns:
+            str: The client script as a str.
+        """
+        return obj.create_client_script(
+            self.context['request'].user.email + "-" + str(obj.id))
 
 
 class VPNTenantList(XOSListCreateAPIView):
+    """Class that provides a list of VPNTenants that the user has permission to access."""
     serializer_class = VPNTenantSerializer
     method_kind = "list"
     method_name = "vpntenant"
diff --git a/xos/services/vpn/admin.py b/xos/services/vpn/admin.py
index deb44f5..90420ba 100644
--- a/xos/services/vpn/admin.py
+++ b/xos/services/vpn/admin.py
@@ -106,14 +106,17 @@
     Attributes:
         creator (forms.ModelChoiceField): The XOS user that created 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.
+        server_network (forms.GenericIPAddressField): The IP address of the VPN network.
+        vpn_subnet (forms.GenericIPAddressField): The subnet used by the VPN network.
         is_persistent (forms.BooleanField): Determines if this Tenant keeps
             this connection alive through failures.
+        clients_can_see_each_other (forms.BooleanField): Determines if the clients on the VPN can
+            communicate with each other.
+        failover_servers (forms.ModelMultipleChoiceField): The other VPNTenants to use as failover
+            servers.
+        protocol (forms.ChoiceField): The protocol to use.
+        use_ca_from (forms.ModelChoiceField): Another VPNTenant to use the CA of, this is a very
+            hacky way to let VPNs have the same clients.
     """
     creator = forms.ModelChoiceField(queryset=User.objects.all())
     server_network = forms.GenericIPAddressField(
diff --git a/xos/services/vpn/models.py b/xos/services/vpn/models.py
index 8586eb8..8479e44 100644
--- a/xos/services/vpn/models.py
+++ b/xos/services/vpn/models.py
@@ -11,15 +11,26 @@
     """Defines the Service for creating VPN servers."""
     KIND = VPN_KIND
     OPENVPN_PREFIX = "/opt/openvpn/"
+    """The location of the openvpn EASY RSA files and PKIs."""
     SERVER_PREFIX = OPENVPN_PREFIX + "server-"
+    """The prefix for server PKIs."""
     VARS = OPENVPN_PREFIX + "vars"
+    """The location of the vars file with information for using EASY RSA."""
     EASYRSA_LOC = OPENVPN_PREFIX + "easyrsa3/easyrsa"
-    EASYRSA_COMMAND = EASYRSA_LOC + " --vars=" + VARS
+    """The location of the EASY RSA binary."""
+    EASYRSA_COMMAND_PREFIX = EASYRSA_LOC + " --vars=" + VARS
+    """Prefix for EASY RSA commands."""
 
     @classmethod
     def execute_easyrsa_command(cls, pki_dir, command):
+        """Executes the given EASY RSA command using the given PKI.
+
+        Parameters:
+            pki_dir (str): The directory for the pki to execute the command on.
+            command (str): The command to execute using ESAY RSA.
+        """
         full_command = (
-            VPNService.EASYRSA_COMMAND + " --pki-dir=" +
+            VPNService.EASYRSA_COMMAND_PREIX + " --pki-dir=" +
             pki_dir + " " + command)
         proc = Popen(
             full_command, shell=True, stdout=PIPE, stderr=PIPE
@@ -32,6 +43,14 @@
 
     @classmethod
     def get_pki_dir(cls, tenant):
+        """Gets the directory of the PKI for the given tenant.
+
+        Parameters:
+            tenant (services.vpn.models.VPNTenant): The tenant to get the PKI directory for.
+
+        Returns:
+            str: The pki directory for the tenant.
+        """
         return VPNService.SERVER_PREFIX + str(tenant.id)
 
     class Meta:
@@ -45,6 +64,7 @@
 
     @property
     def exposed_ports(self):
+        """Mapping[str, list(str)]: maps protocols to a list of ports for that protocol."""
         return self.get_attribute("exposed_ports",
                                   self.default_attributes["exposed_ports"])
 
@@ -54,6 +74,7 @@
 
     @property
     def exposed_ports_str(self):
+        """str: a raw str representing the exposed ports."""
         return self.get_attribute("exposed_ports_str",
                                   self.default_attributes["exposed_ports_str"])
 
@@ -62,6 +83,18 @@
         self.set_attribute("exposed_ports_str", value)
 
     def get_next_available_port(self, protocol):
+        """Gets the next free port for the given protocol.
+
+        Parameters:
+            protocol (str): The protocol to get a port for, must be tcp or udp.
+
+        Returns:
+            int: a port number.
+
+        Raises:
+            xos.exceptions.XOSValidationError: If there the protocol is not udp or tcp.
+            xos.exceptions.XOSValidationError: If there are no available ports for the protocol.
+        """
         if protocol != "udp" and protocol != "tcp":
             raise XOSValidationError("Port protocol must be udp or tcp")
         if not self.exposed_ports[protocol]:
@@ -116,6 +149,7 @@
 
     @property
     def protocol(self):
+        """str: The protocol that this tenant is listening on."""
         return self.get_attribute(
             "protocol", self.default_attributes["protocol"])
 
@@ -125,6 +159,7 @@
 
     @property
     def use_ca_from_id(self):
+        """int: The ID of VPNTenant to use to obtain a CA."""
         return self.get_attribute(
             "use_ca_from_id", self.default_attributes["use_ca_from_id"])
 
@@ -195,6 +230,7 @@
 
     @property
     def failover_server_ids(self):
+        """list(int): The IDs of the VPNTenants to use as failover servers."""
         return self.get_attribute(
             "failover_server_ids", self.default_attributes["failover_server_ids"])
 
@@ -224,6 +260,14 @@
         self.set_attribute("port", value)
 
     def create_client_script(self, client_name):
+        """Create a script that a client can use to access this VPNTenant.
+
+        Parameters:
+            client_name (str): The name of the client to use when creating the cerificate.
+
+        Returns:
+            str: A str representing the client script.
+        """
         pki_dir = VPNService.get_pki_dir(self)
         script = ""
         # write the configuration portion
@@ -250,20 +294,51 @@
         return script
 
     def get_ca_crt(self, pki_dir):
+        """Gets the lines fo the ca.crt file for this VPNTenant.
+
+        Parameters:
+            pki_dir (str): The PKI directory to look in.
+
+        Returns:
+            list(str): The lines of the ca.crt file for this VPNTenant.
+        """
         with open(pki_dir + "/ca.crt", 'r') as f:
             return f.readlines()
 
     def get_client_cert(self, client_name, pki_dir):
+        """Gets the lines fo the crt file for a client.
+
+        Parameters:
+            pki_dir (str): The PKI directory to look in.
+            client_name (str): The client name to use.
+
+        Returns:
+            list(str): The lines of the crt file for the client.
+        """
         with open(pki_dir + "/issued/" + client_name + ".crt", 'r') as f:
             return f.readlines()
 
     def get_client_key(self, client_name, pki_dir):
+        """Gets the lines fo the key file for a client.
+
+        Parameters:
+            pki_dir (str): The PKI directory to look in.
+            client_name (str): The client name to use.
+
+        Returns:
+            list(str): The lines of the key file for the client.
+        """
         with open(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
-            VPN server.
+        """Returns the conf file for the given client.
+
+        Parameters:
+            client_name (str): The client name to use.
+
+        Returns:
+            str: Generates the client configuration to use to connect to this VPN server.
         """
         conf = ("client\n" +
                 "dev tun\n" +
@@ -293,7 +368,11 @@
 
 
 def model_policy_vpn_tenant(pk):
-    """Manages the contain for the VPN Tenant."""
+    """Manages the container for the VPN Tenant.
+
+    Parameters
+        pk (int): The ID of this VPNTenant.
+    """
     # This section of code is atomic to prevent race conditions
     with transaction.atomic():
         # We find all of the tenants that are waiting to update
diff --git a/xos/synchronizers/vpn/steps/sync_tenantprivilege.py b/xos/synchronizers/vpn/steps/sync_tenantprivilege.py
index 9b10192..e155dc4 100644
--- a/xos/synchronizers/vpn/steps/sync_tenantprivilege.py
+++ b/xos/synchronizers/vpn/steps/sync_tenantprivilege.py
@@ -10,7 +10,14 @@
 
 
 class SyncTenantPrivilege(SyncStep):
-    """Class for syncing a TenantPrivilege."""
+    """Class for syncing a TenantPrivilege for a VPNTenant.
+
+    This SyncStep isolates the updated TenantPrivileges that are for VPNTenants and performs
+    actions if the TenantPrivilege has been added or deleted. For added privileges a new client
+    certificate and key are made, signed with the ca.crt file used by this VPNTenant. For deleted
+    privileges the client certificate is revoked and the files associated are deleted. In both
+    cases the associated VPNTenant is saved causing the VPNTenant synchronizer to run.
+    """
     provides = [TenantPrivilege]
     observes = TenantPrivilege
     requested_interval = 0
@@ -59,5 +66,14 @@
         record.delete()
 
     def get_certificate_name(self, tenant_privilege):
+        """Gets the name of a certificate for the given TenantPrivilege
+
+        Parameters:
+            tenant_privilege (core.models.TenantPrivilege): The TenantPrivilege to use to generate
+                the certificate name.
+
+        Returns:
+            str: The certificate name.
+        """"
         return (str(tenant_privilege.user.email) +
                 "-" + str(tenant_privilege.tenant.id))
diff --git a/xos/synchronizers/vpn/steps/sync_vpntenant.py b/xos/synchronizers/vpn/steps/sync_vpntenant.py
index 640b6a7..13f2c9a 100644
--- a/xos/synchronizers/vpn/steps/sync_vpntenant.py
+++ b/xos/synchronizers/vpn/steps/sync_vpntenant.py
@@ -12,7 +12,11 @@
 
 
 class SyncVPNTenant(SyncInstanceUsingAnsible):
-    """Class for syncing a VPNTenant using Ansible."""
+    """Class for syncing a VPNTenant using Ansible.
+
+    This SyncStep creates any necessary files for the VPNTenant using ESAY RSA and then runs the
+    Ansible template to start the server on an instance.
+    """
     provides = [VPNTenant]
     observes = VPNTenant
     requested_interval = 0