CORD-2510 Migrate vEG to dynamic load

Change-Id: I390345541c35d57c0eade808ddc7af79dddf2f82
diff --git a/xos/synchronizer/model_policies/test_model_policy_vegtenant.py b/xos/synchronizer/model_policies/test_model_policy_vegtenant.py
index 34c94ac..2761846 100644
--- a/xos/synchronizer/model_policies/test_model_policy_vegtenant.py
+++ b/xos/synchronizer/model_policies/test_model_policy_vegtenant.py
@@ -27,9 +27,17 @@
     xos_dir=os.path.join(test_path, "../../../../../../orchestration/xos/xos")
     services_dir=os.path.join(xos_dir, "../../xos_services")
 
-# ---------------------------------------------------------------------------------------------------------------------
-# End Model Policy Testing Framework
-# ---------------------------------------------------------------------------------------------------------------------
+# While transitioning from static to dynamic load, the path to find neighboring xproto files has changed. So check
+# both possible locations...
+def get_models_fn(service_name, xproto_name):
+    name = os.path.join(service_name, "xos", xproto_name)
+    if os.path.exists(os.path.join(services_dir, name)):
+        return name
+    else:
+        name = os.path.join(service_name, "xos", "synchronizer", "models", xproto_name)
+        if os.path.exists(os.path.join(services_dir, name)):
+            return name
+    raise Exception("Unable to find service=%s xproto=%s" % (service_name, xproto_name))
 
 class TestModelPolicyVsgTenant(unittest.TestCase):
     def setUp(self):
@@ -45,7 +53,8 @@
         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, ["vEG/xos/veg.xproto", "addressmanager/xos/addressmanager.xproto"])
+        build_mock_modelaccessor(xos_dir, services_dir, [get_models_fn("vEG", "veg.xproto"),
+                                                         get_models_fn("addressmanager", "addressmanager.xproto")])
 
         import synchronizers.new_base.modelaccessor
         import synchronizers.new_base.model_policies.model_policy_tenantwithcontainer
