[SEBA-512]

Fix xosgenx's handling of optional/required modifiers in relation to
the null and blank options, which was nondeterministic depending on the
order of items iterated over in the options dictionary.

Don't allow 'Null' booleans

Change-Id: I3f21180ec11c7e43794c04ebe479d50c11f6271c
diff --git a/VERSION b/VERSION
index 5859406..530cdd9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.2.3
+2.2.4
diff --git a/containers/chameleon/Dockerfile.chameleon b/containers/chameleon/Dockerfile.chameleon
index 90ae1e1..33f64b0 100644
--- a/containers/chameleon/Dockerfile.chameleon
+++ b/containers/chameleon/Dockerfile.chameleon
@@ -13,8 +13,7 @@
 # limitations under the License.
 
 # xosproject/chameleon
-
-FROM xosproject/xos-base:2.2.3
+FROM xosproject/xos-base:2.2.4
 
 # xos-base already has protoc and dependencies installed
 
diff --git a/containers/xos/Dockerfile.client b/containers/xos/Dockerfile.client
index 331f503..ea54ab0 100644
--- a/containers/xos/Dockerfile.client
+++ b/containers/xos/Dockerfile.client
@@ -13,8 +13,7 @@
 # limitations under the License.
 
 # xosproject/xos-client
-
-FROM xosproject/xos-libraries:2.2.3
+FROM xosproject/xos-libraries:2.2.4
 
 # Install XOS client
 COPY lib/xos-api /tmp/xos-api
diff --git a/containers/xos/Dockerfile.libraries b/containers/xos/Dockerfile.libraries
index 3b87c5a..6a58d7d 100644
--- a/containers/xos/Dockerfile.libraries
+++ b/containers/xos/Dockerfile.libraries
@@ -13,8 +13,7 @@
 # limitations under the License.
 
 # xosproject/xos-libraries
-
-FROM xosproject/xos-base:2.2.3
+FROM xosproject/xos-base:2.2.4
 
 # Add libraries
 COPY lib /opt/xos/lib
diff --git a/containers/xos/Dockerfile.synchronizer-base b/containers/xos/Dockerfile.synchronizer-base
index b27815a..042f7c6 100644
--- a/containers/xos/Dockerfile.synchronizer-base
+++ b/containers/xos/Dockerfile.synchronizer-base
@@ -13,8 +13,7 @@
 # limitations under the License.
 
 # xosproject/xos-synchronizer-base
-
-FROM xosproject/xos-client:2.2.3
+FROM xosproject/xos-client:2.2.4
 
 COPY xos/synchronizers/new_base /opt/xos/synchronizers/new_base
 COPY xos/xos/logger.py /opt/xos/xos/logger.py
diff --git a/containers/xos/Dockerfile.xos-core b/containers/xos/Dockerfile.xos-core
index 431f002..d0d7605 100644
--- a/containers/xos/Dockerfile.xos-core
+++ b/containers/xos/Dockerfile.xos-core
@@ -13,8 +13,7 @@
 # limitations under the License.
 
 # xosproject/xos-core
-
-FROM xosproject/xos-libraries:2.2.3
+FROM xosproject/xos-libraries:2.2.4
 
 # Install XOS
 ADD xos /opt/xos
diff --git a/containers/xos/pip_requested.txt b/containers/xos/pip_requested.txt
index c3a3098..889110e 100644
--- a/containers/xos/pip_requested.txt
+++ b/containers/xos/pip_requested.txt
@@ -28,7 +28,7 @@
 oslo.serialization==2.25.0
 oslo.utils==3.36.0
 ply==3.11
-plyxproto==3.1.0
+plyxproto==4.0.0
 protobuf==3.5.2
 prometheus_client==0.4.0
 # Avoids a warning, see http://initd.org/psycopg/docs/faq.html#faq-compile
diff --git a/containers/xos/pip_requirements.txt b/containers/xos/pip_requirements.txt
index f8e4ec4..cb62170 100644
--- a/containers/xos/pip_requirements.txt
+++ b/containers/xos/pip_requirements.txt
@@ -86,7 +86,7 @@
 pika-pool==0.1.3
 pipdeptree==0.13.0
 ply==3.11
-plyxproto==3.1.0
+plyxproto==4.0.0
 prettytable==0.7.2
 prometheus-client==0.4.0
 protobuf==3.5.2
