- Explicit annotations of models with dependency information
- New tool (mldeps) for using this explicitly annotaed dependency
  information

Change-Id: I75de06b65f9656ca17956885bc99df61f4106d3c
diff --git a/xos/core/models/__init__.py b/xos/core/models/__init__.py
index d889a26..3630d9d 100644
--- a/xos/core/models/__init__.py
+++ b/xos/core/models/__init__.py
@@ -1,4 +1,4 @@
-from .plcorebase import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager,PlModelMixIn
+from .plcorebase import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager,PlModelMixIn,ModelLink
 from .project import Project
 from .singletonmodel import SingletonModel
 from .xosmodel import XOS, XOSVolume
diff --git a/xos/core/models/billing.py b/xos/core/models/billing.py
index 48c8a38..083149a 100644
--- a/xos/core/models/billing.py
+++ b/xos/core/models/billing.py
@@ -2,7 +2,7 @@
 import os
 import socket
 from django.db import models
-from core.models import PlCoreBase, Site, Slice, Instance, Deployment
+from core.models import PlCoreBase, Site, Slice, Instance, Deployment, ModelLink
 from core.models.plcorebase import StrippedCharField
 from django.contrib.contenttypes.models import ContentType
 from django.db.models import Sum
@@ -11,6 +11,8 @@
 class Account(PlCoreBase):
     site = models.ForeignKey(Site, related_name="accounts", help_text="Site for this account")
 
+    xos_links = [ModelLink(Site,via='site')]
+
     @property
     def total_invoices(self):
         # Since the amount of an invoice is the sum of it's charges, we can
@@ -38,6 +40,8 @@
     date = models.DateTimeField()
     account = models.ForeignKey(Account, related_name="invoices")
 
+    xos_links = [ModelLink(Account,via='account')]
+
     @property
     def amount(self):
         return str(self.charges.all().aggregate(Sum('amount'))["amount__sum"])
@@ -54,6 +58,8 @@
     amount = models.FloatField(default=0.0)
     date = models.DateTimeField(default=timezone.now)
 
+    xos_links = [ModelLink(Account, via='account')] 
+
     def __unicode__(self): return u'%s-%0.2f-%s' % (self.account.site.name, self.amount, str(self.date))
 
 class Charge(PlCoreBase):
@@ -69,6 +75,7 @@
     amount = models.FloatField(default=0.0)
     coreHours = models.FloatField(default=0.0)
     invoice = models.ForeignKey(Invoice, blank=True, null=True, related_name="charges")
+    xos_links = [ModelLink(Account,via='account'),ModelLink(Slice,via='slice'),ModelLink(Invoice,via='invoice')]
 
     def __unicode__(self):  return u'%s-%0.2f-%s' % (self.account.site.name, self.amount, str(self.date))
 
diff --git a/xos/core/models/controlleruser.py b/xos/core/models/controlleruser.py
index 1031e12..0e05cfa 100644
--- a/xos/core/models/controlleruser.py
+++ b/xos/core/models/controlleruser.py
@@ -3,7 +3,9 @@
 from collections import defaultdict
 from django.db import models
 from django.db.models import F, Q
-from core.models import PlCoreBase,User,Controller
+from core.models import PlCoreBase,User,Controller,ModelLink
+from core.models.site import SitePrivilege
+from core.models.slice import SlicePrivilege
 from core.models.plcorebase import StrippedCharField
 from core.models import Controller,ControllerLinkManager,ControllerLinkDeletionManager
 
@@ -15,6 +17,8 @@
     controller = models.ForeignKey(Controller,related_name='controllersusers')
     kuser_id = StrippedCharField(null=True, blank=True, max_length=200, help_text="Keystone user id")
 
+    xos_links = [ModelLink(Controller,via='controller'), ModelLink(User,via='user')]
+
 
     class Meta:
         unique_together = ('user', 'controller')
@@ -41,6 +45,7 @@
     controller = models.ForeignKey('Controller', related_name='controllersiteprivileges')
     site_privilege = models.ForeignKey('SitePrivilege', related_name='controllersiteprivileges')
     role_id = StrippedCharField(null=True, blank=True, max_length=200, db_index=True, help_text="Keystone id")
+    xos_links = [ModelLink(Controller,via='controller'), ModelLink(SitePrivilege,via='site_privilege')]
 
     class Meta:
         unique_together = ('controller', 'site_privilege', 'role_id')