diff --git a/xos/synchronizer/models/attic/header.py b/xos/synchronizer/models/attic/header.py
new file mode 100644
index 0000000..81facf7
--- /dev/null
+++ b/xos/synchronizer/models/attic/header.py
@@ -0,0 +1,27 @@
+# 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.
+from django.db import models
+from core.models import Service, XOSBase, Slice, Instance, TenantWithContainer, Node, Image, User, Flavor, NetworkParameter, NetworkParameterType, Port, AddressPool, User
+from core.models.xosbase import StrippedCharField
+import os
+from django.db import models, transaction
+from django.forms.models import model_to_dict
+from django.db.models import *
+from operator import itemgetter, attrgetter, methodcaller
+from core.models import Tag
+from core.models.service import LeastLoadedNodeScheduler
+from services.addressmanager.models import AddressManagerService, AddressManagerServiceInstance
+import traceback
+from xos.exceptions import *
+from django.contrib.contenttypes.models import ContentType
diff --git a/xos/synchronizer/models/attic/vegtenant_bottom.py b/xos/synchronizer/models/attic/vegtenant_bottom.py
new file mode 100644
index 0000000..e523f0a
--- /dev/null
+++ b/xos/synchronizer/models/attic/vegtenant_bottom.py
@@ -0,0 +1,28 @@
+
+# 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.
+
+
+def model_policy_veg(pk):
+    # TODO: this should be made in to a real model_policy
+    with transaction.atomic():
+        veg = VEGTenant.objects.select_for_update().filter(pk=pk)
+        if not veg:
+            return
+        veg = veg[0]
+        veg.manage_container()
+        veg.manage_vrouter()
+        veg.cleanup_orphans()
+
+
diff --git a/xos/synchronizer/models/attic/vegtenant_model.py b/xos/synchronizer/models/attic/vegtenant_model.py
new file mode 100644
index 0000000..1c1d15a
--- /dev/null
+++ b/xos/synchronizer/models/attic/vegtenant_model.py
@@ -0,0 +1,128 @@
+
+# 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.
+
+
+sync_attributes = ("wan_container_ip", "wan_container_mac", "wan_container_netbits",
+                   "wan_container_gateway_ip", "wan_container_gateway_mac",
+                   "wan_vm_ip", "wan_vm_mac")
+
+
+def __init__(self, *args, **kwargs):
+    super(VEGTenant, self).__init__(*args, **kwargs)
+    self.cached_address_service_instance = None
+@property
+def address_service_instance(self):
+    address_service_instance = self.get_newest_subscribed_tenant(AddressManagerServiceInstance)
+    if not address_service_instance:
+        return None
+
+    # always return the same object when possible
+    if (self.cached_address_service_instance) and (self.cached_address_service_instance.id == address_service_instance.id):
+        return self.cached_address_service_instance
+
+    address_service_instance.caller = self.creator
+    self.cached_address_service_instance = address_service_instance
+    return address_service_instance
+
+@address_service_instance.setter
+def address_service_instance(self, value):
+    raise XOSConfigurationError("VEGTenant.address_service_instance setter is not implemented")
+
+@property
+def volt(self):
+    from services.volt.models import VOLTTenant
+    if not self.subscriber_tenant:
+        return None
+    volts = VOLTTenant.objects.filter(id=self.subscriber_tenant.id)
+    if not volts:
+        return None
+    return volts[0]
+
+@volt.setter
+def volt(self, value):
+    raise XOSConfigurationError("VEGTenant.volt setter is not implemented")
+
+@property
+def ssh_command(self):
+    if self.instance:
+        return self.instance.get_ssh_command()
+    else:
+        return "no-instance"
+
+def get_address_service_instance_field(self, name, default=None):
+    if self.address_service_instance:
+        return getattr(self.address_service_instance, name, default)
+    else:
+        return default
+
+@property
+def wan_container_ip(self):
+    return self.get_address_service_instance_field("public_ip", None)
+
+@property
+def wan_container_mac(self):
+    return self.get_address_service_instance_field("public_mac", None)
+
+@property
+def wan_container_netbits(self):
+    return self.get_address_service_instance_field("netbits", None)
+
+@property
+def wan_container_gateway_ip(self):
+    return self.get_address_service_instance_field("gateway_ip", None)
+
+@property
+def wan_container_gateway_mac(self):
+    return self.get_address_service_instance_field("gateway_mac", None)
+
+@property
+def wan_vm_ip(self):
+    tags = Tag.objects.filter(content_type=self.instance.get_content_type_key(), object_id=self.instance.id, name="vm_vrouter_tenant")
+    if tags:
+        tenant = AddressManagerServiceInstance.objects.get(id=tags[0].value)
+        return tenant.public_ip
+    else:
+        raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
+
+@property
+def wan_vm_mac(self):
+    tags = Tag.objects.filter(content_type=self.instance.get_content_type_key(), object_id=self.instance.id, name="vm_vrouter_tenant")
+    if tags:
+        tenant = AddressManagerServiceInstance.objects.get(id=tags[0].value)
+        return tenant.public_mac
+    else:
+        raise Exception("no vm_vrouter_tenant tag for instance %s" % o.instance)
+
+@property
+def is_synced(self):
+    return (self.enacted is not None) and (self.enacted >= self.updated)
+
+@is_synced.setter
+def is_synced(self, value):
+    pass
+def save(self, *args, **kwargs):
+    if not self.creator:
+        if not getattr(self, "caller", None):
+            # caller must be set when creating a vEG since it creates a slice
+            raise XOSProgrammingError("VEGTenant's self.caller was not set")
+        self.creator = self.caller
+        if not self.creator:
+            raise XOSProgrammingError("VEGTenant's self.creator was not set")
+
+    super(VEGTenant, self).save(*args, **kwargs)
+
+
+def delete(self, *args, **kwargs):
+    super(VEGTenant, self).delete(*args, **kwargs)
diff --git a/xos/synchronizer/models/veg.xproto b/xos/synchronizer/models/veg.xproto
new file mode 100644
index 0000000..f8d5a0e
--- /dev/null
+++ b/xos/synchronizer/models/veg.xproto
@@ -0,0 +1,20 @@
+option name = "veg";
+option kind = "vEG";
+option verbose_name = "vEG Service";
+option app_label = "veg";
+
+message VEGService (Service){
+    option verbose_name = "vEG Service";
+
+    optional string url_filter_kind = 1 [choices = "((None, 'None'), ('safebrowsing', 'Safe Browsing'), ('answerx', 'AnswerX'))", max_length = 30, content_type = "stripped", blank = True, null = True, db_index = False];
+    required string dns_servers = 2 [default = "8.8.8.8", max_length = 255, content_type = "stripped", blank = False, null = False, db_index = False];
+    optional string node_label = 3 [db_index = False, max_length = 30, null = True, content_type = "stripped", blank = True];
+    required string docker_image_name = 4 [default = "docker.io/xosproject/vsg", max_length = 255, content_type = "stripped", blank = False, null = False, db_index = False];
+    required bool docker_insecure_registry = 5 [default = False, null = False, db_index = False, blank = True];
+}
+
+
+message VEGTenant (TenantWithContainer){
+    option verbose_name = "vEG Tenant";
+    optional string last_ansible_hash = 1 [db_index = False, max_length = 128, null = True, content_type = "stripped", blank = True];
+}
diff --git a/xos/synchronizer/steps/test_sync_vegtenant.py b/xos/synchronizer/steps/test_sync_vegtenant.py
index 05d83f6..aca3d97 100644
--- a/xos/synchronizer/steps/test_sync_vegtenant.py
+++ b/xos/synchronizer/steps/test_sync_vegtenant.py
@@ -27,6 +27,18 @@
     xos_dir=os.path.join(test_path, "../../../../../../orchestration/xos/xos")
     services_dir=os.path.join(xos_dir, "../../xos_services")
 
