Refactor model policy

Change-Id: I4e7b9c0ba6e081e5c7f4af44f5aec94df57a3eed
diff --git a/xos/synchronizer/model_policies/model_policy_vepcserviceinstance.py b/xos/synchronizer/model_policies/model_policy_vepcserviceinstance.py
index aa15f90..3344a2d 100644
--- a/xos/synchronizer/model_policies/model_policy_vepcserviceinstance.py
+++ b/xos/synchronizer/model_policies/model_policy_vepcserviceinstance.py
@@ -20,248 +20,248 @@
 from multistructlog import create_logger
 
 log = create_logger(Config().get('logging'))
-
 blueprints = Config().get('blueprints')
 
 
-def service_of_service_instance(si):
-    if si.endswith('Tenant'):
-        return si[:-len('Tenant')] + 'Service'
-    elif si.endswith('ServiceInstance'):
-        return si[:-len('ServiceInstance')] + 'Service'
-    else:
-        raise Exception(
-            'Could not translate service instance into service: %s' % si)
-
-
 class VEPCServiceInstancePolicy(Policy):
     model_name = "VEPCServiceInstance"
 
-    def __init__(self):
-        self.in_memory_instances = []
-        self.network_map = {}
+    def get_service_object(self, name):
+        """ return: First Service object """
+        if any(map(lambda x: x in name, ["ServiceInstance", "Tenant"])):
+            name = name.replace("ServiceInstance", "Service")
+            name = name.replace("Tenant", "Service")
 
-        super(VEPCServiceInstancePolicy, self).__init__()
+        service_obj = getattr(Slice().stub, name).objects.first()
 
-    """TODO: Update the following to not be service-specific
-       This code assumes there is only one vendor installed
-    """
+        if not service_obj:
+            raise Exception("No %s object existed." % name)
 
-    def configure_service_instance(self, service_instance):
-        if service_instance.leaf_model_name == 'VSPGWUTenant':
-            vendor = VSPGWUVendor.objects.first()
-            if not vendor:
-                raise Exception('No VSPGWU vendors')
-            service_instance.vspgwu_vendor = vendor
-            service_instance.enodeb_ip_addr = self.obj.enodeb_ip_addr_s1u
-            service_instance.enodeb_mac_addr = self.obj.enodeb_mac_addr_s1u
-            service_instance.appserver_ip_addr = self.obj.appserver_ip_addr
-            service_instance.appserver_mac_addr = self.obj.appserver_mac_addr
-            service_instance.invalidate_cache('vspgwu_vendor')
-        elif service_instance.leaf_model_name == 'VSPGWCTenant':
-            vendor = VSPGWCVendor.objects.first()
-            if not vendor:
-                raise Exception('No VSPGWC vendors')
-            service_instance.vspgwc_vendor = vendor
-            service_instance.enodeb_ip_addr = self.obj.enodeb_ip_addr_s1u
-            service_instance.enodeb_mac_addr = self.obj.enodeb_mac_addr_s1u
-            service_instance.appserver_ip_addr = self.obj.appserver_ip_addr
-            service_instance.appserver_mac_addr = self.obj.appserver_mac_addr
-            service_instance.invalidate_cache('vspgwc_vendor')
-        elif service_instance.leaf_model_name == 'VMMETenant':
-            vendor = VMMEVendor.objects.first()
-            if not vendor:
-                raise Exception('No VMME vendors')
-            service_instance.vmme_vendor = vendor
-            service_instance.enodeb_ip_addr = self.obj.enodeb_ip_addr_s1mme
-            service_instance.invalidate_cache('vmme_vendor')
-        elif service_instance.leaf_model_name == 'VHSSTenant':
-            vendor = VHSSVendor.objects.first()
-            if not vendor:
-                raise Exception('No VHSS vendors')
-            service_instance.vhss_vendor = vendor
-            service_instance.invalidate_cache('vhss_vendor')
-        elif service_instance.leaf_model_name == 'HSSDBServiceInstance':
-            vendor = HSSDBVendor.objects.first()
-            if not vendor:
-                raise Exception('No HSSDB vendors')
-            service_instance.hssdb_vendor = vendor
-            service_instance.invalidate_cache('hssdb_vendor')
-
-    def child_service_instance_from_name(self, name):
-        service_instances = self.obj.child_serviceinstances.all()
-        service_instances.extend(self.in_memory_instances)
-
-        try:
-            service_instance = next(
-                s for s in service_instances if s.leaf_model_name == name)
-        except StopIteration:
-            service_instance = None
-
-        return service_instance
-
-    def get_service_for_service_instance(self, si):
-        service = service_of_service_instance(si)
-        service_class = getattr(Slice().stub, service)
-        service_obj = service_class.objects.first()  # There's only one service object
         return service_obj
 