@@ -75,6 +80,7 @@
     controller = models.ForeignKey('Controller', related_name='controllersliceprivileges')
     slice_privilege = models.ForeignKey('SlicePrivilege', related_name='controllersliceprivileges')
     role_id = StrippedCharField(null=True, blank=True, max_length=200, db_index=True, help_text="Keystone id")
+    xos_links = [ModelLink(Controller,via='controller'), ModelLink(SlicePrivilege,via='slice_privilege')]
 
 
     class Meta:
diff --git a/xos/core/models/credential.py b/xos/core/models/credential.py
index ba58360..166b546 100644
--- a/xos/core/models/credential.py
+++ b/xos/core/models/credential.py
@@ -1,6 +1,6 @@
 import os
 from django.db import models
-from core.models import PlCoreBase
+from core.models import PlCoreBase,ModelLink
 from core.models import User,Site,Slice,Controller
 from core.models.plcorebase import StrippedCharField
 from encrypted_fields import EncryptedCharField
@@ -13,6 +13,8 @@
     key_id = StrippedCharField(help_text="The backend id of this credential", max_length=1024)
     enc_value = EncryptedCharField(help_text="The key value of this credential", max_length=1024)
 
+    xos_links = [ModelLink(User,via='user')]
+
 
     def __unicode__(self):
         return self.name
@@ -24,6 +26,7 @@
     key_id = StrippedCharField(help_text="The backend id of this credential", max_length=1024)
     enc_value = EncryptedCharField(help_text="The key value of this credential", max_length=1024)
 
+    xos_links = [ModelLink(Site,via='site')]
 
     def __unicode__(self):
         return self.name
@@ -36,6 +39,9 @@
     enc_value = EncryptedCharField(help_text="The key value of this credential", max_length=1024)
 
 
+    xos_links = [ModelLink(Slice,via='slice')]
+
+
     def __unicode__(self):
         return self.name
 
@@ -48,6 +54,9 @@
     key_id = models.CharField(help_text="The backend id of this credential", max_length=1024)
     enc_value = EncryptedCharField(help_text="The key value of this credential", max_length=1024)
 
+    
+    xos_links = [ModelLink(Controller,via='controller')]
+
 
     def __unicode__(self):
         return self.name
diff --git a/xos/core/models/dashboard.py b/xos/core/models/dashboard.py
index 9b80ba8..52dc306 100644
--- a/xos/core/models/dashboard.py
+++ b/xos/core/models/dashboard.py
@@ -1,7 +1,7 @@
 import os
 from django.db import models
 from core.models import PlCoreBase, Controller, Deployment
-from core.models.plcorebase import StrippedCharField
+from core.models.plcorebase import StrippedCharField, ModelLink
 from core.models.site import ControllerLinkManager, ControllerLinkDeletionManager
 
 class DashboardView(PlCoreBase):
@@ -13,6 +13,7 @@
     icon_active = models.CharField(max_length=200, default="default-icon-active.png", help_text="Icon for active Dashboard")
     deployments = models.ManyToManyField(Deployment, blank=True, null=True, related_name="dashboardviews", help_text="Deployments that should be included in this view")
 
+
     def __unicode__(self):  return u'%s' % (self.name)
 
 class ControllerDashboardView(PlCoreBase):
@@ -22,6 +23,7 @@
     dashboardView = models.ForeignKey(DashboardView, related_name='controllerdashboardviews')
     enabled = models.BooleanField(default=True)
     url = StrippedCharField(max_length=1024, help_text="URL of Dashboard")
+    xos_links=[ModelLink(Controller,via='controller'),ModelLink(DashboardView,via='dashboardview')]
 
 
 
diff --git a/xos/core/models/image.py b/xos/core/models/image.py
index 69d0f03..8f48458 100644
--- a/xos/core/models/image.py
+++ b/xos/core/models/image.py
@@ -1,7 +1,7 @@
 import os
 from django.db import models
 from core.models import PlCoreBase
-from core.models.plcorebase import StrippedCharField
+from core.models.plcorebase import StrippedCharField,ModelLink
 from core.models import Deployment, DeploymentPrivilege, Controller,ControllerLinkManager,ControllerLinkDeletionManager
 
 # Create your models here.
