Merge pull request #117 from jermowery/AddHelloWorldService

Add hello world service
diff --git a/xos/ceilometer/models.py b/xos/ceilometer/models.py
index ae73b3e..e149eb5 100644
--- a/xos/ceilometer/models.py
+++ b/xos/ceilometer/models.py
@@ -126,6 +126,12 @@
             for cs in slice.controllerslices.all():
                 if cs.tenant_id:
                     tenant_ids.add(cs.tenant_id)
+        if self.creator.is_admin:
+            #TODO: Ceilometer publishes the SDN meters without associating to any tenant IDs.
+            #For now, ceilometer code is changed to pusblish all such meters with tenant
+            #id as "default_admin_tenant". Here add that default tenant as authroized tenant_id
+            #for all admin users. 
+            tenant_ids.add("default_admin_tenant")
         return tenant_ids
 
     @property
diff --git a/xos/configurations/common/Dockerfile.common b/xos/configurations/common/Dockerfile.common
index 03b479a..26b753d 100644
--- a/xos/configurations/common/Dockerfile.common
+++ b/xos/configurations/common/Dockerfile.common
@@ -60,7 +60,6 @@
 RUN pip install django_rest_swagger
 
 RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python-setuptools
-RUN easy_install django_evolution
 RUN easy_install python_gflags
 RUN easy_install --upgrade httplib2
 RUN easy_install google_api_python_client
diff --git a/xos/configurations/common/Makefile.cloudlab b/xos/configurations/common/Makefile.cloudlab
index 43e7497..6e609d7 100644
--- a/xos/configurations/common/Makefile.cloudlab
+++ b/xos/configurations/common/Makefile.cloudlab
@@ -13,6 +13,7 @@
 
 flat_name:
 	sudo bash -c "source /root/setup/admin-openrc.sh ; neutron net-list" |grep flat|awk '{printf "%s",$$4}' > flat_net_name
+	[ -s flat_net_name ] # throw error if flat_net_name is empty
 
 nodes_yaml:
 	bash ./make-cloudlab-nodes-yaml.sh
diff --git a/xos/configurations/cord/Makefile b/xos/configurations/cord/Makefile
index 86b4ca1..f85f8a4 100644
--- a/xos/configurations/cord/Makefile
+++ b/xos/configurations/cord/Makefile
@@ -16,7 +16,7 @@
 	#NOTE: The below dashboard install scripts assume 
 	#clouldlab openstack environment created using "OpenStack" profile
 	bash install_xos_ceilometer_dashboard.sh
-	bash install_ceilometer_vcpe_notification_listener.sh
+	bash install_ceilometer_patch.sh
 
 stop:
 	sudo docker stop $(RUNNING_CONTAINER)
diff --git a/xos/configurations/cord/README.md b/xos/configurations/cord/README.md
index f0a0a9b..e1b2d2e 100644
--- a/xos/configurations/cord/README.md
+++ b/xos/configurations/cord/README.md
@@ -43,12 +43,16 @@
 
 To get started on CloudLab:
 * Create an experiment using the *OpenStack-CORD* profile.  (You can also use the *OpenStack* profile, but choose *Kilo*
-and disable security groups.)
+with two compute nodes and disable security groups.)
 * Wait until you get an email from CloudLab with title "OpenStack Instance Finished Setting Up".
 * Login to the *ctl* node of your experiment and run:
 ```
 ctl:~$ git clone https://github.com/open-cloud/xos.git
 ctl:~$ cd xos/xos/configurations/cord/
+```
+Edit `cord.yaml` in this directory.  Change the hostnames `cp-1.devel.xos-pg0.clemson.cloudlab.us` and
+`cp-2.devel.xos-pg0.clemson.cloudlab.us` to the names of the compute nodes in your experiment.  Now run:
+```
 ctl:~/xos/xos/configurations/cord$ make
 ```
 
