Merge branch 'master' of github.com:open-cloud/xos
diff --git a/external/keystone_user.py b/external/keystone_user.py
index 0139ad4..9290847 100644
--- a/external/keystone_user.py
+++ b/external/keystone_user.py
@@ -220,6 +220,7 @@
         user = get_user(keystone, user_name)
     except KeyError:
         # Tenant doesn't exist yet
+        user = None
         pass
     else:
         # User does exist, check if it's current
diff --git a/planetstack/backend-model-deps b/planetstack/backend-model-deps
new file mode 100644
index 0000000..da9565f
--- /dev/null
+++ b/planetstack/backend-model-deps
@@ -0,0 +1,11 @@
+{
+    "Sliver": [
+	["SlicePrivilege","slice.sliceprivileges"],
+	["SitePrivilege","slice.site.siteprivileges"],
+	["ControllerImages","image.controllerimages"]
+    ] 
+    ,
+    "Network": [
+        ["Slice","network.slices"]
+    ]
+}
diff --git a/planetstack/core/models/controlleruser.py b/planetstack/core/models/controlleruser.py
index 678ab77..b3ba720 100644
--- a/planetstack/core/models/controlleruser.py
+++ b/planetstack/core/models/controlleruser.py
@@ -25,6 +25,9 @@
             qs = ControllerUser.objects.filter(user__in=users)
         return qs
 
+    def can_update(self, user):
+        return user.can_update_root()    
+
 
 class ControllerSitePrivilege(PlCoreBase):
     objects = ControllerLinkManager()
diff --git a/planetstack/core/models/image.py b/planetstack/core/models/image.py
index 0bca22f..8d392a3 100644
--- a/planetstack/core/models/image.py
+++ b/planetstack/core/models/image.py
@@ -1,7 +1,7 @@
 import os
 from django.db import models
 from core.models import PlCoreBase
-from core.models import Deployment,Controller,ControllerLinkManager,ControllerLinkDeletionManager
+from core.models import Deployment, DeploymentPrivilege, Controller,ControllerLinkManager,ControllerLinkDeletionManager
 
 # Create your models here.
 
@@ -20,6 +20,9 @@
 
     def __unicode__(self):  return u'%s %s' % (self.image, self.deployment)
 
+    def can_update(self, user):
+        return user.can_update_deployment(self.deployment)
+
 class ControllerImages(PlCoreBase):
     objects = ControllerLinkManager()
     deleted_objects = ControllerLinkDeletionManager()
@@ -28,5 +31,3 @@
     glance_image_id = models.CharField(null=True, blank=True, max_length=200, help_text="Glance image id") 
 
     def __unicode__(self):  return u'%s %s' % (self.image, self.controller)
-
-    
diff --git a/planetstack/core/models/network.py b/planetstack/core/models/network.py
index c7a97a9..c9ae214 100644
--- a/planetstack/core/models/network.py
+++ b/planetstack/core/models/network.py
@@ -128,7 +128,7 @@
         super(Network, self).save(*args, **kwds)
 
     def can_update(self, user):
-        return self.owner.can_update(user)
+        return user.can_update_slice(self.owner)
 
     @property
     def nat_list(self):
@@ -156,9 +156,6 @@
     subnet_id = models.CharField(null=True, blank=True, max_length=256, help_text="Quantum subnet id")
     subnet = models.CharField(max_length=32, blank=True)
        
-    def can_update(self, user):
-        return user.is_admin
-
     @staticmethod
     def select_by_user(user):
         if user.is_admin:
@@ -190,7 +187,7 @@
     def __unicode__(self):  return u'%s-%s' % (self.network.name, self.slice.name)
 
     def can_update(self, user):
-        return self.slice.can_update(user)
+        return user.can_update_slice(self.slice)
 
     @staticmethod
     def select_by_user(user):
@@ -225,7 +222,7 @@
     def __unicode__(self):  return u'%s-%s' % (self.network.name, self.sliver.instance_name)
 
     def can_update(self, user):
-        return self.sliver.can_update(user)
+        return user.can_update_slice(self.sliver.slice)
 
     @staticmethod
     def select_by_user(user):
@@ -244,6 +241,9 @@
 
     def __unicode__(self):  return u'%s' % (self.name)
 