@@ -23,6 +23,7 @@
 class ImageDeployments(PlCoreBase):
     image = models.ForeignKey(Image,related_name='imagedeployments')
     deployment = models.ForeignKey(Deployment,related_name='imagedeployments')
+    xos_links = [ModelLink(Image,'image'),ModelLink(Deployment,'deployment')]
 
     class Meta:
         unique_together = ('image', 'deployment')
@@ -38,6 +39,7 @@
     image = models.ForeignKey(Image,related_name='controllerimages')
     controller = models.ForeignKey(Controller,related_name='controllerimages')
     glance_image_id = StrippedCharField(null=True, blank=True, max_length=200, help_text="Glance image id") 
+    xos_links = [ModelLink(Image,'image'),ModelLink(Controller,'controller')] 
    
     class Meta:
         unique_together = ('image', 'controller')
diff --git a/xos/core/models/instance.py b/xos/core/models/instance.py
index 6e73af8..b8a9f11 100644
--- a/xos/core/models/instance.py
+++ b/xos/core/models/instance.py
@@ -2,7 +2,7 @@
 from django.db import models
 from django.db.models import Q
 from django.core import exceptions
-from core.models import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager
+from core.models import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager,ModelLink
 from core.models.plcorebase import StrippedCharField
 from core.models import Image
 from core.models import Slice, SlicePrivilege
@@ -101,6 +101,8 @@
     volumes = models.TextField(null=True, blank=True, help_text="Comma-separated list of directories to expose to parent context")
     parent = models.ForeignKey("Instance", null=True, blank=True, help_text="Parent Instance for containers nested inside of VMs")
 
+    xos_links = [ModelLink(dest=Slice,via='slice'),ModelLink(dest=Image,via='image')]
+
     def get_controller (self):
         return self.node.site_deployment.controller
 
diff --git a/xos/core/models/network.py b/xos/core/models/network.py
index d53017f..a2b61c2 100644
--- a/xos/core/models/network.py
+++ b/xos/core/models/network.py
@@ -2,7 +2,7 @@
 import socket
 import sys
 from django.db import models, transaction
-from core.models import PlCoreBase, Site, Slice, Instance, Controller, Service
+from core.models import PlCoreBase, Site, Slice, Instance, Controller, Service, ModelLink
 from core.models import ControllerLinkManager,ControllerLinkDeletionManager
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes.fields import GenericForeignKey
@@ -214,6 +214,7 @@
     subnet_id = models.CharField(null=True, blank=True, max_length=256, help_text="Neutron subnet id") # feedback state
     gateway = models.CharField(max_length=32, blank=True, null=True) # feedback state
     segmentation_id = models.CharField(max_length=32, blank=True, null=True) # feedback state
+    xos_links = [ModelLink(Controller,via='controller'),ModelLink(Network,via='network')]
 
     class Meta:
         unique_together = ('network', 'controller')
@@ -244,6 +245,8 @@
     network = models.ForeignKey(Network,related_name='networkslices')
     slice = models.ForeignKey(Slice,related_name='networkslices')
 
+    xos_links = [ModelLink(Network,via='network'),ModelLink(Slice,via='slice')]
+
     class Meta:
         unique_together = ('network', 'slice')
 
@@ -281,6 +284,8 @@
     mac = models.CharField(null=True, blank=True, max_length=256, help_text="MAC address associated with this port")
     xos_created = models.BooleanField(default=False) # True if XOS created this port in Neutron, False if port created by Neutron and observed by XOS
 
+    xos_links = [ModelLink(Network,via='network'), ModelLink('Instance',via='instance')]
+
     class Meta:
         unique_together = ('network', 'instance')
 
diff --git a/xos/core/models/node.py b/xos/core/models/node.py
index cefb48d..5cfc9b8 100644
--- a/xos/core/models/node.py
+++ b/xos/core/models/node.py
@@ -1,6 +1,6 @@
 import os
 from django.db import models
-from core.models import PlCoreBase
+from core.models import PlCoreBase,ModelLink
 from core.models.plcorebase import StrippedCharField
 from core.models.site import Site, SiteDeployment, SitePrivilege
 from core.models import Tag
@@ -13,6 +13,8 @@
     site_deployment = models.ForeignKey(SiteDeployment, related_name='nodes')
     site = models.ForeignKey(Site, null=True, blank=True, related_name='nodes')
     tags = GenericRelation(Tag)