diff --git a/lib/xos-genx/xos-genx-tests/test_jinja2_django.py b/lib/xos-genx/xos-genx-tests/test_jinja2_django.py
index 108ae4e..d5da2d3 100644
--- a/lib/xos-genx/xos-genx-tests/test_jinja2_django.py
+++ b/lib/xos-genx/xos-genx-tests/test_jinja2_django.py
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
 import unittest
 from xosgenx.jinja2_extensions.django import *
 
@@ -33,13 +32,13 @@
         field = {"name": "foo", "options": {"modifier": "required"}}
 
         res = map_xproto_to_django(field)
-        self.assertEqual(res, {"blank": False, "null": False})
+        self.assertEqual(res, {"blank": "False", "null": "False"})
 
     def test_xproto_optional_to_django(self):
         field = {"name": "foo", "options": {"modifier": "optional"}}
 
         res = map_xproto_to_django(field)
-        self.assertEqual(res, {"blank": True, "null": True})
+        self.assertEqual(res, {"blank": "True", "null": "True"})
 
     def test_map_xproto_to_django(self):
 
diff --git a/lib/xos-genx/xosgenx/jinja2_extensions/django.py b/lib/xos-genx/xosgenx/jinja2_extensions/django.py
index 724b85d..3909c00 100644
--- a/lib/xos-genx/xosgenx/jinja2_extensions/django.py
+++ b/lib/xos-genx/xosgenx/jinja2_extensions/django.py
@@ -12,10 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-
+from __future__ import print_function
 from base import *
 import pdb
 import re
+import sys
 
 
 def django_content_type_string(xptags):
@@ -83,48 +84,75 @@
 
 
 def map_xproto_to_django(f):
+
     allowed_keys = [
-        "help_text",
-        "default",
-        "max_length",
-        "modifier",
+        "auto_now_add",
         "blank",
         "choices",
         "db_index",
-        "null",
+        "default",
         "editable",
-        "on_delete",
-        "verbose_name",
-        "auto_now_add",
-        "unique",
-        "min_value",
+        "help_text",
+        "max_length",
         "max_value",
+        "min_value",
+        "null",
+        "on_delete",
+        "unique",
+        "verbose_name",
     ]
 
-    if f.get("link_type") == "manytomany":
-        # map for fields that do not support null
-        m = {
-            "modifier": {"optional": True, "required": False, "_targets": ["blank"]}
-        }
-    else:
-        # map for fields that do support null
-        # TODO evaluate if setting Null = False for all strings
-        m = {
-            "modifier": {"optional": True, "required": False, "_targets": ["null", "blank"]}
-        }
+    out = {}  # output dictionary
 
-    out = {}
-
+    # filter options dict to only have allowed keys
     for k, v in f["options"].items():
         if k in allowed_keys:
-            try:
-                # NOTE this will be used to parse xproto optional/required field prefix
-                # and apply it to the null and blank fields
-                kv2 = m[k]
-                for t in kv2["_targets"]:
-                    out[t] = kv2[v]
-            except BaseException:
-                out[k] = v
+            out[k] = v
+
+    # deal with optional/required modifier fields, and manytomany links
+    # modifier is not added to "out" dict, but affects blank/null truth
+    modifier = f["options"].get('modifier')
+    link_type = f.get("link_type")
+
+    # in some tests, there is no field type
+    if "type" in f:
+        field_type = f["type"]
+    else:
+        field_type = None
+
+    mod_out = {}
+
+    if modifier == "required":
+
+        if field_type == "string":
+            if "blank" not in out:  # if blank is already set, honor that value
+                mod_out["blank"] = 'True'  # by default, required strings can be blank
+        else:
+            mod_out["blank"] = 'False'  # but other required fields can't be blank
+
+        if link_type != "manytomany":
+            mod_out["null"] = 'False'
+
+    elif modifier == "optional":
+
+        mod_out["blank"] = 'True'
+
+        # set defaults on link types
+        if link_type != "manytomany" and field_type != "bool":
+            mod_out["null"] = 'True'
+
+    else:
+        print("map_xproto_to_django - unknown modifier type: %s on %s" % (modifier, f), file=sys.stderr)
+
+    # print an error if there's a field conflict
+    for kmo in mod_out.keys():
+        if kmo in out:
+            if out[kmo] != mod_out[kmo]:
+                print("Option '%s' is manually set to value '%s', which "
+                      "conflicts with value '%s' set automatically by modifier on field: %s" %
+                      (kmo, out[kmo], mod_out[kmo], f), file=sys.stderr)
+
+    out.update(mod_out)  # overwrite out keys with mod_out
 
     return out
 