@@ -74,6 +78,16 @@
 ctl:~/xos/xos/configurations/cord/dataplane$ ansible-playbook -i hosts dataplane.yaml
 ```
 
+To setup the dataplane for containers on bare metal, perform these steps in addition to the above (note: make sure to sudo when running the playbook):
+```
+ctl:~/xos/xos/configurations/cord/dataplane$ ./generate-bm.sh > hosts-bm   
+ctl:~/xos/xos/configurations/cord/dataplane$ sudo ansible-playbook -i hosts-bm dataplane-bm.yaml
+```
+
+Check that the vCPE container has started, by going into the XOS UI, selecting 'Services', 'service_vcpe', 'Administration', 'Vcpe Tenants', and make sure there's a green icon next to the vCPE. 
+
+If the vCPE Tenant is still red, then the Instance could be exponentially backed-off due to errors while trying to sync before dataplane.yaml was run. You can reset the exponential backoff by tracking down the vCPE Instance (Slices->mysite_vcpe->Instances, and find the Instance associated with the vCPE Tenant) and hitting the save button.
+
 Currently the vOLT switch is not forwarding ARP and so it is necessary to set up ARP mappings between the client
 and vCPE.  Log into the client and add an ARP entry for the vCPE: 
 ```
@@ -97,12 +111,26 @@
 ```
 The client should now be able to surf the Internet through the dataplane.
 
+## Setting up /etc/hosts
+
+To make it easy to log into the various VMs that make up the dataplane, add entries for them into `/etc/hosts` on the 
+*ctl* node.  As root, run:
+```
+ctl:~/xos/xos/configurations/cord/dataplane$ ./gen-etc-hosts.sh >> /etc/hosts
+```
+For example, to log into the client:
+```
+ctl:~$ ssh ubuntu@client
+```
+
 ## How to log into ONOS
 
-The ONOS Docker container runs in the VMs belonging to the *mysite_onos* slice.  All ports exposed by the ONOS container are forwarded to the outside, and can be accessed from the *ctl* node using the `flat-lan-1-net` address of the hosting VM.  For example, if the IP addresss of the VM is 10.11.10.30, then it is possible to SSH to ONOS as follows (password is *karaf*):
+ONOS apps are run inside Docker containers hosted in VMs.  All ports exposed by the ONOS container are forwarded to the 
+outside, and can be accessed from the *ctl* node over the `flat-lan-1-net` network.  Assuming that `/etc/hosts`
+has been configured as described above, it is possible to SSH to the ONOS running the `virtualbng` app as follows (password is *karaf*):
 
 ```
-$ ssh -p 8101 karaf@10.11.10.30
+$ ssh -p 8101 karaf@onos_vbng
 Password authentication
 Password:
 Welcome to Open Network Operating System (ONOS)!