+    xos_links = [ModelLink(Site,'site')]
+
 
     def __unicode__(self):  return u'%s' % (self.name)
 
diff --git a/xos/core/models/plcorebase.py b/xos/core/models/plcorebase.py
index e2f4b48..891356d 100644
--- a/xos/core/models/plcorebase.py
+++ b/xos/core/models/plcorebase.py
@@ -312,3 +312,10 @@
             d = {}
 
         return d
+
+class ModelLink:
+    def __init__(self,dest,via,into=None):
+        self.dest=dest
+        self.via=via
+        self.into=into
+
diff --git a/xos/core/models/reservation.py b/xos/core/models/reservation.py
index ecf207c..6b6e7a1 100644
--- a/xos/core/models/reservation.py
+++ b/xos/core/models/reservation.py
@@ -1,7 +1,7 @@
 import os
 import datetime
 from django.db import models
-from core.models import PlCoreBase
+from core.models import PlCoreBase, ModelLink
 from core.models import Instance
 from core.models import Slice
 from core.models import ServiceResource
@@ -13,6 +13,8 @@
     slice = models.ForeignKey(Slice, related_name="reservations")
     duration = models.IntegerField(default=1)
 
+    xos_links = [ModelLink(Slice,via='slice')]
+
     def __unicode__(self):  return u'%s to %s' % (self.startTime, self.endTime)
 
     @property
@@ -36,6 +38,8 @@
     resource = models.ForeignKey(ServiceResource, related_name="reservedresources")
     quantity = models.IntegerField(default=1)
     reservationSet = models.ForeignKey(Reservation, related_name="reservedresources")
+    
+    xos_links = [ModelLink(Instance,'instance'),ModelLink(ServiceResource,'resource')]
 
     class Meta(PlCoreBase.Meta):
        verbose_name_plural = "Reserved Resources"
diff --git a/xos/core/models/site.py b/xos/core/models/site.py
index e0d2fc5..069fec9 100644
--- a/xos/core/models/site.py
+++ b/xos/core/models/site.py
@@ -4,7 +4,7 @@
 from django.contrib.contenttypes.fields import GenericRelation
 from django.core.exceptions import PermissionDenied
 # from geoposition.fields import GeopositionField
-from core.models import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager
+from core.models import PlCoreBase,PlCoreBaseManager,PlCoreBaseDeletionManager,ModelLink
 from core.models import Tag
 from core.models.plcorebase import StrippedCharField
 from core.acl import AccessControlList
@@ -112,6 +112,7 @@
     deployments = models.ManyToManyField('Deployment', through='SiteDeployment', blank=True, help_text="Select which sites are allowed to host nodes in this deployment", related_name='sites')
     tags = GenericRelation(Tag)
 
+
     def __unicode__(self):  return u'%s' % (self.name)
 
     def can_update(self, user):
@@ -130,6 +131,8 @@
     site = models.ForeignKey('Site', related_name='siteprivileges')
     role = models.ForeignKey('SiteRole',related_name='siteprivileges')
 
+    xos_links = [ModelLink(Site,'site'),ModelLink('User','user'),ModelLink('Role','role')]
+
     def __unicode__(self):  return u'%s %s %s' % (self.site, self.user, self.role)
 
     def save(self, *args, **kwds):
@@ -224,6 +227,9 @@
     user = models.ForeignKey('User', related_name='deploymentprivileges')
     deployment = models.ForeignKey('Deployment', related_name='deploymentprivileges')
     role = models.ForeignKey('DeploymentRole',related_name='deploymentprivileges')
+
+    xos_links = [ModelLink(Deployment,'deployment'),ModelLink('User','user'),ModelLink('Role','role')]
+
     class Meta:
         unique_together = ('user', 'deployment', 'role')
 
@@ -268,6 +274,8 @@
     rabbit_password = StrippedCharField(max_length=200, null=True, blank=True, help_text="Password of rabbitmq server at this controller")
     deployment = models.ForeignKey(Deployment,related_name='controllerdeployments')
 
+    xos_links = [ModelLink(Deployment, via='deployment')]
+
     def __init__(self, *args, **kwargs):
         super(Controller, self).__init__(*args, **kwargs)
         self.no_sync=True