diff --git a/scripts/xos_dev_reqs.txt b/scripts/xos_dev_reqs.txt
index 4a4abdb..ff3c3ac 100644
--- a/scripts/xos_dev_reqs.txt
+++ b/scripts/xos_dev_reqs.txt
@@ -12,7 +12,8 @@
 netaddr==0.7.19
 networkx==1.11
 nose2==0.7.4
-plyxproto==3.1.0
+ply==3.11
+plyxproto==4.0.0
 pykwalify==1.6.1
 requests-mock==1.5.0
 tosca-parser==0.9.0
diff --git a/xos/core/migrations/0003_auto_20190304_1358.py b/xos/core/migrations/0003_auto_20190304_1358.py
new file mode 100644
index 0000000..04840af
--- /dev/null
+++ b/xos/core/migrations/0003_auto_20190304_1358.py
@@ -0,0 +1,781 @@
+# Copyright 2017-present Open Networking Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.11 on 2019-03-04 18:58
+from __future__ import unicode_literals
+
+import core.models.xosbase_header
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0002_initial_data'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='addresspool_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='addresspool_decl',
+            name='cidr',
+            field=models.CharField(blank=True, help_text=b'Subnet for this AddressPool', max_length=32),
+        ),
+        migrations.AlterField(
+            model_name='addresspool_decl',
+            name='gateway_ip',
+            field=models.CharField(blank=True, help_text=b'Gateway IP address for this AddressPool', max_length=32),
+        ),
+        migrations.AlterField(
+            model_name='addresspool_decl',
+            name='gateway_mac',
+            field=models.CharField(blank=True, help_text=b'Gateway MAC address for this AddressPool', max_length=32),
+        ),
+        migrations.AlterField(
+            model_name='addresspool_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='addresspool_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controller_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controller_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controller_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controllerimages_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllerimages_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllerimages_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controllernetwork_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllernetwork_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllernetwork_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controllerrole_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllerrole_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllerrole_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controllersite_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllersite_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllersite_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controllersiteprivilege_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllersiteprivilege_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllersiteprivilege_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controllerslice_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllerslice_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllerslice_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controllersliceprivilege_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllersliceprivilege_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controllersliceprivilege_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='controlleruser_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controlleruser_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='controlleruser_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='deployment_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='deployment_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='deployment_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='flavor_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='flavor_decl',
+            name='flavor',
+            field=core.models.xosbase_header.StrippedCharField(blank=True, help_text=b'flavor string used to configure deployments', max_length=32),
+        ),
+        migrations.AlterField(
+            model_name='flavor_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='flavor_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='image_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='image_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='image_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='imagedeployments_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='imagedeployments_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='imagedeployments_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='instance_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='instance_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='instance_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='interfacetype_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='interfacetype_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='interfacetype_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='network_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='network_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='network_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='networkparameter_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='networkparameter_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='networkparameter_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='networkparametertype_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='networkparametertype_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='networkparametertype_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='networkslice_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='networkslice_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='networkslice_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='networktemplate_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='networktemplate_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='networktemplate_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='node_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='node_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='node_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='nodelabel_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='nodelabel_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='nodelabel_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='port_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='port_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='port_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='principal_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='principal_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='principal_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='privilege_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='privilege_decl',
+            name='expires',
+            field=models.DateTimeField(blank=True, max_length=1024, null=True),
+        ),
+        migrations.AlterField(
+            model_name='privilege_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='privilege_decl',
+            name='permission',
+            field=models.CharField(blank=True, default=b'all', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='privilege_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='role_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='role_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='role_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='service_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='service_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='service_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='serviceattribute_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceattribute_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceattribute_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='servicedependency_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='servicedependency_decl',
+            name='connect_method',
+            field=models.CharField(blank=True, choices=[(b'none', b'None'), (b'private', b'Private'), (b'public', b'Public')], default=b'none', help_text=b'method to connect the two services', max_length=30),
+        ),
+        migrations.AlterField(
+            model_name='servicedependency_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='servicedependency_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='servicegraphconstraint_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='servicegraphconstraint_decl',
+            name='constraints',
+            field=core.models.xosbase_header.StrippedCharField(blank=True, help_text=b'A composite array defining positions, eg [volt, vsg, [address_manager, vrouter]]', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='servicegraphconstraint_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='servicegraphconstraint_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstance_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstance_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstance_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstanceattribute_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstanceattribute_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstanceattribute_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstancelink_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstancelink_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceinstancelink_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='serviceinterface_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceinterface_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceinterface_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='serviceport_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceport_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='serviceport_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='site_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='site_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='site_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='sitedeployment_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='sitedeployment_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='sitedeployment_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='siteprivilege_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='siteprivilege_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='siteprivilege_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='siterole_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='siterole_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='siterole_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='slice_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='slice_decl',
+            name='controller_replica_count',
+            field=models.IntegerField(blank=True, default=0, help_text=b'Replica count, controller-dependent', null=True),
+        ),
+        migrations.AlterField(
+            model_name='slice_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='slice_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='sliceprivilege_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='sliceprivilege_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='sliceprivilege_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='slicerole_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='slicerole_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='slicerole_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='tag_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='tag_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='tag_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='trustdomain_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='trustdomain_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='trustdomain_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='xoscore_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='xoscore_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='xoscore_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+        migrations.AlterField(
+            model_name='xosguiextension_decl',
+            name='backend_status',
+            field=models.CharField(blank=True, default=b'Provisioning in progress', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='xosguiextension_decl',
+            name='leaf_model_name',
+            field=models.CharField(blank=True, help_text=b'The most specialized model in this chain of inheritance, often defined by a service developer', max_length=1024),
+        ),
+        migrations.AlterField(
+            model_name='xosguiextension_decl',
+            name='updated',
+            field=models.DateTimeField(blank=True, default=django.utils.timezone.now, help_text=b'Time this model was changed by a non-synchronizer'),
+        ),
+    ]
diff --git a/xos/core/models/core.xproto b/xos/core/models/core.xproto
index 7f30600..fbb945e 100644
--- a/xos/core/models/core.xproto
+++ b/xos/core/models/core.xproto
@@ -17,7 +17,7 @@
      optional string backend_register = 6 [default = "{}", max_length = 1024, feedback_state = True];
      required bool backend_need_delete = 7 [default = False, blank = True];
      required bool backend_need_reap = 8 [default = False, blank = True];
-     required string backend_status = 9 [default = "Provisioning in progress", max_length = 1024, null = True, feedback_state = True];
+     required string backend_status = 9 [default = "Provisioning in progress", max_length = 1024, feedback_state = True];
      required int32 backend_code = 10 [default = 0, feedback_state = True];
      required bool deleted = 11 [default = False, blank = True];
      required bool write_protect = 12 [default = False, blank = True];
@@ -69,8 +69,8 @@
      optional string login_page = 18 [max_length = 200, content_type = "stripped", blank = True, help_text = "send this user to a specific page on login", null = True, db_index = False];
      required string created = 19 [db_index = False, null = False, content_type = "date", blank = True];
      required string updated = 20 [db_index = False, null = False, content_type = "date", blank = True];
-     optional string enacted = 21 [db_index = False, null = True, content_type = "date", blank = False];
-     optional string policed = 22 [db_index = False, null = True, content_type = "date", blank = False];
+     optional string enacted = 21 [db_index = False, null = True, content_type = "date"];
+     optional string policed = 22 [db_index = False, null = True, content_type = "date"];
      required string backend_status = 23 [default = "Provisioning in progress", max_length = 1024, content_type = "stripped", blank = False, null = False, db_index = False];
      required int32 backend_code = 24 [default = 0];
      required bool backend_need_delete = 25 [default = False, null = False, db_index = False, blank = True];
@@ -107,7 +107,7 @@
      required string object_type = 5 [null = False, max_length=1024, blank = False];
      required string permission = 6 [null = False, default = "all", max_length=1024, tosca_key=True];
      required string granted = 7 [content_type = "date", auto_now_add = True, max_length=1024];
-     required string expires = 8 [content_type = "date", null = True, max_length=1024];
+     optional string expires = 8 [content_type = "date", max_length=1024];
 }
 
 message AddressPool (XOSBase) {
@@ -126,12 +126,12 @@
 }
 
 // Admins at a deployment have access to controllers at those deployments
-policy controller_policy 
-       < ctx.user.is_admin 
-           | exists Privilege: 
-                  Privilege.accessor_id = ctx.user.id 
-                  & Privilege.object_type = "Deployment" 
-                  & Privilege.permission = "role:admin" 
+policy controller_policy
+       < ctx.user.is_admin
+           | exists Privilege:
+                  Privilege.accessor_id = ctx.user.id
+                  & Privilege.object_type = "Deployment"
+                  & Privilege.permission = "role:admin"
                   & Privilege.object_id = obj.id >
 
 message Controller::controller_policy (XOSBase) {
@@ -168,7 +168,7 @@
 policy slice_policy <
          ctx.user.is_admin
          | (*site_policy(site)
-         & (ctx.user = obj.creator  
+         & (ctx.user = obj.creator
                | (exists Privilege:
                      Privilege.accessor_id = ctx.user.id
                      & Privilege.accessor_type = "User"
@@ -279,7 +279,7 @@
 }
 
 policy image_deployment_policy <
-        *deployment_policy(deployment) 
+        *deployment_policy(deployment)
 >
 
 message ImageDeployments (XOSBase) {
@@ -619,15 +619,15 @@
      optional manytoone service->Service:slices = 8:1006 [db_index = True, null = True, blank = True];
      optional string network = 9 [blank = True, max_length = 256, null = True, db_index = False, choices = "((None, 'Default'), ('host', 'Host'), ('bridged', 'Bridged'), ('noauto', 'No Automatic Networks'))"];
      optional string exposed_ports = 10 [db_index = False, max_length = 256, null = True, blank = True];
-     optional manytoone creator->User:slices = 12:1004 [db_index = True, null = False, blank = False];
+     optional manytoone creator->User:slices = 12:1004 [db_index = True];
      optional manytoone default_flavor->Flavor:slices = 13:1002 [db_index = True, null = True, blank = True];
      optional manytoone default_image->Image:slices = 14:1005 [db_index = True, null = True, blank = True];
      optional manytoone default_node->Node:slices = 15:1003 [db_index = True, null = True, blank = True];
      optional string mount_data_sets = 16 [default = "GenBank", max_length = 256, content_type = "stripped", blank = True, null = True, db_index = False];
      required string default_isolation = 17 [default = "vm", choices = "(('vm', 'Virtual Machine'), ('container', 'Container'), ('container_vm', 'Container In VM'))", max_length = 30, blank = False, null = False, db_index = False];
-     optional manytoone trust_domain->TrustDomain:slices = 18:1002 [db_index = True, null = False, blank = False, help_text = "Trust domain this slice resides in"];
-     optional manytoone principal->Principal:slices = 19:1001 [db_index = True, null = False, blank = False, help_text = "Principal this slice may use to interact with other components"];
-     optional int32 controller_replica_count = 20 [default = 0, null = False, db_index = False, blank = False, help_text = "Replica count, controller-dependent"];
+     optional manytoone trust_domain->TrustDomain:slices = 18:1002 [db_index = True, help_text = "Trust domain this slice resides in"];
+     optional manytoone principal->Principal:slices = 19:1001 [db_index = True, help_text = "Principal this slice may use to interact with other components"];
+     optional int32 controller_replica_count = 20 [default = 0, db_index = False, help_text = "Replica count, controller-dependent"];
      optional string controller_kind = 21 [max_length = 256, content_type = "stripped", blank = True, help_text = "Type of controller, vim-dependent", null = True, db_index = False];
 }
 
diff --git a/xos/synchronizers/new_base/tests/test_payload.py b/xos/synchronizers/new_base/tests/test_payload.py
deleted file mode 100644
index 308133b..0000000
--- a/xos/synchronizers/new_base/tests/test_payload.py
+++ /dev/null
@@ -1,352 +0,0 @@
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-import unittest
-from mock import patch
-import mock
-import pdb
-import networkx as nx
-
-import os
-import sys
-
-test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
-xos_dir = os.path.join(test_path, "..", "..", "..")
-
-ANSIBLE_FILE = "/tmp/payload_test"
-
-log = None
-
-
-def run_fake_ansible_template(*args, **kwargs):
-    opts = args[1]
-    open(ANSIBLE_FILE, "w").write(json.dumps(opts))
-    return [{"rc": 0}]
-
-
-def run_fake_ansible_template_fail(*args, **kwargs):
-    opts = args[1]
-    open(ANSIBLE_FILE, "w").write(json.dumps(opts))
-    return [{"rc": 1}]
-
-
-def get_ansible_output():
-    ansible_str = open(ANSIBLE_FILE).read()
-    return json.loads(ansible_str)
-
-
-class TestPayload(unittest.TestCase):
-    @classmethod
-    def setUpClass(cls):
-
-        global log
-
-        config = os.path.join(test_path, "test_config.yaml")
-        from xosconfig import Config
-
-        Config.clear()
-        Config.init(config, "synchronizer-config-schema.yaml")
-
-        if not log:
-            from multistructlog import create_logger
-
-            log = create_logger(Config().get("logging"))
-
-    def setUp(self):
-
-        global log, steps, event_loop
-
-        self.sys_path_save = sys.path
-        self.cwd_save = os.getcwd()
-        sys.path.append(xos_dir)
-        sys.path.append(os.path.join(xos_dir, "synchronizers", "new_base"))
-        sys.path.append(
-            os.path.join(xos_dir, "synchronizers", "new_base", "tests", "steps")
-        )
-
-        config = os.path.join(test_path, "test_config.yaml")
-        from xosconfig import Config
-
-        Config.clear()
-        Config.init(config, "synchronizer-config-schema.yaml")
-
-        from synchronizers.new_base.mock_modelaccessor_build import (
-            build_mock_modelaccessor,
-        )
-
-        build_mock_modelaccessor(xos_dir, services_dir=None, service_xprotos=[])
-
-        os.chdir(os.path.join(test_path, ".."))  # config references tests/model-deps
-
-        import event_loop
-
-        reload(event_loop)
-        import backend
-
-        reload(backend)
-        import steps.sync_instances
-        import steps.sync_controller_slices
-        from modelaccessor import model_accessor
-
-        # import all class names to globals
-        for (k, v) in model_accessor.all_model_classes.items():
-            globals()[k] = v
-        b = backend.Backend()
-        steps_dir = Config.get("steps_dir")
-        self.steps = b.load_sync_step_modules(steps_dir)
-        self.synchronizer = event_loop.XOSObserver(self.steps)
-
-    def tearDown(self):
-        sys.path = self.sys_path_save
-        os.chdir(self.cwd_save)
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_delete_record(self, mock_run_template, mock_modelaccessor):
-        with mock.patch.object(Instance, "save") as instance_save:
-            o = Instance()
-            o.name = "Sisi Pascal"
-
-            o.synchronizer_step = steps.sync_instances.SyncInstances()
-            self.synchronizer.delete_record(o, log)
-
-            a = get_ansible_output()
-            self.assertDictContainsSubset({"delete": True, "name": o.name}, a)
-            o.save.assert_called_with(update_fields=["backend_need_reap"])
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template_fail,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_delete_record_fail(self, mock_run_template, mock_modelaccessor):
-        with mock.patch.object(Instance, "save") as instance_save:
-            o = Instance()
-            o.name = "Sisi Pascal"
-
-            o.synchronizer_step = steps.sync_instances.SyncInstances()
-
-            with self.assertRaises(Exception) as e:
-                self.synchronizer.delete_record(o, log)
-
-            self.assertEqual(
-                e.exception.message, "Nonzero rc from Ansible during delete_record"
-            )
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_sync_record(self, mock_run_template, mock_modelaccessor):
-        with mock.patch.object(Instance, "save") as instance_save:
-            o = Instance()
-            o.name = "Sisi Pascal"
-
-            o.synchronizer_step = steps.sync_instances.SyncInstances()
-            self.synchronizer.sync_record(o, log)
-
-            a = get_ansible_output()
-            self.assertDictContainsSubset({"delete": False, "name": o.name}, a)
-            o.save.assert_called_with(
-                update_fields=[
-                    "enacted",
-                    "backend_status",
-                    "backend_register",
-                    "backend_code",
-                ]
-            )
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_sync_cohort(self, mock_run_template, mock_modelaccessor):
-        with mock.patch.object(Instance, "save") as instance_save, mock.patch.object(
-            ControllerSlice, "save"
-        ) as controllerslice_save:
-            cs = ControllerSlice()
-            s = Slice(name="SP SP")
-            cs.slice = s
-
-            o = Instance()
-            o.name = "Sisi Pascal"
-            o.slice = s
-
-            cohort = [cs, o]
-            o.synchronizer_step = steps.sync_instances.SyncInstances()
-            cs.synchronizer_step = steps.sync_controller_slices.SyncControllerSlices()
-
-            self.synchronizer.sync_cohort(cohort, False)
-
-            a = get_ansible_output()
-            self.assertDictContainsSubset({"delete": False, "name": o.name}, a)
-            o.save.assert_called_with(
-                update_fields=[
-                    "enacted",
-                    "backend_status",
-                    "backend_register",
-                    "backend_code",
-                ]
-            )
-            cs.save.assert_called_with(
-                update_fields=[
-                    "enacted",
-                    "backend_status",
-                    "backend_register",
-                    "backend_code",
-                ]
-            )
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_deferred_exception(self, mock_run_template, mock_modelaccessor):
-        with mock.patch.object(Instance, "save") as instance_save:
-            cs = ControllerSlice()
-            s = Slice(name="SP SP")
-            cs.slice = s
-            cs.force_defer = True
-
-            o = Instance()
-            o.name = "Sisi Pascal"
-            o.slice = s
-
-            cohort = [cs, o]
-            o.synchronizer_step = steps.sync_instances.SyncInstances()
-            cs.synchronizer_step = steps.sync_controller_slices.SyncControllerSlices()
-
-            self.synchronizer.sync_cohort(cohort, False)
-            o.save.assert_called_with(
-                always_update_timestamp=True,
-                update_fields=["backend_status", "backend_register"],
-            )
-            self.assertEqual(cs.backend_code, 0)
-
-            self.assertIn("Force", cs.backend_status)
-            self.assertIn("Failed due to", o.backend_status)
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_backend_status(self, mock_run_template, mock_modelaccessor):
-        with mock.patch.object(Instance, "save") as instance_save:
-            cs = ControllerSlice()
-            s = Slice(name="SP SP")
-            cs.slice = s
-            cs.force_fail = True
-
-            o = Instance()
-            o.name = "Sisi Pascal"
-            o.slice = s
-
-            cohort = [cs, o]
-            o.synchronizer_step = steps.sync_instances.SyncInstances()
-            cs.synchronizer_step = steps.sync_controller_slices.SyncControllerSlices()
-
-            self.synchronizer.sync_cohort(cohort, False)
-            o.save.assert_called_with(
-                always_update_timestamp=True,
-                update_fields=["backend_status", "backend_register"],
-            )
-            self.assertIn("Force", cs.backend_status)
-            self.assertIn("Failed due to", o.backend_status)
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_fetch_pending(self, mock_run_template, mock_accessor, *_other_accessors):
-        pending_objects, pending_steps = self.synchronizer.fetch_pending()
-        pending_objects2 = list(pending_objects)
-
-        any_cs = next(
-            obj for obj in pending_objects if obj.leaf_model_name == "ControllerSlice"
-        )
-        any_instance = next(
-            obj for obj in pending_objects2 if obj.leaf_model_name == "Instance"
-        )
-
-        slice = Slice()
-        any_instance.slice = slice
-        any_cs.slice = slice
-
-        self.synchronizer.external_dependencies = []
-        cohorts = self.synchronizer.compute_dependent_cohorts(pending_objects, False)
-        flat_objects = [item for cohort in cohorts for item in cohort]
-
-        self.assertEqual(set(flat_objects), set(pending_objects))
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_fetch_pending_with_external_dependencies(
-        self, mock_run_template, mock_accessor, *_other_accessors
-    ):
-        pending_objects, pending_steps = self.synchronizer.fetch_pending()
-        pending_objects2 = list(pending_objects)
-
-        self.synchronizer = event_loop.XOSObserver(self.steps)
-
-        any_cn = next(
-            obj for obj in pending_objects if obj.leaf_model_name == "ControllerNetwork"
-        )
-        any_user = next(
-            obj for obj in pending_objects2 if obj.leaf_model_name == "User"
-        )
-
-        cohorts = self.synchronizer.compute_dependent_cohorts(pending_objects, False)
-
-        flat_objects = [item for cohort in cohorts for item in cohort]
-        self.assertEqual(set(flat_objects), set(pending_objects))
-
-        # These cannot be None, but for documentation purposes
-        self.assertIsNotNone(any_cn)
-        self.assertIsNotNone(any_user)
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_external_dependency_exception(self, mock_run_template, mock_modelaccessor):
-        cs = ControllerSlice()
-        s = Slice(name="SP SP")
-        cs.slice = s
-
-        o = Instance()
-        o.name = "Sisi Pascal"
-        o.slice = s
-
-        cohort = [cs, o]
-        o.synchronizer_step = None
-        o.synchronizer_step = steps.sync_instances.SyncInstances()
-
-        self.synchronizer.sync_cohort(cohort, False)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/xos/synchronizers/new_base/tests/test_run.py b/xos/synchronizers/new_base/tests/test_run.py
deleted file mode 100644
index 863bc0f..0000000
--- a/xos/synchronizers/new_base/tests/test_run.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# Copyright 2017-present Open Networking Foundation
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import json
-import unittest
-from mock import patch
-import mock
-import pdb
-import networkx as nx
-
-import os
-import sys
-
-test_path = os.path.abspath(os.path.dirname(os.path.realpath(__file__)))
-xos_dir = os.path.join(test_path, "..", "..", "..")
-
-ANSIBLE_FILE = "/tmp/payload_test"
-
-
-def run_fake_ansible_template(*args, **kwargs):
-    opts = args[1]
-    open(ANSIBLE_FILE, "w").write(json.dumps(opts))
-
-
-def get_ansible_output():
-    ansible_str = open(ANSIBLE_FILE).read()
-    return json.loads(ansible_str)
-
-
-class TestRun(unittest.TestCase):
-    def setUp(self):
-        self.sys_path_save = sys.path
-        self.cwd_save = os.getcwd()
-        sys.path.append(xos_dir)
-        sys.path.append(os.path.join(xos_dir, "synchronizers", "new_base"))
-        sys.path.append(
-            os.path.join(xos_dir, "synchronizers", "new_base", "tests", "steps")
-        )
-
-        config = os.path.join(test_path, "test_config.yaml")
-        from xosconfig import Config
-
-        Config.clear()
-        Config.init(config, "synchronizer-config-schema.yaml")
-
-        from synchronizers.new_base.mock_modelaccessor_build import (
-            build_mock_modelaccessor,
-        )
-
-        build_mock_modelaccessor(xos_dir, services_dir=None, service_xprotos=[])
-
-        os.chdir(os.path.join(test_path, ".."))  # config references tests/model-deps
-
-        import event_loop
-
-        reload(event_loop)
-        import backend
-
-        reload(backend)
-        from modelaccessor import model_accessor
-
-        # import all class names to globals
-        for (k, v) in model_accessor.all_model_classes.items():
-            globals()[k] = v
-
-        b = backend.Backend()
-        steps_dir = Config.get("steps_dir")
-        self.steps = b.load_sync_step_modules(steps_dir)
-        self.synchronizer = event_loop.XOSObserver(self.steps)
-        try:
-            os.remove("/tmp/sync_ports")
-        except OSError:
-            pass
-        try:
-            os.remove("/tmp/delete_ports")
-        except OSError:
-            pass
-
-    def tearDown(self):
-        sys.path = self.sys_path_save
-        os.chdir(self.cwd_save)
-
-    @mock.patch(
-        "steps.sync_instances.syncstep.run_template",
-        side_effect=run_fake_ansible_template,
-    )
-    @mock.patch("event_loop.model_accessor")
-    def test_run_once(self, mock_run_template, mock_accessor, *_other_accessors):
-
-        pending_objects, pending_steps = self.synchronizer.fetch_pending()
-        pending_objects2 = list(pending_objects)
-
-        any_cs = next(
-            obj for obj in pending_objects if obj.leaf_model_name == "ControllerSlice"
-        )
-        any_instance = next(
-            obj for obj in pending_objects2 if obj.leaf_model_name == "Instance"
-        )
-
-        slice = Slice()
-        any_instance.slice = slice
-        any_cs.slice = slice
-
-        self.synchronizer.run_once()
-
-        sync_ports = open("/tmp/sync_ports").read()
-        delete_ports = open("/tmp/delete_ports").read()
-
-        self.assertIn("successful", sync_ports)
-        self.assertIn("successful", delete_ports)
-
-
-if __name__ == "__main__":
-    unittest.main()