-    def create_service_instance(self, si, node_label = None):
-        service = self.get_service_for_service_instance(si)
-        if not service:
-            raise Exception('No service object for %s' % service)
+    def get_tenant_class(self, name):
+        """
+        return: Tenant class
+        We need to claim new Tenant instance(object) by this class
+        """
+        if not name.endswith("Service"):
+            raise Exception("Tenant object needs to find with Service name.")
 
-        si_class = getattr(Slice().stub, si)
-        s = si_class(owner=service, name='epc-%s-%d' %
-                     (si.lower(), self.obj.id))
-        s.master_serviceinstance = self.obj
+        if hasattr(Slice().stub, name + "Instance"):
+            tenant_class = getattr(Slice().stub, name + "Instance")
+        elif hasattr(Slice().stub, name.replace("Service", "Tenant")):
+            tenant_class = getattr(Slice().stub, name.replace("Service", "Tenant"))
+        else:
+            raise Exception("No %s class existed." % name)
+
+        return tenant_class
+
+    def get_vendor_object(self, name):
+        postfixs = ["Service", "ServiceInstance", "Tenant"]
+        if not any(map(lambda x: name.endswith(x), postfixs)):
+            raise Exception("Vendor object need to find with Service or Tenant name.")
+
+        for postfix in postfixs:
+            name = name.replace(postfix, "Vendor")
+
+        vendor_obj = getattr(Slice().stub, name).objects.first()
+
+        if not vendor_obj:
+            raise Exception("No %s object existed." % name)
+
+        return vendor_obj
+
+    def create_service_instance(self, service_obj, node_label=None):
+        tenant_class = self.get_tenant_class(service_obj.leaf_model_name)
+        vendor_obj = self.get_vendor_object(service_obj.leaf_model_name)
+
+        vendor_name = "%s_vendor" % service_obj.name.lower()
+        name = "epc-%s-%d" % (tenant_class.__name__.lower(), self.obj.id)
+
+        instance = tenant_class.objects.filter(owner=service_obj.id, name=name).first()
+
+        if instance:
+            return instance
+
+        instance = tenant_class(owner=service_obj, name=name)
+        instance.master_serviceinstance = self.obj
+        instance.__setattr__(vendor_name, vendor_obj)
 
         if node_label:
-            s.node_label = '%s-%d'%(node_label, self.obj.id)
+            instance.node_label = "%s-%d" % (node_label, self.obj.id)
 
-        s.no_sync = True
-        s.no_policy = True
-        s.save()
+        # Assign custom parameter to child tenant
+        if name in ["vspgwc", "vspgwu"]:
+            instance.enodeb_ip_addr = self.obj.enodeb_ip_addr_s1u
+            instance.enodeb_mac_addr = self.obj.enodeb_mac_addr_s1u
+            instance.appserver_ip_addr = self.obj.appserver_ip_addr
+            instance.appserver_mac_addr = self.obj.appserver_mac_addr
+        elif name in ["vmme"]:
+            instance.enodeb_ip_addr = self.obj.enodeb_ip_addr_s1mme
 
-        self.configure_service_instance(s)
-        s.save()
+        instance.no_sync = True
+        instance.no_policy = True
+        instance.invalidate_cache(vendor_name)
 
-        self.in_memory_instances.append(s)
-        return s
+        instance.save()
 
-    def add_networks_to_service(self, service, networks):
-        for n in networks:
-            net = Network.objects.filter(name=n)[0]
-            one_and_only_slice_hopefully = service.slices.all()[0]
-            ns_object = NetworkSlice.objects.filter(
-                network=net.id, slice=one_and_only_slice_hopefully.id)
-            if not ns_object:
-                ns_object = NetworkSlice(
-                    network=net, slice=one_and_only_slice_hopefully)
-                ns_object.save()
-
-    def add_networks_to_service_instance(self, instance, networks):
-        for n in networks:
-            net = Network.objects.filter(name=n)[0]
-            one_and_only_slice_hopefully = instance.owner.slices.all()[0]
-            ns_object = NetworkSlice.objects.filter(
-                network=net.id, slice=one_and_only_slice_hopefully.id)
-            if not ns_object:
-                ns_object = NetworkSlice(
-                    network=net, slice=one_and_only_slice_hopefully)
-                ns_object.save()
-
-    def create_service_instance_with_networks(self, si_name, networks, node_label = None):
-        service = self.get_service_for_service_instance(si_name)
-        self.add_networks_to_service(service, networks)
-
-        instance = self.child_service_instance_from_name(si_name)
-
-        if not instance:
-            instance = self.create_service_instance(si_name, node_label = node_label)
+        log.info("Instance %s was created." % instance)
 
         return instance
 