@@ -300,6 +308,8 @@
     controller = models.ForeignKey(Controller, null=True, blank=True, related_name='sitedeployments')
     availability_zone = StrippedCharField(max_length=200, null=True, blank=True, help_text="OpenStack availability zone")
 
+    xos_links = [ModelLink(Site,'site'),ModelLink(Deployment,'deployment'),ModelLink(Controller,'controller')]
+
     class Meta:
         unique_together = ('site', 'deployment', 'controller')
 
@@ -311,6 +321,8 @@
     controller = models.ForeignKey(Controller, null=True, blank=True, related_name='controllersite')
     tenant_id = StrippedCharField(null=True, blank=True, max_length=200, db_index=True, help_text="Keystone tenant id")
 
+    xos_links = [ModelLink(Controller,via='controller'),ModelLink(Site,via='site')]
+
     def delete(self, *args, **kwds):
         super(ControllerSite, self).delete(*args, **kwds)
 
diff --git a/xos/core/models/slice.py b/xos/core/models/slice.py
index 33e5521..183241f 100644
--- a/xos/core/models/slice.py
+++ b/xos/core/models/slice.py
@@ -1,6 +1,6 @@
 import os
 from django.db import models
-from core.models import PlCoreBase
+from core.models import PlCoreBase,ModelLink
 from core.models import Site
 from core.models.site import SitePrivilege
 from core.models import User
@@ -45,6 +45,7 @@
     mount_data_sets = StrippedCharField(default="GenBank",null=True, blank=True, max_length=256)
 
     default_isolation = models.CharField(null=False, blank=False, max_length=30, choices=ISOLATION_CHOICES, default="vm")
+    xos_links = [ModelLink(Site,'site'),ModelLink(User,'user')]
 
     def __unicode__(self):  return u'%s' % (self.name)
 
@@ -136,6 +137,8 @@
     slice = models.ForeignKey('Slice', related_name='sliceprivileges')
     role = models.ForeignKey('SliceRole',related_name='sliceprivileges')
 
+    xos_links=[ModelLink(User,via='user'),ModelLink(Slice,via='slice'),ModelLink(Role,via='role')]
+
     class Meta:
         unique_together = ('user', 'slice', 'role')
 
@@ -176,6 +179,8 @@
     controller = models.ForeignKey(Controller, related_name='controllerslices')
     slice = models.ForeignKey(Slice, related_name='controllerslices')
     tenant_id = StrippedCharField(null=True, blank=True, max_length=200, help_text="Keystone tenant id")
+    
+    xos_links = [ModelLink(Controller,via='controller'),ModelLink(Slice,via='slice')]
 
     class Meta:
         unique_together = ('controller', 'slice')
diff --git a/xos/core/models/slicetag.py b/xos/core/models/slicetag.py
index a335ce0..7c0b57a 100644
--- a/xos/core/models/slicetag.py
+++ b/xos/core/models/slicetag.py
@@ -1,6 +1,6 @@
 import os
 from django.db import models
-from core.models import PlCoreBase
+from core.models import PlCoreBase,ModelLink
 from core.models import Slice
 from core.models.plcorebase import StrippedCharField
 
@@ -11,6 +11,8 @@
     name = StrippedCharField(help_text="The name of this tag", max_length=30, choices=NAME_CHOICES)
     value = StrippedCharField(help_text="The value of this tag", max_length=1024)
 
+    xos_links = [ModelLink(Slice,via='slice')]
+
     def can_update(self, user):
         return user.can_update_slice(self.slice)
 
diff --git a/xos/core/models/user.py b/xos/core/models/user.py
index aa00d44..a081afd 100644
--- a/xos/core/models/user.py
+++ b/xos/core/models/user.py
@@ -6,7 +6,7 @@
 from operator import attrgetter, itemgetter
 
 from core.middleware import get_request
-from core.models import DashboardView, PlCoreBase, PlModelMixIn, Site
+from core.models import DashboardView, PlCoreBase, PlModelMixIn, Site, ModelLink
 from core.models.plcorebase import StrippedCharField
 from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
 from django.core.exceptions import PermissionDenied
@@ -147,6 +147,8 @@
     no_sync = models.BooleanField(default=False)     # prevent object sync
     no_policy = models.BooleanField(default=False)   # prevent model_policy run
 
