CORD-1923: Eliminate hardcoded dependencies between VTR and VSG

Change-Id: I25017fa8f03e3ca9a160b6ff7d5074ce9320ac8a
diff --git a/xos/synchronizer/steps/sync_vtrtenant.py b/xos/synchronizer/steps/sync_vtrtenant.py
index e3ff7af..c3f7708 100644
--- a/xos/synchronizer/steps/sync_vtrtenant.py
+++ b/xos/synchronizer/steps/sync_vtrtenant.py
@@ -60,26 +60,39 @@
                     return csrs[0]
         return None
 
-    def get_vcpe_service(self, o):
-        target = self.get_target(o)
-        if target and target.volt and target.volt.vcpe:
-            return target.volt.vcpe
-        return None
+    def gather_information(self, service_instance):
+        """ gather_information: inspect a service chain for information that will be useful to the VTN service, and
+            try to do it in a service-agnostic way. We know what we're looking for (instances, ip addresses, etc) but
+            not necessarily where we will find it.
+        """
+
+        if not service_instance:
+            return {}
+
+        # extract useful information from the service_instance
+        info = {}
+        for link in service_instance.subscribed_links.all():
+            provider_si = link.provider_service_instance.leaf_model
+            for k in ["instance", "wan_vm_ip", "wan_container_ip", "s_tag", "c_tag", "container_name"]:
+                if hasattr(provider_si, k):
+                    info[k] = getattr(provider_si, k)
+
+        # now, recurse to check the children
+        for link in service_instance.subscribed_links.all():
+            child_info = self.gather_information(link.provider_service_instance)
+
+            # prefer values we got from a parent to values we got from a child
+            for (k,v) in child_info.items():
+                if not k in info:
+                    info[k] = v
+
+        return info
 
     def get_instance(self, o):
-        target = self.get_target(o)
-        if target and target.volt and target.volt.vcpe:
-            return target.volt.vcpe.instance
-        else:
-            return None
+        """ get_instance: Called by the SyncInstanceUsingAnslbe sync step. """
+        return self.gather_information(self.get_target(o)).get("instance")
 
     def get_key_name(self, instance):
-#        if instance.slice.service and (instance.slice.service.kind==VCPE_KIND):
-#            # We need to use the vsg service's private key. Onboarding won't
-#            # by default give us another service's private key, so let's assume
-#            # onboarding has been configured to add vsg_rsa to the vtr service.
-#            return "/opt/xos/services/vtr/keys/vsg_rsa"
-
         if instance.slice and instance.slice.service and instance.slice.service.private_key_fn:
             # Assume the service has shared its key with VTR.
             # Look for the instance's service key name in VTR's key directory.
@@ -89,47 +102,36 @@
             raise Exception("VTR doesn't know how to get the private key for this instance")
 
     def get_extra_attributes(self, o):
-        vtr_service = self.get_vtr_service(o)
-        vcpe_service = self.get_vcpe_service(o)
+        target = self.get_target(o)
+        target_info = self.gather_information(target)
 
-        if not vcpe_service:
-            raise Exception("No vcpeservice")
-
-        instance = self.get_instance(o)
-
+        instance = target_info.get("instance")
         if not instance:
             raise Exception("No instance")
 
-        target = self.get_target(o)
+        # For container scope, we need to figure out the container name. There are three ways we can do this:
+        #    1) The service_instance can provide a `container_name` attribute
+        #    2) The service_instance can provide `container_prefix`, `s_tag`, and `c_tag` attributes.
+        #    3) The service_instance can provide `s_tag` and `c_tag` and we'll assume a default prefix of `vsg`
+        container_name = target_info.get("container_name")
+        if not container_name:
+            if (not target_info.get("s_tag")) or (not target_info.get("c_tag")):
+                raise Exception("No s_tag or no c_tag")
 
-        s_tags = []
-        c_tags = []
-        if target and target.volt:
-            s_tags.append(target.volt.s_tag)
-            c_tags.append(target.volt.c_tag)
+            container_name = "%s-%s-%s" % (target_info.get("container_prefix", "vsg"), target_info["s_tag"], target_info["c_tag"])
 
-        fields = {"s_tags": s_tags,
-                "c_tags": c_tags,
-                "isolation": instance.isolation,
-                "container_name": "vcpe-%s-%s" % (s_tags[0], c_tags[0]),
-#                "dns_servers": [x.strip() for x in vcpe_service.dns_servers.split(",")],
-                "result_fn": "%s-vcpe-%s-%s" % (o.test, s_tags[0], c_tags[0]),
-                "resultcode_fn": "code-%s-vcpe-%s-%s" % (o.test, s_tags[0], c_tags[0]) }
+        fields = {"isolation": instance.isolation,
+                  "container_name": container_name,
+                  "result_fn": "%s-vtrserviceinstance-%s" % (o.test, str(o.id)),
+                  "resultcode_fn": "code-%s-vtrserviceinstance-%s" % (o.test, str(o.id)) }
 
-        # add in the sync_attributes that come from the vSG object
-        # this will be wan_ip, wan_mac, wan_container_ip, wan_container_mac, ...
-        if target and target.volt and target.volt.vcpe:
-            for attribute_name in ["wan_vm_ip", "wan_container_ip"]:
-                if hasattr(target.volt.vcpe, attribute_name):
-                    fields[attribute_name] = getattr(target.volt.vcpe, attribute_name)
+        # copy in values that we learned from inspecting the service chain
+        for k in ["s_tag", "c_tag", "wan_vm_ip", "wan_container_ip"]:
+            if target_info.get(k):
+                fields[k] = target_info[k]
 
-        # add in the sync_attributes that come from the SubscriberRoot object
-#        if target and hasattr(target, "sync_attributes"):
-#            for attribute_name in target.sync_attributes:
-#                fields[attribute_name] = getattr(target, attribute_name)
-
-        for attribute_name in ["scope", "test", "argument"]: # o.sync_attributes:
-            fields[attribute_name] = getattr(o,attribute_name)
+        for attribute_name in ["scope", "test", "argument"]: 
+            fields[attribute_name] = getattr(o, attribute_name)
 
         return fields