+    def can_update(self, user):
+        return user.can_update_slice(self.owner)
+
 class NetworkParameterType(PlCoreBase):
     name = models.SlugField(help_text="The name of this parameter", max_length=128)
     description = models.CharField(max_length=1024)
diff --git a/planetstack/core/models/node.py b/planetstack/core/models/node.py
index 1cd0e40..bb4fe24 100644
--- a/planetstack/core/models/node.py
+++ b/planetstack/core/models/node.py
@@ -22,12 +22,4 @@
         super(Node, self).save(*args, **kwds)
 
     def can_update(self, user):
-        if user.is_readonly:
-            return False
-        if user.is_admin:
-            return True
-        if SitePrivilege.objects.filter(
-            user=user, site=self.site, role__role__in=['admin','tech']):
-            return True
-            
-        return False                    
+        return user.can_update_site(self.site, allow=['tech'])
diff --git a/planetstack/core/models/plcorebase.py b/planetstack/core/models/plcorebase.py
index c86d675..97c3f82 100644
--- a/planetstack/core/models/plcorebase.py
+++ b/planetstack/core/models/plcorebase.py
@@ -193,12 +193,7 @@
         self.silent = False
 
     def can_update(self, user):
-        if user.is_readonly:
-            return False
-        if user.is_admin:
-            return True
-
-        return False
+        return user.can_update_root()
 
     def delete(self, *args, **kwds):
         # so we have something to give the observer
diff --git a/planetstack/core/models/reservation.py b/planetstack/core/models/reservation.py
index 4dd1274..1a838a2 100644
--- a/planetstack/core/models/reservation.py
+++ b/planetstack/core/models/reservation.py
@@ -20,7 +20,7 @@
         return self.startTime + datetime.timedelta(hours=self.duration)
 
     def can_update(self, user):
-        return self.slice.can_update(user)
+        return user.can_update_slice(self.slice)
 
     @staticmethod
     def select_by_user(user):
@@ -43,7 +43,7 @@
     def __unicode__(self):  return u'%d %s on %s' % (self.quantity, self.resource, self.sliver)
 
     def can_update(self, user):
-        return self.sliver.slice.can_update(user)
+        return user.can_update(self.sliver.slice)
 
     @staticmethod
     def select_by_user(user):
diff --git a/planetstack/core/models/site.py b/planetstack/core/models/site.py
index f368bbe..b1b4871 100644
--- a/planetstack/core/models/site.py
+++ b/planetstack/core/models/site.py
@@ -111,26 +111,7 @@
     def __unicode__(self):  return u'%s' % (self.name)
 
     def can_update(self, user):
-        if user.is_readonly:
-            return False
-        if user.is_admin:
-            return True
-        site_privs = SitePrivilege.objects.filter(user=user, site=self)
-        for site_priv in site_privs:
-            if site_priv.role.role == 'pi':
-                return True
-        return False 
-
-    @staticmethod
-    def select_by_user(user):
-        if user.is_admin:
-            qs = Site.objects.all()
-        else:
-            site_ids = [sp.site.id for sp in SitePrivilege.objects.filter(user=user)]
-            site_ids.append(user.site.id)
-            qs = Site.objects.filter(id__in=site_ids)
-        return qs
-
+        return user.can_update_site(self, allow=['pi'])
 
 class SiteRole(PlCoreBase):
 
@@ -154,7 +135,7 @@
         super(SitePrivilege, self).delete(*args, **kwds)
 
     def can_update(self, user):
-        return self.site.can_update(user)
+        return user.can_update_site(self, allow=['pi'])
 
     @staticmethod
     def select_by_user(user):
@@ -215,16 +196,8 @@
         return Deployment.objects.filter(id__in=ids)
 
     def can_update(self, user):
-        if user.is_readonly:
-            return False
-        if user.is_admin:
-            return True
-            
-        if self.deploymentprivileges.filter(user=user, role__role='admin'):
-            return True
-          
-        return False    
-          
+        return user.can_update_deploymemt(self)
+    
     def __unicode__(self):  return u'%s' % (self.name)
 
 class DeploymentRole(PlCoreBase):
@@ -246,15 +219,7 @@
     def __unicode__(self):  return u'%s %s %s' % (self.deployment, self.user, self.role)
 
     def can_update(self, user):