-    def create_link(self, src_instance, dst_instance):
-        src_service = self.get_service_for_service_instance(
-            src_instance.leaf_model_name)
-        dst_service = self.get_service_for_service_instance(
-            dst_instance.leaf_model_name)
-
-        service_dependency = ServiceDependency.objects.filter(
-            provider_service_id=dst_service.id, subscriber_service_id=src_service.id)
-        if not service_dependency:
-            service_dependency = ServiceDependency(
-                provider_service=dst_service, subscriber_service=src_service)
-            service_dependency.save()
-
-        service_instance_link = ServiceInstanceLink.objects.filter(
-            provider_service_instance_id=dst_instance.id, subscriber_service_instance_id=src_instance.id)
-        if not service_instance_link:
-            service_instance_link = ServiceInstanceLink(
-                provider_service_instance=dst_instance, subscriber_service_instance=src_instance)
-            service_instance_link.save()
-
-    def recursive_create_instances_and_links(self, blueprint, src, service_instances):
-        for node in blueprint:
-            k = node['name']
-            networks = node.get('networks', [])
-            links = node.get('links', [])
-
-            try:
-                node_label = node['node_label']
-            except KeyError:
-                try:
-                    node_label = next(l['node_label'] for l in links if l.get('node_label', None))
-                except StopIteration:
-                    node_label = None
-
-            instance = self.create_service_instance_with_networks(k, networks, node_label = node_label)
-            service_instances.append(instance)
-
-            if src:
-                self.add_networks_to_service_instance(src, networks)
-                self.create_link(src, instance)
-
-            service_instances = self.recursive_create_instances_and_links(links, instance, service_instances)
-
-        return service_instances
-
-    def create_epc_network(self, n):
-        network_name = n['name']
+    def create_network(self, network):
+        name = network.get("name", "")
+        owner = network.get("owner", "")
         site_name = self.obj.site.login_base
+        template_name = network.get("template", "public")
+        subnet = network.get("subnet", "")
+        permit_all_slices = network.get("permit_all_slices", False)
 
-        nets = Network.objects.filter(name=network_name)
-        if not nets:
-            template_name = n.get('template', 'public')
-            try:
-                template = NetworkTemplate.objects.filter(name=template_name)[0]
-            except:
-                raise Exception('Template %s for network %s not found' % (template_name, network_name))
+        # Get Network, If Network subnet mismatch, then update
+        # If Network existed, then early return.
+        network_obj = Network.objects.filter(name=name).first()
+        if network_obj:
+            if network_obj.subnet != subnet:
+                network_obj.subnet = subnet
+                network_obj.save()
+            return network_obj
 
-            slice_name = '%s_%s' % (site_name, n['owner'])
-            try:
-                slice = Slice.objects.filter(name=slice_name)[0]
-            except:
-                raise Exception('Owner slice %s for network %s not found' % (slice_name, network_name))
+        # Get Network Template by assigned name.
+        template = NetworkTemplate.objects.filter(name=template_name).first()
+        if not template:
+            raise Exception("Template %s for network %s is not exist." % (template_name, name))
 
+        # Get Network owner slice by assigned name.
+        slice_name = "%s_%s" % (site_name, owner)
+        owner_slice = Slice.objects.filter(name=slice_name).first()
+        if not owner_slice:
+            raise Exception("Owner Slice %s for network %s is not exist." % (owner, name))
 
-            net = Network(name=network_name, subnet=n['subnet'], permit_all_slices=n.get(
-                'permit_all_slices', False), template=template, owner=slice)
-            net.save()
-        else:
-            net = nets[0]
-            if net.subnet != n['subnet']:
-                net.subnet = n['subnet']
-                net.save()
+        # Create Network Instance and save.
+        network = Network(name=name, subnet=subnet, template=template,
+                          permit_all_slices=permit_all_slices, owner=owner_slice)
+        network.save()
 
-        self.network_map[network_name] = net
+        log.info("Network %s was created." % network)
 
-    def create_networks(self, networks):
-        for n in networks:
-            self.create_epc_network(n)
+        return network
 
