Merge pull request #25 from caglar10ur/ACLValidationError

NameError: global name 'ACLValidationError' is not defined
diff --git a/planetstack/core/admin.py b/planetstack/core/admin.py
index e03098e..81fdbf1 100644
--- a/planetstack/core/admin.py
+++ b/planetstack/core/admin.py
@@ -397,7 +397,7 @@
 class DeploymentPrivilegeInline(PlStackTabularInline):
     model = DeploymentPrivilege
     extra = 0
-    suit_classes = 'suit-tab suit-tab-admin-only'
+    suit_classes = 'suit-tab suit-tab-deploymentprivileges'
     fields = ['backend_status_icon', 'user','role','deployment']
     readonly_fields = ('backend_status_icon', )
 
@@ -507,14 +507,6 @@
     pass
 
 class DeploymentAdminForm(forms.ModelForm):
-    sites = forms.ModelMultipleChoiceField(
-        queryset=Site.objects.all(),
-        required=False,
-        help_text="Select which sites are allowed to host nodes in this deployment",
-        widget=FilteredSelectMultiple(
-            verbose_name=('Sites'), is_stacked=False
-        )
-    )
     images = forms.ModelMultipleChoiceField(
         queryset=Image.objects.all(),
         required=False,
@@ -542,7 +534,6 @@
       self.fields['accessControl'].initial = "allow site " + request.user.site.name
 
       if self.instance and self.instance.pk:
-        self.fields['sites'].initial = [x.site for x in self.instance.sitedeployments.all()]
         self.fields['images'].initial = [x.image for x in self.instance.imagedeployments.all()]
         self.fields['flavors'].initial = self.instance.flavors.all()
 
@@ -594,7 +585,6 @@
         #    create/destroy the through models ourselves. There has to be
         #    a better way...
 
-        self.manipulate_m2m_objs(deployment, self.cleaned_data['sites'], deployment.sitedeployments.all(), SiteDeployment, "deployment", "site")
         self.manipulate_m2m_objs(deployment, self.cleaned_data['images'], deployment.imagedeployments.all(), ImageDeployments, "deployment", "image")
         # manipulate_m2m_objs doesn't work for Flavor/Deployment relationship
         # so well handle that manually here
@@ -620,8 +610,8 @@
 
 class DeploymentAdmin(PlanetStackBaseAdmin):
     model = Deployment
-    fieldList = ['backend_status_text', 'name', 'sites', 'images', 'flavors', 'accessControl']
-    fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-sites']})]
+    fieldList = ['backend_status_text', 'name', 'images', 'flavors', 'accessControl']
+    fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
     # node no longer directly connected to deployment
     #inlines = [DeploymentPrivilegeInline,NodeInline,TagInline,ImageDeploymentsInline]
     inlines = [DeploymentPrivilegeInline,TagInline,ImageDeploymentsInline,SiteDeploymentInline]
@@ -632,8 +622,7 @@
     user_readonly_fields = ['name']
 
     # nodes no longer direclty connected to deployments
-    #suit_form_tabs =(('sites','Deployment Details'),('nodes','Nodes'),('deploymentprivileges','Privileges'),('tags','Tags'),('imagedeployments','Images'))
-    suit_form_tabs =(('sites','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Site Deployments'))
+    suit_form_tabs =(('general','Deployment Details'),('deploymentprivileges','Privileges'), ('sitedeployments', 'Sites'))
 
     def get_form(self, request, obj=None, **kwargs):
         if request.user.isReadOnlyUser() or not request.user.is_admin:
@@ -651,73 +640,10 @@
 
         return AdminFormMetaClass
 
-class ControllerAdminForm(forms.ModelForm):
-    sites = forms.ModelMultipleChoiceField(
-        queryset=Site.objects.all(),
-        required=False,
-        help_text="Select which sites are managed by this controller",
-        widget=FilteredSelectMultiple(
-            verbose_name=('Sites'), is_stacked=False
-        )
-    )
-
-    class Meta: 
-        model = Controller
-        
-    def __init__(self, *args, **kwargs):
-        request = kwargs.pop('request', None)
-        super(ControllerAdminForm, self).__init__(*args, **kwargs)  
-
-        if self.instance and self.instance.pk:
-            self.fields['sites'].initial = [x.site for x in self.instance.controllersite.all()]
-
-    def manipulate_m2m_objs(self, this_obj, selected_objs, all_relations, relation_class, local_attrname, foreign_attrname):
-        """ helper function for handling m2m relations from the MultipleChoiceField
-            this_obj: the source object we want to link from
-            selected_objs: a list of destination objects we want to link to
-            all_relations: the full set of relations involving this_obj, including ones we don't want
-            relation_class: the class that implements the relation from source to dest
-            local_attrname: field name representing this_obj in relation_class
-            foreign_attrname: field name representing selected_objs in relation_class
-            This function will remove all newobjclass relations from this_obj
-            that are not contained in selected_objs, and add any relations that
-            are in selected_objs but don't exist in the data model yet.
-        """
-        existing_dest_objs = []
-        for relation in list(all_relations):
-            if getattr(relation, foreign_attrname) not in selected_objs:
-                #print "deleting site", sdp.site
-                relation.delete()
-            else:
-                existing_dest_objs.append(getattr(relation, foreign_attrname))
-
-        for dest_obj in selected_objs:
-            if dest_obj not in existing_dest_objs:
-                #print "adding site", site
-                kwargs = {foreign_attrname: dest_obj, local_attrname: this_obj}
-                relation = relation_class(**kwargs)
-                relation.save()
-
-    def save(self, commit=True):
-        controller = super(ControllerAdminForm, self).save(commit=False)
-        if commit:
-            controller.save()
-
-        if controller.pk:
-            # save_m2m() doesn't seem to work with 'through' relations. So we
-            #    create/destroy the through models ourselves. There has to be
-            #    a better way...
-            self.manipulate_m2m_objs(controller, self.cleaned_data['sites'], controller.controllersite.all(), ControllerSite, "controller", "site")
-            pass
-    	
-        self.save_m2m()
-
-        return controller 
-      
 class ControllerAdmin(PlanetStackBaseAdmin):
-    model = Controller 
-    fieldList = ['name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password']
-    #fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
+    model = Controller
+    fieldList = ['name', 'backend_type', 'version', 'auth_url', 'admin_user', 'admin_tenant','admin_password', 'domain']
+    fieldsets = [(None, {'fields': fieldList, 'classes':['suit-tab suit-tab-general']})]
     inlines = [ControllerSiteInline] # ,ControllerImagesInline]
     list_display = ['backend_status_icon', 'name', 'version', 'backend_type']
     list_display_links = ('backend_status_icon', 'name', )
@@ -725,29 +651,23 @@
 
     user_readonly_fields = []
 
-    def get_form(self, request, obj=None, **kwargs):
-        print self.fieldsets
-        if request.user.isReadOnlyUser():
-            kwargs["form"] = ControllerAdminROForm
-        else:
-            kwargs["form"] = ControllerAdminForm
-        adminForm = super(ControllerAdmin,self).get_form(request, obj, **kwargs)
-
-        # from stackexchange: pass the request object into the form
-
-        class AdminFormMetaClass(adminForm):
-           def __new__(cls, *args, **kwargs):
-               kwargs['request'] = request
-               return adminForm(*args, **kwargs)
-
-        return AdminFormMetaClass
-
     def save_model(self, request, obj, form, change):
         # update openstack connection to use this site/tenant
         obj.save_by_user(request.user)
                     
     def delete_model(self, request, obj):
-        obj.delete_by_user(request.user)    
+        obj.delete_by_user(request.user)
+
+    @property
+    def suit_form_tabs(self):
+        tabs = [('general', 'Controller Details'),
+        ]
+
+        request=getattr(_thread_locals, "request", None)
+        if request and request.user.is_admin:
+            tabs.append( ('admin-only', 'Admin-Only') )
+
+        return tabs
 
 class ServiceAttrAsTabInline(PlStackTabularInline):
     model = ServiceAttribute
diff --git a/planetstack/core/migrations/0001_initial.py b/planetstack/core/migrations/0001_initial.py
index fe34224..ad82329 100644
--- a/planetstack/core/migrations/0001_initial.py
+++ b/planetstack/core/migrations/0001_initial.py
@@ -103,6 +103,7 @@
                 ('admin_user', models.CharField(help_text=b'Username of an admin user at this controller', max_length=200, null=True, blank=True)),
                 ('admin_password', models.CharField(help_text=b'Password of theadmin user at this controller', max_length=200, null=True, blank=True)),
                 ('admin_tenant', models.CharField(help_text=b'Name of the tenant the admin user belongs to', max_length=200, null=True, blank=True)),
+                ('domain', models.CharField(help_text=b'Name of the domain this controller belongs to', max_length=200, null=True, blank=True)),
             ],
             options={
                 'abstract': False,
diff --git a/planetstack/core/models/plcorebase.py b/planetstack/core/models/plcorebase.py
index 97c3f82..5369512 100644
--- a/planetstack/core/models/plcorebase.py
+++ b/planetstack/core/models/plcorebase.py
@@ -224,8 +224,8 @@
         super(PlCoreBase, self).save(*args, **kwargs)
 
         # This is a no-op if observer_disabled is set
-        if not silent:
-            notify_observer()
+        # if not silent:
+        #    notify_observer()
 
         self._initial = self._dict
 
diff --git a/planetstack/core/models/site.py b/planetstack/core/models/site.py
index b1b4871..2a41352 100644
--- a/planetstack/core/models/site.py
+++ b/planetstack/core/models/site.py
@@ -196,7 +196,7 @@
         return Deployment.objects.filter(id__in=ids)
 
     def can_update(self, user):
-        return user.can_update_deploymemt(self)
+        return user.can_update_deployment(self)
     
     def __unicode__(self):  return u'%s' % (self.name)
 
@@ -219,7 +219,7 @@
     def __unicode__(self):  return u'%s %s %s' % (self.deployment, self.user, self.role)
 
     def can_update(self, user):
-        return user.can_update_deploymemt(self)
+        return user.can_update_deployment(self)
 
     @staticmethod
     def select_by_user(user):
@@ -251,6 +251,7 @@
     admin_user = models.CharField(max_length=200, null=True, blank=True, help_text="Username of an admin user at this controller")
     admin_password = models.CharField(max_length=200, null=True, blank=True, help_text="Password of theadmin user at this controller")
     admin_tenant = models.CharField(max_length=200, null=True, blank=True, help_text="Name of the tenant the admin user belongs to")
+    domain = models.CharField(max_length=200, null=True, blank=True, help_text="Name of the domain this controller belongs to")
 
     def __unicode__(self):  return u'%s %s %s' % (self.name, self.backend_type, self.version)
 
diff --git a/planetstack/core/xoslib/objects/plus.py b/planetstack/core/xoslib/objects/plus.py
index 5f2e2f3..01cb8d7 100644
--- a/planetstack/core/xoslib/objects/plus.py
+++ b/planetstack/core/xoslib/objects/plus.py
@@ -6,10 +6,10 @@
 
 class PlusObjectMixin:
     def getBackendIcon(self):
-        if (self.enacted is not None) and self.enacted >= self.updated:
+        if (self.enacted is not None) and self.enacted >= self.updated or self.backend_status.startswith("1 -"):
             return "/static/admin/img/icon_success.gif"
         else:
-            if self.backend_status == "Provisioning in progress" or self.backend_status=="":
+            if ((self.backend_status is not None) and self.backend_status.startswith("0 -")) or self.backend_status == "Provisioning in progress" or self.backend_status=="":
                 return "/static/admin/img/icon_clock.gif"
             else:
                 return "/static/admin/img/icon_error.gif"
@@ -20,3 +20,4 @@
         else:
             return '<span title="%s"><img src="%s"></span>' % (self.backend_status, self.getBackendIcon())
 
+
diff --git a/planetstack/ec2_observer/steps/sync_nodes.py b/planetstack/ec2_observer/steps/sync_nodes.py
deleted file mode 100644
index 0fa8b61..0000000
--- a/planetstack/ec2_observer/steps/sync_nodes.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import os
-import base64
-import random
-import time
-from datetime import datetime 
-from django.db.models import F, Q
-from planetstack.config import Config
-from ec2_observer.syncstep import SyncStep
-from core.models.node import Node
-from core.models.site import *
-from ec2_observer.awslib import *
-import pdb
-
-class SyncNodes(SyncStep):
-    provides=[Node]
-    requested_interval=0
-
-    def fetch_pending(self, deletion):
-        if (deletion):
-            return []
-
-        deployment = Deployment.objects.filter(Q(name="Amazon EC2"))[0]
-        current_site_deployments = SiteDeployment.objects.filter(Q(deployment=deployment))
-
-        zone_ret = aws_run('ec2 describe-availability-zones')
-        zones = zone_ret['AvailabilityZones']
-
-        # collect local nodes
-        instance_types = 'm1.small | m1.medium | m1.large | m1.xlarge | m3.medium | m3.large | m3.xlarge | m3.2xlarge'.split(' | ')
-
-        all_new_nodes = []
-        for sd in current_site_deployments:
-            s = sd.site
-            current_fqns = [n.name for n in s.nodes.all()]
-            all_fqns = ['.'.join([n,s.name]) for n in instance_types]
-            new_node_names = list(set(all_fqns) - set(current_fqns))
-
-            new_nodes = []
-            for node_name in new_node_names:
-                node = Node(name=node_name,
-                            site=s,deployment=deployment)
-                new_nodes.append(node)
-
-            all_new_nodes.extend(new_nodes)
-
-        return all_new_nodes
-                 
-
-    def sync_record(self, node):
-        node.save()
-          
diff --git a/planetstack/openstack_observer/steps/sync_nodes.py b/planetstack/openstack_observer/steps/sync_nodes.py
deleted file mode 100644
index e2c63bc..0000000
--- a/planetstack/openstack_observer/steps/sync_nodes.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import os
-import base64
-import random
-from datetime import datetime
-from django.db.models import F, Q
-from planetstack.config import Config
-from observer.openstacksyncstep import OpenStackSyncStep
-from core.models.node import Node
-from core.models.site import SiteDeployment, Controller
-from util.logger import Logger, logging
-
-logger = Logger(level=logging.INFO)
-
-class SyncNodes(OpenStackSyncStep):
-    provides=[Node]
-    requested_interval=0
-
-    def fetch_pending(self, deleted):
-        # Nodes come from the back end
-        # You can't delete them
-        if (deleted):
-            return []
-
-        # collect local nodes
-        controllers = Controller.objects.all()
-        nodes = Node.objects.all()
-        node_hostnames = [node.name for node in nodes]
-
-        # fetch all nodes from each controller
-        new_nodes = []
-        for controller in controllers:
-            try:
-            	controller_site_deployments = SiteDeployment.objects.filter(controller=controller)[0]
-	    except IndexError:
-                raise Exception("Controller %s not bound to any site deployments"%controller.name)
-
-            site_deployment = controller_site_deployments.site_deployment
-            if (not site_deployment):
-                raise Exception('Controller without Site Deployment: %s'%controller.name)
-
-            try:
-                driver = self.driver.admin_driver(controller=controller,tenant='admin')
-                compute_nodes = driver.shell.nova.hypervisors.list()
-            except:
-                logger.log_exc("Failed to get nodes from controller %s" % str(controller))
-                continue
-
-            for compute_node in compute_nodes:
-                if compute_node.hypervisor_hostname not in node_hostnames:
-                    # XX TODO:figure out how to correctly identify a node's site.
-                    # XX pick the first one
-                    node = Node(name=compute_node.hypervisor_hostname,
-                                site_deployment=site_deployment)
-                    new_nodes.append(node)
-
-        return new_nodes    
-                 
-
-    def sync_record(self, node):
-        node.save()
-