+# While transitioning from static to dynamic load, the path to find neighboring xproto files has changed. So check
+# both possible locations...
+def get_models_fn(service_name, xproto_name):
+    name = os.path.join(service_name, "xos", xproto_name)
+    if os.path.exists(os.path.join(services_dir, name)):
+        return name
+    else:
+        name = os.path.join(service_name, "xos", "synchronizer", "models", xproto_name)
+        if os.path.exists(os.path.join(services_dir, name)):
+            return name
+    raise Exception("Unable to find service=%s xproto=%s" % (service_name, xproto_name))
+
 class TestSyncVEGServiceInstance(unittest.TestCase):
     def setUp(self):
         global SyncVEGTenant, LeastLoadedNodeScheduler, MockObjectList
@@ -41,7 +53,8 @@
         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, ["vEG/xos/veg.xproto", "addressmanager/xos/addressmanager.xproto"])
+        build_mock_modelaccessor(xos_dir, services_dir, [get_models_fn("vEG", "veg.xproto"),
+                                                         get_models_fn("addressmanager", "addressmanager.xproto")])
 
         import synchronizers.new_base.modelaccessor
         import synchronizers.new_base.model_policies.model_policy_tenantwithcontainer
diff --git a/xos/synchronizer/veg_config.yaml b/xos/synchronizer/veg_config.yaml
index af18596..0393ecf 100644
--- a/xos/synchronizer/veg_config.yaml
+++ b/xos/synchronizer/veg_config.yaml
@@ -12,13 +12,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-name: veg-synchronizer
+name: veg
 accessor:
   username: xosadmin@opencord.org
   password: "@/opt/xos/services/veg/credentials/xosadmin@opencord.org"
+required_models:
+  - VEGService
+  - VEGTenant
 dependency_graph: "/opt/xos/synchronizers/veg/model-deps"
 steps_dir: "/opt/xos/synchronizers/veg/steps"
 sys_dir: "/opt/xos/synchronizers/veg/sys"
+models_dir: "/opt/xos/synchronizers/veg/models"
 model_policies_dir: "/opt/xos/synchronizers/veg/model_policies"
 wrappers:
   - wrappers.veeserviceinstance