-        if user.is_readonly:
-            return False
-        if user.is_admin:
-            return True
-        dprivs = DeploymentPrivilege.objects.filter(user=user)
-        for dpriv in dprivs:
-            if dpriv.role.role == 'admin':
-                return True
-        return False
+        return user.can_update_deploymemt(self)
 
     @staticmethod
     def select_by_user(user):
@@ -289,13 +254,6 @@
 
     def __unicode__(self):  return u'%s %s %s' % (self.name, self.backend_type, self.version)
 
-    def can_update(self, user):
-        if user.is_readonly:
-            return False
-        if user.is_admin:
-            return True
-        return False
-
 class SiteDeployment(PlCoreBase):
     objects = ControllerLinkManager()
     deleted_objects = ControllerLinkDeletionManager()
diff --git a/planetstack/core/models/slice.py b/planetstack/core/models/slice.py
index 476cf8e..d695295 100644
--- a/planetstack/core/models/slice.py
+++ b/planetstack/core/models/slice.py
@@ -81,22 +81,8 @@
         super(Slice, self).save(*args, **kwds)
 
     def can_update(self, user):
-        if user.is_readonly:
-            return False
-        if user.is_admin:
-            return True
-        if user == self.creator:
-            return True    
-        # slice admins can update
-        if SlicePrivilege.objects.filter(
-            user=user, slice=self, role__role='admin'):
-            return True
-        # site pis can update
-        if SitePrivilege.objects.filter(
-            user=user, site=self.site, role__role__in=['admin', 'pi']):
-            return True
- 
-        return False
+        return user.can_update_slice(self)
+
 
     @staticmethod
     def select_by_user(user):
@@ -142,7 +128,7 @@
     def __unicode__(self):  return u'%s %s %s' % (self.slice, self.user, self.role)
 
     def can_update(self, user):
-        return self.slice.can_update(user)
+        return user.can_update_slice(self.slice)
 
     @staticmethod
     def select_by_user(user):
diff --git a/planetstack/core/models/slicetag.py b/planetstack/core/models/slicetag.py
index ea1d026..246e6fd 100644
--- a/planetstack/core/models/slicetag.py
+++ b/planetstack/core/models/slicetag.py
@@ -11,7 +11,7 @@
     value = models.CharField(help_text="The value of this tag", max_length=1024)
 
     def can_update(self, user):
-        return self.slice.can_update(user)
+        return user.can_update_slice(self.slice)
 
     @staticmethod
     def select_by_user(user):
diff --git a/planetstack/core/models/sliver.py b/planetstack/core/models/sliver.py
index 3804dba..62e487b 100644
--- a/planetstack/core/models/sliver.py
+++ b/planetstack/core/models/sliver.py
@@ -120,7 +120,7 @@
         super(Sliver, self).save(*args, **kwds)
 
     def can_update(self, user):
-        return self.slice.can_update(user)
+        return user.can_update_slice(self.slice)
 
     def all_ips(self):
         ips={}
diff --git a/planetstack/core/models/tag.py b/planetstack/core/models/tag.py
index 7818c32..b1e510a 100644
--- a/planetstack/core/models/tag.py
+++ b/planetstack/core/models/tag.py
@@ -24,9 +24,7 @@
 
 
     def can_update(self, user):
-        if user.is_admin:
-            return True
-        return False
+        return user.can_update_root()
 
     @staticmethod
     def select_by_user(user):
diff --git a/planetstack/core/models/user.py b/planetstack/core/models/user.py
index 60fd290..924f602 100644
--- a/planetstack/core/models/user.py
+++ b/planetstack/core/models/user.py
@@ -294,13 +294,14 @@
     def can_update(self, user):
         from core.models import SitePrivilege
         _cant_update_fieldName = None
-        if user.is_readonly:
-            return False
-        if user.is_admin:
+        if user.can_update_root():
             return True
+
         # site pis can update
         site_privs = SitePrivilege.objects.filter(user=user, site=self.site)
         for site_priv in site_privs:
+            if site_priv.role.role == 'admin':
+                return True 
             if site_priv.role.role == 'pi':
                 for fieldName in self.diff.keys():
                     if fieldName in self.PI_FORBIDDEN_FIELDS:
@@ -316,6 +317,52 @@
 
         return False
 