@@ -126,3 +154,9 @@
    Private IP - Public IP
    10.0.1.3 - 10.254.0.129
 ```
+
+## Troubleshooting
+
+#### Problem: No external connectivity from vCPE container
+1. Make sure the hosts listed in `virtualbng.json` are the actual compute nodes used in your experiment.
+2. Try rebooting the ONOS container running the `virtualbng` app: `$ ssh ubuntu@onos-vbng "sudo docker restart ONOS"`
diff --git a/xos/configurations/cord/ceilometer_vcpe_notification_agent.tar.gz b/xos/configurations/cord/ceilometer_vcpe_notification_agent.tar.gz
deleted file mode 100644
index dcc6765..0000000
--- a/xos/configurations/cord/ceilometer_vcpe_notification_agent.tar.gz
+++ /dev/null
Binary files differ
diff --git a/xos/configurations/cord/cord.yaml b/xos/configurations/cord/cord.yaml
index 46acde9..9929a84 100644
--- a/xos/configurations/cord/cord.yaml
+++ b/xos/configurations/cord/cord.yaml
@@ -14,6 +14,12 @@
           - vcpe_tenant:
               node: service_vcpe
               relationship: tosca.relationships.TenantOfService
+          - lan_network:
+              node: lan_network
+              relationship: tosca.relationships.UsesNetwork
+          - wan_network:
+              node: wan_network
+              relationship: tosca.relationships.UsesNetwork
       properties:
           view_url: /admin/cord/voltservice/$id$/
           kind: vOLT
@@ -241,6 +247,11 @@
           - site:
               node: mysite
               relationship: tosca.relationships.MemberOfSite
+          - vcpe_docker_image:
+              node: andybavier/docker-vcpe
+              relationship: tosca.relationships.UsesImage
+      properties:
+          default_isolation: container
 
     mysite_onos_vbng:
       description: ONOS Controller Slice for vBNG
diff --git a/xos/configurations/cord/dataplane/dataplane-bm.yaml b/xos/configurations/cord/dataplane/dataplane-bm.yaml
index 2fbbf6a..e1e78ee 100644
--- a/xos/configurations/cord/dataplane/dataplane-bm.yaml
+++ b/xos/configurations/cord/dataplane/dataplane-bm.yaml
@@ -5,11 +5,15 @@
   - name: Create tunnel port on br-lan
     openvswitch_port:
       bridge=br-lan
-      port={{ grename }}
+      port={{ item }}
       state=present
+    with_items: "grenames.split(' ')"
 
   - name: Set up GRE tunnel to vCPE
-    shell: ovs-vsctl set Interface {{ grename }} type=gre options:remote_ip={{ bm_addr }}
+    shell: ovs-vsctl set Interface {{ item.0 }} type=gre options:remote_ip={{ item.1 }}
+    with_together:
+      - "grenames.split(' ')"
+      - "bm_ips.split(' ')"
 
 - hosts: baremetal
 
diff --git a/xos/configurations/cord/dataplane/dataplane.yaml b/xos/configurations/cord/dataplane/dataplane.yaml
index f43e4d7..489609a 100644
--- a/xos/configurations/cord/dataplane/dataplane.yaml
+++ b/xos/configurations/cord/dataplane/dataplane.yaml
@@ -158,6 +158,14 @@
     shell: ifconfig br-lan inet6 del {{ ipv6.stdout }}
     when: ipv6.stdout != ""
 
+  - name: Check if veth1 has an IPv6 address
+    shell: ip addr show veth1|grep inet6|awk '{print $2}'
+    register: ipv6
+
+  - name: Remove veth1 IPv6 address if present
+    shell: ifconfig veth1 inet6 del {{ ipv6.stdout }}
+    when: ipv6.stdout != ""
+
   - name: Run the datapath
     command: /usr/local/bin/ofdatapath -i veth1,br-lan punix:/tmp/s1 -d 000000000001 --no-slicing -D -P
       creates=/usr/local/var/run/ofdatapath.pid
diff --git a/xos/configurations/cord/dataplane/generate-bm.sh b/xos/configurations/cord/dataplane/generate-bm.sh
index 1a3ec78..25f6fa5 100755
--- a/xos/configurations/cord/dataplane/generate-bm.sh
+++ b/xos/configurations/cord/dataplane/generate-bm.sh
@@ -1,4 +1,4 @@
-source ~/admin-openrc.sh
+source ../../common/admin-openrc.sh
 
 get_ip () {
     LABEL=$1
@@ -6,23 +6,28 @@
     nova list --all-tenants|grep $LABEL|sed "s/^.*$NETWORK=//g"|sed 's/; .*$//g'|awk '{print $1}'
     }
 
+GRENAMES=()
+BM_IPS=()
+
 NODES=`sudo bash -c "source /root/setup/admin-openrc.sh ; nova hypervisor-list" |grep cloudlab|awk '{print $4}'`
 I=1
 for NODE in $NODES; do
-    IP=`getent hosts $NODE | awk '{ print $1 }'`
-    echo switch_volt$I    ansible_ssh_host=$( get_ip mysite_volt flat-lan-1-net) grename=gre-bm-$I bm_addr=$IP
-    echo bm$I           ansible_ssh_host=$IP grename=gre-bm-$I volt_addr=$( get_ip mysite_volt lan_network)  ansible_ssh_private_key_file=/root/.ssh/id_rsa
+    BM_SSH_IP=`getent hosts $NODE | awk '{ print $1 }'`
+    IFS=. read BM_NAME BM_REMAINDER <<< $NODE
+    BM_IP=`sudo grep -i $BM_NAME /root/setup/data-hosts.flat-lan-1 | awk '{print $1}'`
+
+    GRE_NAMES+=("gre-bm-$I")
+    BM_IPS+=("$BM_IP")
+
+    #echo switch_volt$I    ansible_ssh_host=$( get_ip mysite_volt flat-lan-1-net) grename=gre-bm-$I bm_addr=$BM_IP
+    echo bm$I           ansible_ssh_host=$BM_SSH_IP grename=gre-bm-$I volt_addr=$( get_ip mysite_volt flat-lan-1-net)  ansible_ssh_private_key_file=/root/.ssh/id_rsa
     I=$(( I+1 ))
 done
 
-# a kludge for now -- just rerun the onos_volt step for each baremetal machine
+GRE_NAMES=${GRE_NAMES[@]}
+BM_IPS=${BM_IPS[@]}
 
-echo "[switch_volt]"
-I=1
-for NODE in $NODES; do
-    echo switch_volt$I
-    I=$((I+1))
-done
+echo switch_volt ansible_ssh_host=$( get_ip mysite_volt flat-lan-1-net) grenames=\"$GRE_NAMES\" bm_ips=\"$BM_IPS\"
 
 echo "[baremetal]"
 I=1
diff --git a/xos/configurations/cord/install_ceilometer_patch.sh b/xos/configurations/cord/install_ceilometer_patch.sh
new file mode 100755
index 0000000..279e07c
--- /dev/null
+++ b/xos/configurations/cord/install_ceilometer_patch.sh
@@ -0,0 +1,20 @@
+if [ -d /usr/lib/python2.7/dist-packages/ceilometer/network/ext_services ]; then
+    echo "Seems VCPE notification listeners are already enabled in ceilometer... so exiting gracefully..."
+    exit 0
+fi
+echo "Verifying if all the required files are present"
+if [ ! -f openstack_ceilometer_patch.tar.gz ];
+then
+    echo "File openstack_ceilometer_patch.tar.gz not found"
+    exit 1
+fi
+echo "Copying the ceilometer patch files to /usr/lib/python2.7/dist-packages/ceilometer"
+tar -xzf openstack_ceilometer_patch.tar.gz
+sudo mv ceilometer/network/ext_services /usr/lib/python2.7/dist-packages/ceilometer/network/
+sudo mv ceilometer/network/statistics /usr/lib/python2.7/dist-packages/ceilometer/network/
+sudo mv ceilometer-2015.1.1.egg-info/entry_points.txt /usr/lib/python2.7/dist-packages/ceilometer-*egg-info/
+sudo mv pipeline.yaml /etc/ceilometer/
+echo "Restarting ceilometer-agent-notification"
+sudo service ceilometer-agent-notification restart
+echo "Restarting ceilometer-agent-central"
+sudo service ceilometer-agent-central restart
diff --git a/xos/configurations/cord/install_ceilometer_vcpe_notification_listener.sh b/xos/configurations/cord/install_ceilometer_vcpe_notification_listener.sh
deleted file mode 100755
index 1992e80..0000000
--- a/xos/configurations/cord/install_ceilometer_vcpe_notification_listener.sh
+++ /dev/null
@@ -1,16 +0,0 @@
-if [ -d /usr/lib/python2.7/dist-packages/ceilometer/network/ext_services ]; then
-    echo "Seems VCPE notification listeners are already enabled in ceilometer... so exiting gracefully..."
-    exit 0
-fi
-echo "Verifying if all the required files are present"
-if [ ! -f ceilometer_vcpe_notification_agent.tar.gz ];
-then
-    echo "File ceilometer_vcpe_notification_agent.tar.gz not found"
-    exit 1
-fi
-echo "Copying the ceilometer vcpe notification agent files /usr/lib/python2.7/dist-packages/ceilometer"
-tar -xzf ceilometer_vcpe_notification_agent.tar.gz
-sudo mv ceilometer/network/ext_services /usr/lib/python2.7/dist-packages/ceilometer/network/
-sudo mv ceilometer-2015.1.1.egg-info/entry_points.txt /usr/lib/python2.7/dist-packages/ceilometer-*egg-info/
-echo "Restarting ceilometer-agent-notification"
-sudo service ceilometer-agent-notification restart
diff --git a/xos/configurations/cord/openstack_ceilometer_patch.tar.gz b/xos/configurations/cord/openstack_ceilometer_patch.tar.gz
new file mode 100644
index 0000000..dc1852a
--- /dev/null
+++ b/xos/configurations/cord/openstack_ceilometer_patch.tar.gz
Binary files differ
diff --git a/xos/configurations/cord/xos_metering_dashboard.tar.gz b/xos/configurations/cord/xos_metering_dashboard.tar.gz
index 648f91a..3b4d127 100644
--- a/xos/configurations/cord/xos_metering_dashboard.tar.gz
+++ b/xos/configurations/cord/xos_metering_dashboard.tar.gz
Binary files differ
diff --git a/xos/core/admin.py b/xos/core/admin.py
index 26c6dba..be9dcc0 100644
--- a/xos/core/admin.py
+++ b/xos/core/admin.py
@@ -23,7 +23,6 @@
 from cgi import escape as html_escape
 from django.contrib import messages
 
-import django_evolution
 import threading
 
 # thread locals necessary to work around a django-suit issue
@@ -106,6 +105,10 @@
             # this 'if' might be redundant if save_by_user is implemented right
             raise PermissionDenied
 
+        # reset exponential backoff
+        if hasattr(obj, "backend_register"):
+            obj.backend_register = "{}"
+
         obj.caller = request.user
         # update openstack connection to use this site/tenant
         obj.save_by_user(request.user)
@@ -2033,12 +2036,6 @@
 # unregister the Group model from admin.
 #admin.site.unregister(Group)
 
-#Do not show django evolution in the admin interface
-from django_evolution.models import Version, Evolution
-#admin.site.unregister(Version)
-#admin.site.unregister(Evolution)
-
-
 # When debugging it is often easier to see all the classes, but for regular use 
 # only the top-levels should be displayed
 showAll = False
diff --git a/xos/openstack_observer/steps/sync_container.py b/xos/openstack_observer/steps/sync_container.py
index b9b41c5..272e5f8 100644
--- a/xos/openstack_observer/steps/sync_container.py
+++ b/xos/openstack_observer/steps/sync_container.py
@@ -7,7 +7,7 @@
 from django.db.models import F, Q
 from xos.config import Config
 from observers.base.SyncInstanceUsingAnsible import SyncInstanceUsingAnsible
-from observer.syncstep import SyncStep
+from observer.syncstep import SyncStep, DeferredException
 from observer.ansible import run_template_ssh
 from core.models import Service, Slice, Instance
 from services.onos.models import ONOSService, ONOSApp
@@ -45,7 +45,7 @@
         for parent_port in instance.parent.ports.all():
             if parent_port.network == port.network:
                 if not parent_port.mac:
-                     raise Exception("parent port on network %s does not have mac yet" % parent_port.network.name)
+                     raise DeferredException("parent port on network %s does not have mac yet" % parent_port.network.name)
                 return parent_port.mac
         raise Exception("failed to find corresponding parent port for network %s" % port.network.name)
 
@@ -56,18 +56,24 @@
             if (not port.ip):
                 # 'unmanaged' ports may have an ip, but no mac
                 # XXX: are there any ports that have a mac but no ip?
-                raise Exception("Port on network %s is not yet ready" % port.network.name)
+                raise DeferredException("Port on network %s is not yet ready" % port.network.name)
 
             pd={}
             pd["mac"] = port.mac or ""
             pd["ip"] = port.ip or ""
             pd["xos_network_id"] = port.network.id
 
+            if port.network.name == "wan_network":
+                if port.ip:
+                    (a, b, c, d) = port.ip.split('.')
+                    pd["mac"] = "02:42:%02x:%02x:%02x:%02x" % (int(a), int(b), int(c), int(d))
+
+
             if o.isolation == "container":
                 # container on bare metal
                 instance_port = self.get_instance_port(port)
                 if not instance_port:
-                    raise Exception("No instance on slice for port on network %s" % port.network.name)
+                    raise DeferredException("No instance on slice for port on network %s" % port.network.name)
 
                 pd["snoop_instance_mac"] = instance_port.mac
                 pd["snoop_instance_id"] = instance_port.instance.instance_id
diff --git a/xos/openstack_observer/steps/sync_container.yaml b/xos/openstack_observer/steps/sync_container.yaml
index 56edaea..b60ffb8 100644
--- a/xos/openstack_observer/steps/sync_container.yaml
+++ b/xos/openstack_observer/steps/sync_container.yaml
@@ -104,6 +104,12 @@
     shell: systemctl daemon-reload
     when: systemctl.stat.exists == True
 
+{% if ports %}
+  - name: make sure bridges are setup
+    shell: ifconfig {{ '{{' }} item.bridge {{ '}}' }}
+    with_items: "ports"
+{% endif %}
+
   - name: Make sure container is running
     service: name=container-{{ container_name }} state=started
 
diff --git a/xos/tosca/custom_types/xos.m4 b/xos/tosca/custom_types/xos.m4
index 3606601..4f726fc 100644
--- a/xos/tosca/custom_types/xos.m4
+++ b/xos/tosca/custom_types/xos.m4
@@ -588,6 +588,10 @@
                 type: integer
                 default: 10
                 description: Quota of instances that this slice may create.
+            default_isolation:
+                type: string
+                required: false
+                description: default isolation to use when bringing up instances (default to 'vm')
 
     tosca.nodes.Node:
         derived_from: tosca.nodes.Root
diff --git a/xos/tosca/custom_types/xos.yaml b/xos/tosca/custom_types/xos.yaml
index 7a6030e..246c922 100644
--- a/xos/tosca/custom_types/xos.yaml
+++ b/xos/tosca/custom_types/xos.yaml
@@ -776,6 +776,10 @@
                 type: integer
                 default: 10
                 description: Quota of instances that this slice may create.
+            default_isolation:
+                type: string
+                required: false
+                description: default isolation to use when bringing up instances (default to 'vm')
 
     tosca.nodes.Node:
         derived_from: tosca.nodes.Root
diff --git a/xos/tosca/resources/onosapp.py b/xos/tosca/resources/onosapp.py
index 111cf9a..648bb09 100644
--- a/xos/tosca/resources/onosapp.py
+++ b/xos/tosca/resources/onosapp.py
@@ -43,9 +43,9 @@
             if attrs:
                 attr = attrs[0]
                 if attr.value != value:
-                    self.info("updating attribute %s" % k)
-                    attrs.value = value
-                    attrs.save()
+                    self.info("updating attribute %s" % prop_name)
+                    attr.value = value
+                    attr.save()
             else:
                 self.info("adding attribute %s" % prop_name)
                 ta = TenantAttribute(tenant=obj, name=prop_name, value=value)
diff --git a/xos/tosca/resources/slice.py b/xos/tosca/resources/slice.py
index 2c02365..e37bfc8 100644
--- a/xos/tosca/resources/slice.py
+++ b/xos/tosca/resources/slice.py
@@ -12,7 +12,7 @@
 class XOSSlice(XOSResource):
     provides = "tosca.nodes.Slice"
     xos_model = Slice
-    copyin_props = ["enabled", "description", "slice_url", "max_instances"]
+    copyin_props = ["enabled", "description", "slice_url", "max_instances", "default_isolation"]
 
     def get_xos_args(self):
         args = super(XOSSlice, self).get_xos_args()
diff --git a/xos/tosca/samples/container_slice.yaml b/xos/tosca/samples/container_slice.yaml
new file mode 100644
index 0000000..520bec0
--- /dev/null
+++ b/xos/tosca/samples/container_slice.yaml
@@ -0,0 +1,24 @@
+tosca_definitions_version: tosca_simple_yaml_1_0
+
+description: >
+    * Create a new deployment, controller, and site.
+    * Add a SiteDeployment from the site to the deployment using the controller.
+    * Create a Slice in the Site, with one Instance
+
+imports:
+   - custom_types/xos.yaml
+
+topology_template:
+  node_templates:
+    mysite:
+      type: tosca.nodes.Site
+
+    mysite_containers:
+      type: tosca.nodes.Slice
+      requirements:
+          - slice:
+                node: mysite
+                relationship: tosca.relationships.MemberOfSite
+      properties:
+          default_isolation: container
+
diff --git a/xos/xos/settings.py b/xos/xos/settings.py
index b8dea14..fc2ad2f 100644
--- a/xos/xos/settings.py
+++ b/xos/xos/settings.py
@@ -150,7 +150,6 @@
     'django.contrib.admindocs',
     'rest_framework',
     'django_extensions',
-    'django_evolution',
     'core',
     'hpc',
     'cord',
@@ -166,10 +165,9 @@
 )
 
 if DJANGO_VERSION[1]>=7:
-    # if django >= 1.7, then remove evolution and change the admin module
+    # if django >= 1.7, then change the admin module
     INSTALLED_APPS = list(INSTALLED_APPS)
     INSTALLED_APPS[INSTALLED_APPS.index('django.contrib.admin')] = 'django.contrib.admin.apps.SimpleAdminConfig'
-    INSTALLED_APPS.remove('django_evolution')
     INSTALLED_APPS = tuple(INSTALLED_APPS)
 
 # Added for django-suit form