+    #xos_links = [ModelLink(Site,via='site')]
+
     timezone = TimeZoneField()
 
     dashboards = models.ManyToManyField(
@@ -537,3 +539,5 @@
     dashboardView = models.ForeignKey(
         DashboardView, related_name='userdashboardviews')
     order = models.IntegerField(default=0)
+
+    xos_links = [ModelLink(User, via='user'), ModelLink(DashboardView,via='userdashboardviews')]
diff --git a/xos/tools/mldeps b/xos/tools/mldeps
new file mode 100755
index 0000000..c4dc68a
--- /dev/null
+++ b/xos/tools/mldeps
@@ -0,0 +1,132 @@
+#!/usr/bin/python
+
+import os
+import pdb
+import sys
+import json
+
+sys.path.append('.')
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xos.settings")
+
+from django.db.models.fields.related import ForeignKey
+
+# try to make sure we're running from the right place
+if (not os.path.exists("core")):
+    if (os.path.exists("../core")):
+        os.chdir("..")
+    else:
+        print >> sys.stderr, "Are you sure you're running dmdot from the root of an XOS installation"
+        sys.exit(-1)
+
+from core.models import ModelLink
+
+# defaults
+apps = ["core"]#, "services.hpc", "services.requestrouter", "services.onos"]
+output = "-json"
+
+# syntax: mldeps [-json | -dot] [app_name]
+
+# poor man's argument parser
+for arg in sys.argv[1:]:
+    if arg.startswith("-"):
+        output = arg
+    else:
+        apps+= [arg]
+
+model_classes = []
+class_names = []
+lower_class_names = {}
+synonyms = {
+        'user':'creator'
+}
+
+for app in apps:
+    app = app + ".models"
+    #models_module = imp.load_source(app, ".")
+    models_module = __import__(app)
+    for part in app.split(".")[1:]:
+        if hasattr(models_module, "PlCoreBase"):
+            break
+        models_module = getattr(models_module,part)
+
+    PlCoreBase = getattr(models_module,"PlCoreBase")
+
+    for classname in dir(models_module):
+        c = getattr(models_module, classname, None)
+        if type(c)==type(PlCoreBase):
+            model_classes.append(c)
+            class_names.append(c.__name__)
+            lower_class_names[c.__name__.lower()] = c
+            try:
+                synonym = synonyms[c.__name__.lower()]
+                lower_class_names[synonym] = c
+            except:
+                pass
+
+# django doesn't use the correct case in field.name.title() for objects that
+# have CamelCased class names. So, compare everything in lower case.
+
+if (output=='-dot'):
+    print "digraph plstack {";
+    for c in model_classes:
+        fields = c._meta.fields
+
+        for f in fields:
+            if type(f)==ForeignKey and f.name.lower().split('_') in lower_class_names:
+                linked_class = lower_class_names[f.name.lower()]
+                if ('backref' in f.name):
+                    print '\t"%s"->"%s";'%(linked_class.__name__,c.__name__)
+                else:
+                    print '\t"%s"->"%s";'%(c.__name__,linked_class.__name__)
+    print "}\n";
+elif (output=='-json'):
+    d = {}
+    tl = {}
+    for c in model_classes:
+        fields = c._meta.fields
+        try:
+            exp_links = c.xos_links
+        except AttributeError:
+            exp_links = []
+        for f in fields:
+            field_type = f.name.lower().split('_')[0]
+            if type(f)==ForeignKey and field_type in lower_class_names:
+                linked_class = lower_class_names[field_type]
+
+
+                if ('backref' in f.name.lower()):
+                    a = linked_class.__name__
+                    b = c.__name__
+                else:
+                    b = linked_class.__name__
+                    a = c.__name__
+
+                try:
+
+                    if (b not in d[a]):
+                        d[a].append(b)
+                except KeyError:
+                    d[c.__name__]=[linked_class.__name__]
+
+        for l in exp_links:
+            linked_class = l.dest
+            if (type(linked_class)==str):
+                linked_class = lower_class_names[linked_class.lower()]
+            via = l.via
+            if ('backref' in via):
+                a = linked_class.__name__
+                b = c.__name__
+            else:
+                b = linked_class.__name__
+                a = c.__name__
+
+            try:
+                if (b not in tl[a]):
+                    tl[a].append(b)
+            except KeyError:
+                tl[c.__name__]=[linked_class.__name__]
+
+
+    #d['ControllerNetwork'].append('SliceDeployments')
+    print json.dumps(tl,indent=4)