+    def can_update_root(self):
+        """
+        Return True if user has root (global) write access. 
+        """
+        if self.is_readonly:
+            return False
+        if self.is_admin:
+            return True
+
+        return False 
+
+    def can_update_deployment(self, deployment):
+        from core.models.site import DeploymentPrivilege
+        if self.can_update_root():
+            return True    
+                          
+        if DeploymentPrivilege.objects.filter(
+            deployment=deployment,
+            user=self,
+            role__role__in=['admin', 'Admin']):
+            return True
+        return False    
+
+    def can_update_site(self, site, allow=[]):
+        from core.models.site import SitePrivilege
+        if self.can_update_root():
+            return True
+        if SitePrivilege.objects.filter(
+            site=site, user=self, role__role__in=['admin', 'Admin']+allow):
+            return True
+        return False
+    
+    def can_update_slice(self, slice):
+        from core.models.slice import SlicePrivilege
+        if self.can_update_root():
+            return True
+        if self == slice.creator:
+            return True
+        if self.can_update_site(slice.site, allow=['pi']):
+            return True
+                     
+        if SlicePrivilege.objects.filter(
+            slice=slice, user=self, role__role__in=['admin', 'Admin']):
+            return True
+        return False
+
     @staticmethod
     def select_by_user(user):
         if user.is_admin:
diff --git a/planetstack/openstack_observer/event_loop.py b/planetstack/openstack_observer/event_loop.py
index ce0ab5e..0d1486d 100644
--- a/planetstack/openstack_observer/event_loop.py
+++ b/planetstack/openstack_observer/event_loop.py
@@ -122,12 +122,17 @@
 		try:
 			# This contains dependencies between records, not sync steps
 			self.model_dependency_graph = json.loads(open(dep_path).read())
-			for lst in self.model_dependency_graph.values():
+			for left,lst in self.model_dependency_graph.items():
+                                new_lst = [] 
 				for k in lst:
 					try:
+                                                tup = (k,k.lower())
+                                                new_lst.append(tup)
 						deps = self.model_dependency_graph[k]
 					except:
 						self.model_dependency_graph[k] = []
+
+                                self.model_dependency_graph[left] = new_lst
 		except Exception,e:
 			raise e
 
@@ -157,13 +162,13 @@
 					provides_dict[m.__name__]=[s.__name__]
 
 		step_graph = {}
-		for k,v in self.model_dependency_graph.iteritems():
+		for k,v in self.model_dependency_graph.items():
 			try:
 				for source in provides_dict[k]:
 					if (not v):
 						step_graph[source] = []
 		
-					for m in v:
+					for m,_ in v:
 						try:
 							for dest in provides_dict[m]:
 								# no deps, pass
@@ -187,7 +192,7 @@
 		pp.pprint(step_graph)
 		self.ordered_steps = toposort(self.dependency_graph, map(lambda s:s.__name__,self.sync_steps))
 		#self.ordered_steps = ['SyncRoles', 'SyncControllerSites', 'SyncControllerSitePrivileges','SyncImages', 'SyncControllerImages','SyncControllerUsers','SyncControllerUserSitePrivileges','SyncControllerSlices', 'SyncControllerSlicePrivileges', 'SyncControllerUserSlicePrivileges', 'SyncControllerNetworks','SyncSlivers']
-		#self.ordered_steps = ['SyncControllerSites']
+		#self.ordered_steps = ['SyncControllerSites','SyncControllerUsers','SyncControllerSlices','SyncControllerNetworks']
 
 		print "Order of steps=",self.ordered_steps
 
@@ -247,7 +252,9 @@
 	def check_class_dependency(self, step, failed_steps):
 		step.dependenices = []
 		for obj in step.provides:
-			step.dependenices.extend(self.model_dependency_graph.get(obj.__name__, []))
+		        lst = self.model_dependency_graph.get(obj.__name__, [])
+			nlst = map(lambda(a,b):b,lst)
+			step.dependenices.extend(nlst)
 		for failed_step in failed_steps:
 			if (failed_step in step.dependencies):
 				raise StepNotReady
@@ -302,13 +309,15 @@
 			my_status = STEP_STATUS_KO
 		else:
 			sync_step = step(driver=self.driver,error_map=self.error_mapper)