-    def create_networks_and_child_services(self, service_instance):
-        self.obj = service_instance
-        # Create service graph based on blueprint
-        chosen_blueprint = service_instance.blueprint
-        try:
-            blueprint = next(
-                b for b in blueprints if b['name'] == chosen_blueprint)
-        except StopIteration:
-            log.error('Chosen blueprint (%s) not found' % chosen_blueprint)
+    def create_service_dependency(self, source, target):
+        """
+        Create Service Dependency object for Connectivity between service
+        source: source service object
+        target: target service object
+        """
 
-        self.create_networks(blueprint['networks'])
+        # Use Subscriber and Provider's ID to get Dependency
+        dependency = ServiceDependency.objects.filter(
+            subscriber_service_id=source.id, provider_service_id=target.id
+        ).first()
 
-        service_instances = self.recursive_create_instances_and_links(blueprint['graph'], None, [])
+        if not dependency:
+            # If no dependency existed, then create dependency
+            dependency = ServiceDependency(
+                subscriber_service=source, provider_service=target
+            )
+            dependency.save()
 
-        for si in service_instances:
-            si.no_policy = False
-            si.no_sync = False
-            si.save()
+            log.info("Service dependency %s was created, %s->%s" % (dependency, source, target))
+
+        # Get subscriber and provider's Service Instance
+        source_tenant_class = self.get_tenant_class(source.leaf_model_name)
+        target_tenant_class = self.get_tenant_class(target.leaf_model_name)
+        source_tenants = source_tenant_class.objects.all()
+        target_tenants = target_tenant_class.objects.all()
+
+        # Use cross product to create ServiceInstance Link
+        for source_tenant in source_tenants:
+            for target_tenant in target_tenants:
+                link = ServiceInstanceLink.objects.filter(
+                    subscriber_service_instance_id=source_tenant.id,
+                    provider_service_instance_id=target_tenant.id
+                )
+
+                if not link:
+                    link = ServiceInstanceLink(
+                        subscriber_service_instance=source_tenant,
+                        provider_service_instance=target_tenant
+                    )
+
+                    link.save()
+
+                    log.info("Service instance link %s was created" % link)
+
+        return dependency
+
+    def assign_network_to_service(self, service, network):
+        service_slice = service.slices.first()
+        network_slice = NetworkSlice.objects.filter(
+            network=network.id, slice=service_slice.id
+        )
+
+        if not network_slice:
+            network_slice = NetworkSlice(
+                network=network, slice=service_slice
+            )
+            network_slice.save()
+
+        return network_slice
+
+    def create_services_from_blueprint(self, blueprint):
+        dependencies = list()
+        instances = list()
+
+        for network in blueprint["networks"]:
+            network_instance = self.create_network(network)
+
+        for node in blueprint["graph"]:
+            # Get Service's Name, Network, Link attribute from blueprint node
+            # If value is not set, return empty data struct instead.
+            tenant_str = node.get("name", "")
+            networks = node.get("networks", list())
+            links = node.get("links", list())
+            node_label = node.get("node_label", None)
+
+            # Get Service Class by Defined Service Instance Name
+            # service_obj: Service Object in XOS core
+            # tenant_obj: Tenant Object in XOS core
+            service_obj = self.get_service_object(tenant_str)
+
+            for network in networks:
+                network_obj = Network.objects.filter(name=network).first()
+                self.assign_network_to_service(service_obj, network_obj)
+
+            instance = self.create_service_instance(service_obj, node_label=node_label)
+            instances.append(instance)
+
+            # Collect Service Dependencies relationship from links
+            for provider in links:
+                provider_str = provider.get("name", "")
+                provider_service_obj = self.get_service_object(provider_str)
+                dependencies.append((service_obj, provider_service_obj))
+
+        log.info("Dependency Pair: %s" % dependencies)
+
+        # Create Service Dependency between subscriber and provider
+        for subscriber, provider in dependencies:
+            self.create_service_dependency(subscriber, provider)
+
+        for instance in instances:
+            instance.no_policy = False
+            instance.no_sync = False
+            instance.save()
 
     def handle_create(self, service_instance):
         self.handle_update(service_instance)
 
     def handle_update(self, service_instance):
-        self.create_networks_and_child_services(service_instance)
+        # Register EPC-Service's service_instance as self.obj
+        self.obj = service_instance
+
+        blueprint_name = service_instance.blueprint
+        try:
+            blueprint = filter(lambda x: x["name"] == blueprint_name, blueprints)[0]
+        except StopIteration:
+            log.error("Chosen blueprint: %s not defined" % blueprint_name)
+
+        self.create_services_from_blueprint(blueprint)