-			sync_step.__name__ = step.__name__
+			sync_step. __name__= step.__name__
 			sync_step.dependencies = []
 			try:
 				mlist = sync_step.provides
 
 				for m in mlist:
-					sync_step.dependencies.extend(self.model_dependency_graph[m.__name__])
+				        lst =  self.model_dependency_graph[m.__name__]
+			                nlst = map(lambda(a,b):b,lst)
+					sync_step.dependencies.extend(nlst)
 			except KeyError:
 				pass
 			sync_step.debug_mode = debug_mode
diff --git a/planetstack/openstack_observer/steps/sync_network_slivers.py b/planetstack/openstack_observer/steps/sync_network_slivers.py
index 11a343b..7406fb2 100644
--- a/planetstack/openstack_observer/steps/sync_network_slivers.py
+++ b/planetstack/openstack_observer/steps/sync_network_slivers.py
@@ -12,6 +12,7 @@
 class SyncNetworkSlivers(OpenStackSyncStep):
     requested_interval = 0 # 3600
     provides=[NetworkSliver]
+    observes=NetworkSliver
 
     #     The way it works is to enumerate the all of the ports that quantum
     #     has, and then work backward from each port's network-id to determine
diff --git a/planetstack/openstack_observer/syncstep.py b/planetstack/openstack_observer/syncstep.py
index 247d4c9..638c1eb 100644
--- a/planetstack/openstack_observer/syncstep.py
+++ b/planetstack/openstack_observer/syncstep.py
@@ -5,12 +5,26 @@
 from util.logger import Logger, logging
 from observer.steps import *
 from django.db.models import F, Q
+from core.models import * 
 import json
 import time
 import pdb
 
 logger = Logger(level=logging.INFO)
 
+def f7(seq):
+    seen = set()
+    seen_add = seen.add
+    return [ x for x in seq if not (x in seen or seen_add(x))]
+
+def elim_dups(backend_str):
+    strs = backend_str.split(' // ')
+    strs2 = f7(strs)
+    return ' // '.join(strs2)
+    
+def deepgetattr(obj, attr):
+    return reduce(getattr, attr.split('.'), obj)
+
 class FailedDependency(Exception):
     pass
 
@@ -63,14 +77,19 @@
     def check_dependencies(self, obj, failed):
         for dep in self.dependencies:
             peer_name = dep[0].lower() + dep[1:]    # django names are camelCased with the first letter lower
+ 
             try:
-                peer_object = getattr(obj, peer_name)
+                peer_object = deepgetattr(obj, peer_name)
+                try: 
+                    peer_objects = peer_object.all() 
+                except AttributeError:
+                    peer_objects = [peer_object] 
             except:
-                peer_object = None
+                peer_objects = []
 
-            if (peer_object and peer_object.pk==failed.pk and type(peer_object)==type(failed)):
-                if (obj.backend_status!=peer_object.backend_status):
-                    obj.backend_status = peer_object.backend_status
+            if (failed in peer_objects):
+                if (obj.backend_status!=failed.backend_status):
+                    obj.backend_status = failed.backend_status
                     obj.save(update_fields=['backend_status'])
                 raise FailedDependency("Failed dependency for %s:%s peer %s:%s failed  %s:%s" % (obj.__class__.__name__, str(obj.pk), peer_object.__class__.__name__, str(peer_object.pk), failed.__class__.__name__, str(failed.pk)))
 
@@ -104,7 +123,15 @@
                         o.save(update_fields=['enacted','backend_status','backend_register'])
                 except Exception,e:
                     logger.log_exc("sync step failed!")
-                    str_e = '%r'%e
+                    try:
+                        if (o.backend_status.startswith('2 - ')):
+                            str_e = '%s // %r'%(o.backend_status[4:],e)
+			    str_e = elim_dups(str_e)
+                        else:
+                            str_e = '%r'%e
+                    except:
+                        str_e = '%r'%e
+
                     try:
                         o.backend_status = '2 - %s'%self.error_map.map(str_e)
                     except:
@@ -131,7 +158,7 @@
                     # DatabaseError: value too long for type character varying(140)
                     if (o.pk):
                         try:
-                            o.backend_status = o.backend_status[:140]
+                            o.backend_status = o.backend_status[:1024]
                             o.save(update_fields=['backend_status','backend_register'])
                         except:
                             print "Could not update backend status field!"