Merged plcorebase
authorSiobhan Tully <stully@verivue.com>
Wed, 9 Oct 2013 01:56:18 +0000 (21:56 -0400)
committerSiobhan Tully <stully@verivue.com>
Wed, 9 Oct 2013 01:56:18 +0000 (21:56 -0400)
35 files changed:
planetstack/core/models/deployment.py
planetstack/core/models/plcorebase.py
planetstack/core/models/role.py
planetstack/core/models/site.py
planetstack/core/models/slice.py
planetstack/observer/deleters/network_deleter.py [new file with mode: 0644]
planetstack/observer/deleters/network_sliver_deleter.py [new file with mode: 0644]
planetstack/observer/deleters/site_deleter.py [new file with mode: 0644]
planetstack/observer/deleters/slice_deleter.py
planetstack/observer/deleters/sliver_deleter.py [new file with mode: 0644]
planetstack/observer/deleters/user_deleter.py [new file with mode: 0644]
planetstack/observer/event_loop.py
planetstack/observer/event_manager.py
planetstack/observer/openstacksyncstep.py
planetstack/observer/steps/__init__.py
planetstack/observer/steps/garbage_collector.py
planetstack/observer/steps/sync_external_routes.py
planetstack/observer/steps/sync_network_slivers.py
planetstack/observer/steps/sync_networks.py
planetstack/observer/steps/sync_roles.py [new file with mode: 0644]
planetstack/observer/steps/sync_site_privileges.py
planetstack/observer/steps/sync_sites.py
planetstack/observer/steps/sync_slice_memberships.py
planetstack/observer/steps/sync_slices.py
planetstack/observer/steps/sync_sliver_ips.py
planetstack/observer/steps/sync_slivers.py
planetstack/observer/steps/sync_users.py
planetstack/observer/syncstep.py
planetstack/observer/toposort.py
planetstack/openstack/driver.py
planetstack/openstack/manager.py
planetstack/plstackapi_config
planetstack/tools/openstack-db-cleanup.sh [moved from planetstack/openstack/openstack-db-cleanup.sh with 100% similarity]
planetstack/tools/openstack-healthcheck.py [new file with mode: 0755]
planetstack/util/logger.py [new file with mode: 0644]

index 8bf9c06..9a4cbe1 100644 (file)
@@ -15,6 +15,7 @@ class DeploymentRole(PlCoreBase):
 
     ROLE_CHOICES = (('admin','Admin'),)
     role = models.CharField(choices=ROLE_CHOICES, unique=True, max_length=30)
+    krole_id = models.CharField(max_length=80, verbose_name="Keystone role id", null=True, blank=True)
 
     def __unicode__(self):  return u'%s' % (self.role)
 
index 4a64ce5..dcc3c39 100644 (file)
@@ -8,6 +8,7 @@ class PlCoreBase(models.Model):
 
        created = models.DateTimeField(auto_now_add=True)
        updated = models.DateTimeField(auto_now=True)
+       enacted = models.DateTimeField(null=True, default=None)
 
        class Meta:
                abstract = True
@@ -38,7 +39,11 @@ class PlCoreBase(models.Model):
        def delete(self, *args, **kwds):
                super(PlCoreBase, self).delete(*args, **kwds)
 
-#              EventSender().fire({'delete_flag':True,'model':self.__name__})
+               try:
+                       EventSender().fire({'delete_flag':True,'model':self.__name__})
+               except:
+                       # Investigate later. 
+                       pass
 
        def save(self, *args, **kwargs):
                super(PlCoreBase, self).save(*args, **kwargs)
index 234868e..bd97f52 100644 (file)
@@ -8,6 +8,7 @@ from django.contrib.contenttypes import generic
 class Role(PlCoreBase):
 
     role_type = models.CharField(max_length=80, verbose_name="Name")
+    role = models.CharField(max_length=80, verbose_name="Keystone role id", null=True, blank=True)
     description = models.CharField(max_length=120, verbose_name="Description")
     content_type = models.ForeignKey(ContentType, verbose_name="Role Scope")
 
@@ -15,16 +16,8 @@ class Role(PlCoreBase):
 
 
     def save(self, *args, **kwds):
-        if not hasattr(self, 'os_manager'):
-            from openstack.manager import OpenStackManager
-            setattr(self, 'os_manager', OpenStackManager())
-        self.os_manager.save_role(self)
         super(Role, self).save(*args, **kwds)
     
     def delete(self, *args, **kwds):
-        if not hasattr(self, 'os_manager'):
-            from openstack.manager import OpenStackManager
-            setattr(self, 'os_manager', OpenStackManager())
-        self.os_manager.delete_role(self)   
         super(Role, self).delete(*args, **kwds)
             
index aee3843..56f9bd0 100644 (file)
@@ -26,8 +26,9 @@ class Site(PlCoreBase):
 
 class SiteRole(PlCoreBase):
 
-    ROLE_CHOICES = (('admin','Admin'),('pi','PI'),('tech','Tech'),('billing','Billing'))
+    ROLE_CHOICES = (('admin','Admin'),('pi','PI'),('tech','Tech'),('billing','Billing'), ('user', 'User'))
     role = models.CharField(choices=ROLE_CHOICES, unique=True, max_length=30)
+    krole_id = models.CharField(max_length=80, verbose_name="Keystone role id", null=True, blank=True)
 
     def __unicode__(self):  return u'%s' % (self.role)
 
index 51e05f5..b7ca15d 100644 (file)
@@ -43,9 +43,10 @@ class Slice(PlCoreBase):
         super(Slice, self).save(*args, **kwds)
 
 class SliceRole(PlCoreBase):
-    ROLE_CHOICES = (('admin','Admin'),('default','Default'))
+    ROLE_CHOICES = (('admin','Admin'),('default','Default'), ('user', 'User'), ('pi', 'PI'))
 
     role = models.CharField(choices=ROLE_CHOICES, unique=True, max_length=30)
+    krole_id = models.CharField(max_length=80, verbose_name="Keystone role id", null=True, blank=True)
 
     def __unicode__(self):  return u'%s' % (self.role)
 
diff --git a/planetstack/observer/deleters/network_deleter.py b/planetstack/observer/deleters/network_deleter.py
new file mode 100644 (file)
index 0000000..51f9fcb
--- /dev/null
@@ -0,0 +1,17 @@
+from core.models import Network
+from deleter import Deleter
+
+class NetworkDeleter(Deleter):
+    model='Network'
+
+    def call(self, pk):
+        network = Network.objects.get(pk=pk) 
+        if (network.router_id) and (network.subnet_id):
+            self.driver.delete_router_interface(network.router_id, network.subnet_id)
+        if network.subnet_id:
+            self.driver.delete_subnet(network.subnet_id)
+        if network.router_id:
+            self.driver.delete_router(network.router_id)
+        if network.network_id:
+            self.driver.delete_network(network.network_id)
+        network.delete()
diff --git a/planetstack/observer/deleters/network_sliver_deleter.py b/planetstack/observer/deleters/network_sliver_deleter.py
new file mode 100644 (file)
index 0000000..71ba040
--- /dev/null
@@ -0,0 +1,13 @@
+from core.models import NetworkSliver
+from observer.deleter import Deleter
+
+class NetworkSliverDeleter(Deleter):
+    model='NetworkSliver'
+
+    def call(self, pk):
+        network_sliver = NetworkSlivers.objects.get(pk=pk)
+        # handle openstack delete
+
+        network_sliver.delete() 
+
+    
diff --git a/planetstack/observer/deleters/site_deleter.py b/planetstack/observer/deleters/site_deleter.py
new file mode 100644 (file)
index 0000000..bb29c94
--- /dev/null
@@ -0,0 +1,11 @@
+from core.models import Site
+from observer.delete import Deleter
+
+class SiteDeleter(Deleter):
+    model='Site'
+    
+    def call(self, pk):
+        site = Site.objects.get(pk=pk)
+        if site.tenant_id:
+            self.driver.delete_tenant(site.tenant_id)
+        site.delete() 
index 4cb0a72..6796d7a 100644 (file)
@@ -1,9 +1,22 @@
-#from code.models import Slice
+from core.models import Slice
+from observer.deleter import Deleter
 
-class SliceDeleter:
+class SliceDeleter(Deleter):
        model='Slice'
 
        def call(self, pk):
-               s = Slice.objects.get(pk=pk)
-
-               # Proceed with delete
+               slice = Slice.objects.get(pk=pk)
+        self.driver.delete_router_interface(slice.router_id, slice.subnet_id)
+        self.driver.delete_subnet(slice.subnet_id)
+        self.driver.delete_router(slice.router_id)
+        self.driver.delete_network(slice.network_id)
+        self.driver.delete_tenant(slice.tenant_id)
+        # delete external route
+        subnet = None
+        subnets = self.driver.shell.quantum.list_subnets()['subnets']
+        for snet in subnets:
+            if snet['id'] == slice.subnet_id:
+                subnet = snet
+        if subnet:
+            self.driver.delete_external_route(subnet)
+        slice.delete()
diff --git a/planetstack/observer/deleters/sliver_deleter.py b/planetstack/observer/deleters/sliver_deleter.py
new file mode 100644 (file)
index 0000000..d76b533
--- /dev/null
@@ -0,0 +1,11 @@
+from core.models import Sliver
+from observer.deleter import Deleter
+
+class SliverDeleter(Deleter):
+    model='Sliver'
+
+    def call(self, pk):
+        sliver = Sliver.objects.get(pk=pk)
+        if sliver.instance_id:
+            self.driver.destroy_instance(sliver.instance_id)
+        sliver.delete()
diff --git a/planetstack/observer/deleters/user_deleter.py b/planetstack/observer/deleters/user_deleter.py
new file mode 100644 (file)
index 0000000..f250993
--- /dev/null
@@ -0,0 +1,11 @@
+from core.models import User
+from observer.deleter import Deleter
+
+class UserDeleter(Deleter):
+    model='User'
+
+    def call(self, pk):
+        user = User.objects.get(pk=pk)
+        if user.kuser_id:
+            self.driver.delete_user(user.kuser_id)
+        user.delete()
index 671bdc3..7903ce4 100644 (file)
@@ -8,7 +8,8 @@ from datetime import datetime
 from collections import defaultdict
 from core.models import *
 from django.db.models import F, Q
-from openstack.manager import OpenStackManager
+#from openstack.manager import OpenStackManager
+from openstack.driver import OpenStackDriver
 from util.logger import Logger, logging, logger
 #from timeout import timeout
 from planetstack.config import Config
@@ -21,7 +22,14 @@ logger = Logger(logfile='observer.log', level=logging.INFO)
 class StepNotReady(Exception):
        pass
 
-def toposort(g, steps):
+def toposort(g, steps=None):
+       if (not steps):
+               keys = set(g.keys())
+               values = set({})
+               for v in g.values():
+                       values=values | set(v)
+               
+               steps=list(keys|values)
        reverse = {}
 
        for k,v in g.items():
@@ -53,16 +61,22 @@ def toposort(g, steps):
                                        marked.append(m)
                except KeyError:
                        pass
-               order.append(n)
+               if (n in steps):
+                       order.append(n)
+
+       order.reverse()
+       order.extend(set(steps)-set(order))
        return order
 
 class PlanetStackObserver:
-       sync_steps = [SyncNetworks,SyncNetworkSlivers,SyncSites,SyncSitePrivileges,SyncSlices,SyncSliceMemberships,SyncSlivers,SyncSliverIps]
+       sync_steps = [SyncNetworks,SyncNetworkSlivers,SyncSites,SyncSitePrivileges,SyncSlices,SyncSliceMemberships,SyncSlivers,SyncSliverIps,SyncExternalRoutes,SyncUsers,SyncRoles,GarbageCollector]
 
        def __init__(self):
                # The Condition object that gets signalled by Feefie events
+               self.step_lookup = {}
                self.load_sync_steps()
                self.event_cond = threading.Condition()
+               self.driver = OpenStackDriver()
 
        def wait_for_event(self, timeout):
                self.event_cond.acquire()
@@ -76,15 +90,15 @@ class PlanetStackObserver:
                self.event_cond.release()
 
        def load_sync_steps(self):
-               dep_path = Config().observer_dependency_path
+               dep_path = Config().observer_backend_dependency_graph
                try:
                        # This contains dependencies between records, not sync steps
                        self.model_dependency_graph = json.loads(open(dep_path).read())
                except Exception,e:
                        raise e
 
-               backend_path = Config().observer_backend_dependency_path
                try:
+                       backend_path = Config().observer_pl_dependency_graph
                        # This contains dependencies between backend records
                        self.backend_dependency_graph = json.loads(open(backend_path).read())
                except Exception,e:
@@ -93,6 +107,7 @@ class PlanetStackObserver:
 
                provides_dict = {}
                for s in self.sync_steps:
+                       self.step_lookup[s.__name__] = s 
                        for m in s.provides:
                                try:
                                        provides_dict[m.__name__].append(s.__name__)
@@ -119,11 +134,11 @@ class PlanetStackObserver:
                                pass
                                # no dependencies, pass
                
-               import pdb
-               pdb.set_trace()
+               #import pdb
+               #pdb.set_trace()
                if (self.backend_dependency_graph):
                        backend_dict = {}
-                       for s in sync_steps:
+                       for s in self.sync_steps:
                                for m in s.serves:
                                        backend_dict[m]=s.__name__
                                        
@@ -144,29 +159,29 @@ class PlanetStackObserver:
 
                dependency_graph = step_graph
 
-               self.ordered_steps = toposort(dependency_graph, self.sync_steps)
+               self.ordered_steps = toposort(dependency_graph, map(lambda s:s.__name__,self.sync_steps))
                print "Order of steps=",self.ordered_steps
                self.load_run_times()
                
 
-       def check_duration(self):
+       def check_duration(self, step, duration):
                try:
-                       if (duration > S.deadline):
-                               logger.info('Sync step %s missed deadline, took %.2f seconds'%(S.name,duration))
+                       if (duration > step.deadline):
+                               logger.info('Sync step %s missed deadline, took %.2f seconds'%(step.name,duration))
                except AttributeError:
                        # S doesn't have a deadline
                        pass
 
        def update_run_time(self, step):
-               self.last_run_times[step.name]=time.time()
+               self.last_run_times[step.__name__]=time.time()
 
        def check_schedule(self, step):
-               time_since_last_run = time.time() - self.last_run_times[step.name]
+               time_since_last_run = time.time() - self.last_run_times.get(step.__name__, 0)
                try:
                        if (time_since_last_run < step.requested_interval):
                                raise StepNotReady
                except AttributeError:
-                       logger.info('Step %s does not have requested_interval set'%step.name)
+                       logger.info('Step %s does not have requested_interval set'%step.__name__)
                        raise StepNotReady
        
        def load_run_times(self):
@@ -176,8 +191,7 @@ class PlanetStackObserver:
                except:
                        self.last_run_times={}
                        for e in self.ordered_steps:
-                               self.last_run_times[e.name]=0
-
+                               self.last_run_times[e]=0
 
 
        def save_run_times(self):
@@ -185,19 +199,21 @@ class PlanetStackObserver:
                open('/tmp/observer_run_times','w').write(run_times)
 
        def check_class_dependency(self, step, failed_steps):
-               for failed_step in failed_steps:
-                       if (failed_step in self.dependency_graph[step.name]):
-                               raise StepNotReady
+        step.dependenices = []
+        for obj in step.provides:
+            step.dependenices.extend(self.model_dependency_graph.get(obj.__name__, []))
+        for failed_step in failed_steps:
+            if (failed_step in step.dependencies):
+                raise StepNotReady
 
        def run(self):
-               if not self.manager.enabled or not self.manager.has_openstack:
+               if not self.driver.enabled or not self.driver.has_openstack:
                        return
-
                while True:
                        try:
                                logger.info('Waiting for event')
                                tBeforeWait = time.time()
-                               self.wait_for_event(timeout=300)
+                               self.wait_for_event(timeout=30)
                                logger.info('Observer woke up')
 
                                # Set of whole steps that failed
@@ -207,10 +223,19 @@ class PlanetStackObserver:
                                failed_step_objects = []
 
                                for S in self.ordered_steps:
+                                       step = self.step_lookup[S]
                                        start_time=time.time()
                                        
-                                       sync_step = S()
-                                       sync_step.dependencies = self.dependencies[sync_step.name]
+                                       sync_step = step(driver=self.driver)
+                                       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__])
+                                       except KeyError:
+                                               pass
                                        sync_step.debug_mode = debug_mode
 
                                        should_run = False
@@ -221,24 +246,28 @@ class PlanetStackObserver:
                                                self.check_schedule(sync_step) # dont run sync_network_routes if time since last run < 1 hour
                                                should_run = True
                                        except StepNotReady:
-                                               logging.info('Step not ready: %s'%sync_step.name)
-                                               failed_steps.add(sync_step)
+                                               logging.info('Step not ready: %s'%sync_step.__name__)
+                                               failed_steps.append(sync_step)
                                        except:
-                                               failed_steps.add(sync_step)
+                                               failed_steps.append(sync_step)
 
                                        if (should_run):
                                                try:
                                                        duration=time.time() - start_time
 
                                                        # ********* This is the actual sync step
+                                                       #import pdb
+                                                       #pdb.set_trace()
                                                        failed_objects = sync_step(failed=failed_step_objects)
 
 
-                                                       check_deadline(sync_step, duration)
-                                                       failed_step_objects.extend(failed_objects)
+                                                       self.check_duration(sync_step, duration)
+                                                       if failed_objects:
+                                                               failed_step_objects.extend(failed_objects)
                                                        self.update_run_time(sync_step)
                                                except:
-                                                       failed_steps.add(S)
+                                                       raise
+                                                       failed_steps.append(S)
                                self.save_run_times()
                        except:
                                logger.log_exc("Exception in observer run loop")
index c4215ac..60b615a 100644 (file)
@@ -1,8 +1,6 @@
 import threading
 import requests, json
 
-from core.models import *
-#from openstack.manager import OpenStackManager
 from planetstack.config import Config
 from observer.deleters import deleters
 
@@ -11,80 +9,80 @@ import base64
 from fofum import Fofum
 import json
 
-# decorator that marks dispatachable event methods     
+# decorator that marks dispatachable event methods    
 def event(func):
-       setattr(func, 'event', func.__name__)
-       return func              
+    setattr(func, 'event', func.__name__)
+    return func         
 
 class EventHandler:
-       # This code is currently not in use.
-       def __init__(self):
-               pass #self.manager = OpenStackManager()
-
-       @staticmethod
-       def get_events():
-               events = []
-               for name in dir(EventHandler):
-                       attribute = getattr(EventHandler, name)
-                       if hasattr(attribute, 'event'):
-                               events.append(getattr(attribute, 'event'))
-               return events
-
-       def dispatch(self, event, *args, **kwds):
-               if hasattr(self, event):
-                       return getattr(self, event)(*args, **kwds)
-                       
-               
+    # This code is currently not in use.
+    def __init__(self):
+        pass 
+
+    @staticmethod
+    def get_events():
+        events = []
+        for name in dir(EventHandler):
+            attribute = getattr(EventHandler, name)
+            if hasattr(attribute, 'event'):
+                events.append(getattr(attribute, 'event'))
+        return events
+
+    def dispatch(self, event, *args, **kwds):
+        if hasattr(self, event):
+            return getattr(self, event)(*args, **kwds)
+            
+        
 class EventSender:
-       def __init__(self,user=None,clientid=None):
-               try:
-                       clid = Config().feefie_client_id
-                       user = Config().feefie_client_user
-               except:
-                       clid = 'planetstack_core_team'
-                       user = 'pl'
+    def __init__(self,user=None,clientid=None):
+        try:
+            clid = Config().feefie_client_id
+            user = Config().feefie_client_user
+        except:
+            clid = 'planetstack_core_team'
+            user = 'pl'
 
-               self.fofum = Fofum(user=user)
-               self.fofum.make(clid)
+        self.fofum = Fofum(user=user)
+        self.fofum.make(clid)
 
-       def fire(self,**args):
-               self.fofum.fire(json.dumps(args))
+    def fire(self,**args):
+        self.fofum.fire(json.dumps(args))
 
 class EventListener:
-       def __init__(self,wake_up=None):
-               self.handler = EventHandler()
-               self.wake_up = wake_up
-
-       def handle_event(self, payload):
-               payload_dict = json.loads(payload)
-
-               try:
-                       deletion = payload_dict['deletion_flag']
-                       if (deletion):
-                               model = payload_dict['model']
-                               pk = payload_dict['pk']
-
-                               for deleter in deleters[model]:
-                                       deleter(pk)
-               except:
-                       deletion = False
-
-               if (not deletion and self.wake_up):
-                       self.wake_up()
-               
-
-       def run(self):
-               # This is our unique client id, to be used when firing and receiving events
-               # It needs to be generated once and placed in the config file
-
-               try:
-                       clid = Config().feefie_client_id
-                       user = Config().feefie_client_user
-               except:
-                       clid = 'planetstack_core_team'
-                       user = 'pl'
-
-               f = Fofum(user=user)
-               
-               listener_thread = threading.Thread(target=f.listen_for_event,args=(clid,self.handle_event))
-               listener_thread.start()
+    def __init__(self,wake_up=None):
+        self.handler = EventHandler()
+        self.wake_up = wake_up
+
+    def handle_event(self, payload):
+        payload_dict = json.loads(payload)
+
+        try:
+            deletion = payload_dict['deletion_flag']
+            if (deletion):
+                model = payload_dict['model']
+                pk = payload_dict['pk']
+
+                for deleter in deleters[model]:
+                    deleter(pk)
+        except:
+            deletion = False
+
+        if (not deletion and self.wake_up):
+            self.wake_up()
+        
+
+    def run(self):
+        # This is our unique client id, to be used when firing and receiving events
+        # It needs to be generated once and placed in the config file
+
+        try:
+            clid = Config().feefie_client_id
+            user = Config().feefie_client_user
+        except:
+            clid = 'planetstack_core_team'
+            user = 'pl'
+
+        f = Fofum(user=user)
+        
+        listener_thread = threading.Thread(target=f.listen_for_event,args=(clid,self.handle_event))
+        listener_thread.start()
index 3ce3c68..51b510f 100644 (file)
@@ -2,16 +2,13 @@ import os
 import base64
 from syncstep import SyncStep
 
-class OpenStackSyncStep:
-       """ PlanetStack Sync step for copying data to OpenStack 
-       """ 
-       
-       def __init__(self, **args):
-               super(SyncStep,self).__init__(**args)
-               return
-
-       
-
-
-       def __call__(self):
-               return self.call()
+class OpenStackSyncStep(SyncStep):
+    """ PlanetStack Sync step for copying data to OpenStack 
+    """ 
+    
+    def __init__(self, **args):
+        SyncStep.__init__(self, **args)
+        return
+
+    def __call__(self, **args):
+        return self.call(**args)
index 6d7adb8..7954426 100644 (file)
@@ -8,3 +8,5 @@ from .sync_slices import SyncSlices
 from .sync_sliver_ips import SyncSliverIps
 from .sync_slivers import SyncSlivers
 from .sync_users import SyncUsers
+from .sync_roles import SyncRoles
+from .garbage_collector import GarbageCollector
index f03577c..5d434a0 100644 (file)
 import os
 import base64
+import traceback
+from collections import defaultdict
+from django.db.models import F, Q
 from planetstack.config import Config
+from util.logger import Logger, logging
+from observer.openstacksyncstep import OpenStackSyncStep
+from core.models import *
 
-class GarbageCollector(SyncStep):
-       requested_interval = 86400
-       provides=[]
+logger = Logger(logfile='observer.log', level=logging.INFO)
 
-       def call(self):
-               pass
-        
+class GarbageCollector(OpenStackSyncStep):
+    requested_interval = 86400
+    provides=[]
+
+    def call(self, **args):
+        try:
+            #self.sync_roles()
+            self.gc_tenants()
+            self.gc_users()
+            self.gc_user_tenant_roles()
+            self.gc_slivers()
+            self.gc_sliver_ips()
+            self.gc_external_routes()
+        except:
+            traceback.print_exc() 
+
+    def gc_roles(self):
+        """
+         all role that don't already exist in keystone. Remove keystone roles that
+        don't exist in planetstack
+        """
+        # sync all roles that don't already in keystone  
+        keystone_roles = self.driver.shell.keystone.roles.findall()
+        keystone_role_names = [kr.name for kr in keystone_roles]
+        pending_roles = Role.objects.all()
+        pending_role_names = [r.role_type for r in pending_roles] 
+        # don't delete roles for now 
+        """ 
+        # delete keystone roles that don't exist in planetstack
+        for keystone_role in keystone_roles:
+            if keystone_role.name == 'admin':
+                continue
+            if keystone_role.name not in pending_role_names:
+                try:
+                    self.driver.delete_role({id: keystone_role.id})
+                except:
+                    traceback.print_exc()
+        """
+
+    def gc_tenants(self):
+        """
+        Remove sites and slices that no don't exist in openstack db if they 
+        have an enacted time (enacted != None).
+        """ 
+        # get all sites that where enacted != null. We can assume these sites
+        # have previously been synced and need to be checed for deletion.
+        sites = Site.objects.filter(enacted__isnull=False)
+        site_dict = {}
+        for site in sites:
+            site_dict[site.login_base] = site
+
+        # get all slices that where enacted != null. We can assume these slices
+        # have previously been synced and need to be checed for deletion.
+        slices = Slice.objects.filter(enacted__isnull=False)
+        slice_dict = {}
+        for slice in slices:
+            slice_dict[slice.name] = slice
+
+        # delete keystone tenants that don't have a site record
+        tenants = self.driver.shell.keystone.tenants.findall()
+        system_tenants = ['admin','service', 'invisible_to_admin']
+        for tenant in tenants:
+            if tenant.name in system_tenants: 
+                continue
+            if tenant.name not in site_dict and tenant.name not in slice_dict:
+                try:
+                    self.driver.delete_tenant(tenant.id)
+                    logger.info("deleted tenant: %s" % (tenant))
+                except:
+                    logger.log_exc("delete tenant failed: %s" % tenant)
+
+
+    def gc_users(self):
+        """
+        Remove users that no don't exist in openstack db if they have an 
+        enacted time (enacted != None).
+        """ 
+        # get all users that where enacted != null. We can assume these users
+        # have previously been synced and need to be checed for deletion.
+        users = User.objects.filter(enacted__isnull=False)
+        user_dict = {}
+        for user in users:
+            user_dict[user.kuser_id] = user
+
+        # delete keystone users that don't have a user record
+        system_users = ['admin', 'nova', 'quantum', 'glance', 'cinder', 'swift', 'service', 'demo']
+        users = self.driver.shell.keystone.users.findall()
+        for user in users:
+            if user.name in system_users:
+                continue
+            if user.id not in user_dict:
+                try:
+                    self.driver.delete_user(user.id)
+                    logger.info("deleted user: %s" % user)
+                except:
+                    logger.log_exc("delete user failed: %s" % user)
+                    
+
+    def gc_user_tenant_roles(self):
+        """
+        Remove roles that don't exist in openstack db if they have 
+        an enacted time (enacted != None).
+        """
+        # get all site privileges and slice memberships that have been enacted 
+        user_tenant_roles = defaultdict(list)
+        for site_priv in SitePrivilege.objects.filter(enacted__isnull=False):
+            user_tenant_roles[(site_priv.user.kuser_id, site_priv.site.tenant_id)].append(site_priv.role.role)
+        for slice_memb in SlicePrivilege.objects.filter(enacted__isnull=False):
+            user_tenant_roles[(slice_memb.user.kuser_id, slice_memb.slice.tenant_id)].append(slice_memb.role.role)  
+        # Some user tenant role aren't stored in planetstack but they must be preserved. 
+        # Role that fall in this category are
+        # 1. Never remove a user's role that their home site
+        # 2. Never remove a user's role at a slice they've created.
+        # Keep track of all roles that must be preserved.     
+        users = User.objects.all()
+        preserved_roles = {}
+        for user in users:
+            tenant_ids = [s['tenant_id'] for s in user.slices.values()]
+            if user.site:
+                tenant_ids.append(user.site.tenant_id) 
+            preserved_roles[user.kuser_id] = tenant_ids
+
+        # begin removing user tenant roles from keystone. This is stored in the 
+        # Metadata table.
+        for metadata in self.driver.shell.keystone_db.get_metadata():
+            # skip admin roles
+            if metadata.user_id == self.driver.admin_user.id:
+                continue
+            # skip preserved tenant ids
+            if metadata.user_id in preserved_roles and \
+               metadata.tenant_id in preserved_roles[metadata.user_id]: 
+                continue           
+            # get roles for user at this tenant
+            user_tenant_role_ids = user_tenant_roles.get((metadata.user_id, metadata.tenant_id), [])
+
+            if user_tenant_role_ids:
+                # The user has roles at the tenant. Check if roles need to 
+                # be updated.
+                user_keystone_role_ids = metadata.data.get('roles', [])
+                for role_id in user_keystone_role_ids:
+                    if role_id not in user_tenant_role_ids: 
+                        user_keystone_role_ids.pop(user_keystone_role_ids.index(role_id))
+            else:
+                # The user has no roles at this tenant. 
+                metadata.data['roles'] = [] 
+            #session.add(metadata)
+            logger.info("pruning metadata for %s at %s" % (metadata.user_id, metadata.tenant_id))
+    def gc_slivers(self):
+        """
+        Remove slivers that no don't exist in openstack db if they have 
+        an enacted time (enacted != None).
+        """
+        # get all slivers where enacted != null. We can assume these users
+        # have previously been synced and need to be checed for deletion.
+        slivers = Sliver.objects.filter(enacted__isnull=False)
+        sliver_dict = {}
+        for sliver in slivers:
+            sliver_dict[sliver.instance_id] = sliver
+
+        # delete sliver that don't have a sliver record
+        ctx = self.driver.shell.nova_db.ctx 
+        instances = self.driver.shell.nova_db.instance_get_all(ctx)
+        for instance in instances:
+            if instance.uuid not in sliver_dict:
+                try:
+                    # lookup tenant and update context  
+                    tenant = self.driver.shell.keystone.tenants.find(id=instance.project_id)
+                    driver = self.driver.client_driver(tenant=tenant.name) 
+                    driver.destroy_instance(instance.uuid)
+                    logger.info("destroyed sliver: %s" % (instance))
+                except:
+                    logger.log_exc("destroy sliver failed: %s" % instance) 
+                
+
+    def gc_sliver_ips(self):
+        """
+        Update ips that have changed.
+        """
+        # fill in null ip addresses
+        slivers = Sliver.objects.filter(ip=None)
+        for sliver in slivers:
+            # update connection
+            driver = self.driver.client_driver(tenant=sliver.slice.name)
+            servers = driver.shell.nova.servers.findall(id=sliver.instance_id)
+            if not servers:
+                continue
+            server = servers[0]
+            ips = server.addresses.get(sliver.slice.name, [])
+            if ips and sliver.ip != ips[0]['addr']:
+                sliver.ip = ips[0]['addr']
+                sliver.save()
+                logger.info("updated sliver ip: %s %s" % (sliver, ips[0]))
+
+    def gc_external_routes(self):
+        pass
index 6c22c8b..334d19d 100644 (file)
@@ -1,20 +1,18 @@
 import os
 import base64
 from planetstack.config import Config
-from observer.syncstep import SyncStep
+from observer.openstacksyncstep import OpenStackSyncStep
 
-class SyncExternalRoutes(SyncStep):
-       # XXX what does this provide?
-       requested_interval = 86400 # This step is slow like a pig. Let's run it infrequently
+class SyncExternalRoutes(OpenStackSyncStep):
+    # XXX what does this provide?
+    provides=[]
+    requested_interval = 86400 # This step is slow like a pig. Let's run it infrequently
 
-       def __init__(self):
-               pass
-
-       def call(self):
-               routes = self.manager.driver.get_external_routes()
-               subnets = self.manager.driver.shell.quantum.list_subnets()['subnets']
-               for subnet in subnets:
-                       try:
-                               self.manager.driver.add_external_route(subnet, routes)
-                       except:
-                               logger.log_exc("failed to add external route for subnet %s" % subnet)
+    def call(self, **args):
+        routes = self.driver.get_external_routes()
+        subnets = self.driver.shell.quantum.list_subnets()['subnets']
+        for subnet in subnets:
+            try:
+                self.driver.add_external_route(subnet, routes)
+            except:
+                logger.log_exc("failed to add external route for subnet %s" % subnet)
index 9e24fae..09dc7ed 100644 (file)
@@ -1,75 +1,79 @@
 import os
 import base64
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.network import *
 
 class SyncNetworkSlivers(OpenStackSyncStep):
-       requested_interval = 3600
-       provides=[NetworkSliver]
+    requested_interval = 3600
+    provides=[NetworkSliver]
 
-       def call(self):
-               networkSlivers = NetworkSliver.objects.all()
-               networkSlivers_by_id = {}
-               networkSlivers_by_port = {}
-               for networkSliver in networkSlivers:
-                       networkSlivers_by_id[networkSliver.id] = networkSliver
-                       networkSlivers_by_port[networkSliver.port_id] = networkSliver
+    def fetch_pending(self):
+        return NetworkSliver.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
 
-               networks = Network.objects.all()
-               networks_by_id = {}
-               for network in networks:
-                       networks_by_id[network.network_id] = network
+    def call(self, failed=[]):
+        networkSlivers = NetworkSliver.objects.all()
+        networkSlivers_by_id = {}
+        networkSlivers_by_port = {}
+        for networkSliver in networkSlivers:
+            networkSlivers_by_id[networkSliver.id] = networkSliver
+            networkSlivers_by_port[networkSliver.port_id] = networkSliver
 
-               slivers = Sliver.objects.all()
-               slivers_by_instance_id = {}
-               for sliver in slivers:
-                       slivers_by_instance_id[sliver.instance_id] = sliver
+        networks = Network.objects.all()
+        networks_by_id = {}
+        for network in networks:
+            networks_by_id[network.network_id] = network
 
-               ports = self.manager.driver.shell.quantum.list_ports()["ports"]
-               for port in ports:
-                       if port["id"] in networkSlivers_by_port:
-                               # we already have it
-                               print "already accounted for port", port["id"]
-                               continue
+        slivers = Sliver.objects.all()
+        slivers_by_instance_id = {}
+        for sliver in slivers:
+            slivers_by_instance_id[sliver.instance_id] = sliver
 
-                       if port["device_owner"] != "compute:nova":
-                               # we only want the ports that connect to instances
-                               continue
+        ports = self.driver.shell.quantum.list_ports()["ports"]
+        for port in ports:
+            if port["id"] in networkSlivers_by_port:
+                # we already have it
+                print "already accounted for port", port["id"]
+                continue
 
-                       network = networks_by_id.get(port['network_id'], None)
-                       if not network:
-                               #print "no network for port", port["id"], "network", port["network_id"]
-                               continue
+            if port["device_owner"] != "compute:nova":
+                # we only want the ports that connect to instances
+                continue
 
-                       sliver = slivers_by_instance_id.get(port['device_id'], None)
-                       if not sliver:
-                               print "no sliver for port", port["id"], "device_id", port['device_id']
-                               continue
+            network = networks_by_id.get(port['network_id'], None)
+            if not network:
+                #print "no network for port", port["id"], "network", port["network_id"]
+                continue
 
-                       if network.template.sharedNetworkId is not None:
-                               # If it's a shared network template, then more than one network
-                               # object maps to the quantum network. We have to do a whole bunch
-                               # of extra work to find the right one.
-                               networks = network.template.network_set.all()
-                               network = None
-                               for candidate_network in networks:
-                                       if (candidate_network.owner == sliver.slice):
-                                               print "found network", candidate_network
-                                               network = candidate_network
+            sliver = slivers_by_instance_id.get(port['device_id'], None)
+            if not sliver:
+                print "no sliver for port", port["id"], "device_id", port['device_id']
+                continue
 
-                               if not network:
-                                       print "failed to find the correct network for a shared template for port", port["id"], "network", port["network_id"]
-                                       continue
+            if network.template.sharedNetworkId is not None:
+                # If it's a shared network template, then more than one network
+                # object maps to the quantum network. We have to do a whole bunch
+                # of extra work to find the right one.
+                networks = network.template.network_set.all()
+                network = None
+                for candidate_network in networks:
+                    if (candidate_network.owner == sliver.slice):
+                        print "found network", candidate_network
+                        network = candidate_network
 
-                       if not port["fixed_ips"]:
-                               print "port", port["id"], "has no fixed_ips"
-                               continue
+                if not network:
+                    print "failed to find the correct network for a shared template for port", port["id"], "network", port["network_id"]
+                    continue
 
-#                       print "XXX", port
+            if not port["fixed_ips"]:
+                print "port", port["id"], "has no fixed_ips"
+                continue
 
-                       ns = NetworkSliver(network=network,
-                                                          sliver=sliver,
-                                                          ip=port["fixed_ips"][0]["ip_address"],
-                                                          port_id=port["id"])
-                       ns.save()
+#             print "XXX", port
+
+            ns = NetworkSliver(network=network,
+                               sliver=sliver,
+                               ip=port["fixed_ips"][0]["ip_address"],
+                               port_id=port["id"])
+            ns.save()
index e64f0a4..82d6bc7 100644 (file)
@@ -1,52 +1,62 @@
 import os
 import base64
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.network import *
 
 class SyncNetworks(OpenStackSyncStep):
-       provides=[Network]
-       requested_interval = 0
-
-       def save_network(self, network):
-               if not network.network_id:
-                       if network.template.sharedNetworkName:
-                               network.network_id = network.template.sharedNetworkId
-                               (network.subnet_id, network.subnet) = self.driver.get_network_subnet(network.network_id)
-                       else:
-                               network_name = network.name
-
-                               # create network
-                               os_network = self.driver.create_network(network_name, shared=True)
-                               network.network_id = os_network['id']
-
-                               # create router
-                               router = self.driver.create_router(network_name)
-                               network.router_id = router['id']
-
-                               # create subnet
-                               next_subnet = self.get_next_subnet()
-                               cidr = str(next_subnet.cidr)
-                               ip_version = next_subnet.version
-                               start = str(next_subnet[2])
-                               end = str(next_subnet[-2])
-                               subnet = self.driver.create_subnet(name=network_name,
-                                                                                                  network_id = network.network_id,
-                                                                                                  cidr_ip = cidr,
-                                                                                                  ip_version = ip_version,
-                                                                                                  start = start,
-                                                                                                  end = end)
-                               network.subnet = cidr
-                               network.subnet_id = subnet['id']
-
-       def sync_record(self, site):
-               if network.owner and network.owner.creator:
-                               try:
-                                       # update manager context
-                                       self.driver.init_caller(network.owner.creator, network.owner.name)
-                                       self.save_network(network)
-                                       logger.info("saved network: %s" % (network))
-                               except Exception,e:
-                                       logger.log_exc("save network failed: %s" % network)     
-                                       raise e
+    provides=[Network]
+    requested_interval = 0
+
+    def fetch_pending(self):
+        return Network.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
+
+    def save_network(self, network):
+        if not network.network_id:
+            if network.template.sharedNetworkName:
+                network.network_id = network.template.sharedNetworkId
+                (network.subnet_id, network.subnet) = self.driver.get_network_subnet(network.network_id)
+            else:
+                network_name = network.name
+
+                # create network
+                os_network = self.driver.create_network(network_name, shared=True)
+                network.network_id = os_network['id']
+
+                # create router
+                router = self.driver.create_router(network_name)
+                network.router_id = router['id']
+
+                # create subnet
+                next_subnet = self.get_next_subnet()
+                cidr = str(next_subnet.cidr)
+                ip_version = next_subnet.version
+                start = str(next_subnet[2])
+                end = str(next_subnet[-2])
+                subnet = self.driver.create_subnet(name=network_name,
+                                                   network_id = network.network_id,
+                                                   cidr_ip = cidr,
+                                                   ip_version = ip_version,
+                                                   start = start,
+                                                   end = end)
+                network.subnet = cidr
+                network.subnet_id = subnet['id']
+                # add subnet as interface to slice's router
+                self.driver.add_router_interface(router['id'], subnet['id'])
+                # add external route
+                self.driver.add_external_route(subnet)
+
+    def sync_record(self, site):
+        if network.owner and network.owner.creator:
+            try:
+                # update manager context
+                real_driver = self.driver
+                self.driver = self.driver.client_driver(network.owner.creator, network.owner.name)
+                self.save_network(network)
+                self.driver = real_driver
+                logger.info("saved network: %s" % (network))
+            except Exception,e:
+                logger.log_exc("save network failed: %s" % network)    
+                raise e
 
diff --git a/planetstack/observer/steps/sync_roles.py b/planetstack/observer/steps/sync_roles.py
new file mode 100644 (file)
index 0000000..6f7373a
--- /dev/null
@@ -0,0 +1,40 @@
+import os
+import base64
+from django.db.models import F, Q
+from planetstack.config import Config
+from observer.openstacksyncstep import OpenStackSyncStep
+from core.models.role import Role
+from core.models.site import SiteRole
+from core.models.slice import SliceRole
+from core.models.deployment import DeploymentRole
+
+class SyncRoles(OpenStackSyncStep):
+    provides=[Role]
+    requested_interval=0
+
+    def fetch_pending(self):
+        site_roles = SiteRole.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None)
+        slice_roles = SliceRole.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
+        deployment_roles = DeploymentRole.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
+
+        roles = []
+        for site_role in site_roles:
+            roles.append(site_role)
+        for slice_role in slice_roles:
+            roles.append(slice_role)
+        for deployment_role in deployment_roles:
+            roles.append(deployment_role)
+
+        return roles
+
+
+    def sync_record(self, role):
+        save_role = False
+        if not role.krole_id:
+            krole = self.driver.create_role(role.role)
+            role.krole_id = krole.id
+            save_role = True
+
+        if (save_role):
+            role.save()
+    
index ac0dbac..e3dde26 100644 (file)
@@ -1,15 +1,19 @@
 import os
 import base64
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.site import *
 
 class SyncSitePrivileges(OpenStackSyncStep):
-       requested_interval=0
+    requested_interval=0
+    provides=[SitePrivilege]
 
-       provides=[SitePrivilege]
-       def sync_record(self, user):
-               if site_priv.user.kuser_id and site_priv.site.tenant_id:
-                       self.driver.add_user_role(site_priv.user.kuser_id,
-                                                                         site_priv.site.tenant_id,
-                                                                         site_priv.role.role_type) 
+    def fetch_pending(self):
+        return SitePrivilege.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
+
+    def sync_record(self, user):
+        if site_priv.user.kuser_id and site_priv.site.tenant_id:
+            self.driver.add_user_role(site_priv.user.kuser_id,
+                                      site_priv.site.tenant_id,
+                                      site_priv.role.role) 
index 1f7a0f8..2013c6d 100644 (file)
@@ -1,29 +1,34 @@
 import os
 import base64
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.site import Site
 
 class SyncSites(OpenStackSyncStep):
-       provides=[Site]
-       requested_interval=0
-       def sync_record(self, site):
-               save_site = False
-               if not site.tenant_id:
-                       tenant = self.driver.create_tenant(tenant_name=site.login_base,
-                                                                                          description=site.name,
-                                                                                          enabled=site.enabled)
-                       site.tenant_id = tenant.id
-                       save_site = True
-                       # XXX - What's caller?
-                       # self.driver.add_user_role(self.caller.kuser_id, tenant.id, 'admin')
+    provides=[Site]
+    requested_interval=0
 
-               # update the record
-               if site.id and site.tenant_id:
-                       self.driver.update_tenant(site.tenant_id,
-                                                                         description=site.name,
-                                                                         enabled=site.enabled)
+    def fetch_pending(self):
+        return Site.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
 
-               if (save_site):
-                       site.save() # 
+    def sync_record(self, site):
+        save_site = False
+        if not site.tenant_id:
+            tenant = self.driver.create_tenant(tenant_name=site.login_base,
+                                               description=site.name,
+                                               enabled=site.enabled)
+            site.tenant_id = tenant.id
+            save_site = True
+            # XXX - What's caller?
+            # self.driver.add_user_role(self.caller.kuser_id, tenant.id, 'admin')
+
+        # update the record
+        if site.id and site.tenant_id:
+            self.driver.update_tenant(site.tenant_id,
+                                      description=site.name,
+                                      enabled=site.enabled)
+
+        if (save_site):
+            site.save() # 
 
index 66953f1..d0936c4 100644 (file)
@@ -1,14 +1,19 @@
 import os
 import base64
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.slice import *
 
 class SyncSliceMemberships(OpenStackSyncStep):
-       requested_interval=0
-       provides=[SliceMembership]
-       def sync_record(self, user):
-               if slice_memb.user.kuser_id and slice_memb.slice.tenant_id:
-                               self.driver.add_user_role(slice_memb.user.kuser_id,
-                                                                                 slice_memb.slice.tenant_id,
-                                                                                 slice_memb.role.role_type)
+    requested_interval=0
+    provides=[SlicePrivilege]
+
+    def fetch_pending(self):
+        return SlicePrivilege.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
+
+    def sync_record(self, user):
+        if slice_memb.user.kuser_id and slice_memb.slice.tenant_id:
+                self.driver.add_user_role(slice_memb.user.kuser_id,
+                                          slice_memb.slice.tenant_id,
+                                          slice_memb.role.role)
index 81ed925..53dc06b 100644 (file)
@@ -1,58 +1,73 @@
 import os
 import base64
+from netaddr import IPAddress, IPNetwork
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.slice import Slice
 
 class SyncSlices(OpenStackSyncStep):
-       provides=[Slice]
-       requested_interval=0
-       def sync_record(self, slice):
-               if not slice.tenant_id:
-                       nova_fields = {'tenant_name': slice.name,
-                                  'description': slice.description,
-                                  'enabled': slice.enabled}
-                       tenant = self.driver.create_tenant(**nova_fields)
-                       slice.tenant_id = tenant.id
-
-                       # XXX give caller an admin role at the tenant they've created
-                       self.driver.add_user_role(self.caller.kuser_id, tenant.id, 'admin')
-
-                       # refresh credentials using this tenant
-                       self.driver.shell.connect(username=self.driver.shell.keystone.username,
-                                                                         password=self.driver.shell.keystone.password,
-                                                                         tenant=tenant.name)
-
-                       # create network
-                       network = self.driver.create_network(slice.name)
-                       slice.network_id = network['id']
-
-                       # create router
-                       router = self.driver.create_router(slice.name)
-                       slice.router_id = router['id']
-
-                       # create subnet
-                       next_subnet = self.get_next_subnet()
-                       cidr = str(next_subnet.cidr)
-                       ip_version = next_subnet.version
-                       start = str(next_subnet[2])
-                       end = str(next_subnet[-2]) 
-                       subnet = self.driver.create_subnet(name=slice.name,
-                                                                                          network_id = network['id'],
-                                                                                          cidr_ip = cidr,
-                                                                                          ip_version = ip_version,
-                                                                                          start = start,
-                                                                                          end = end)
-                       slice.subnet_id = subnet['id']
-                       # add subnet as interface to slice's router
-                       self.driver.add_router_interface(router['id'], subnet['id'])
-                       # add external route
-                       self.driver.add_external_route(subnet)
-
-
-               if slice.id and slice.tenant_id:
-                       self.driver.update_tenant(slice.tenant_id,
-                                                                         description=slice.description,
-                                                                         enabled=slice.enabled)   
-
-               slice.save()
+    provides=[Slice]
+    requested_interval=0
+
+    def fetch_pending(self):
+        return Slice.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
+
+    def get_next_subnet(self):
+        # limit ourself to 10.0.x.x for now
+        valid_subnet = lambda net: net.startswith('10.0')
+        subnets = self.driver.shell.quantum.list_subnets()['subnets']
+        ints = [int(IPNetwork(subnet['cidr']).ip) for subnet in subnets \
+                if valid_subnet(subnet['cidr'])]
+        ints.sort()
+        last_ip = IPAddress(ints[-1])
+        last_network = IPNetwork(str(last_ip) + "/24")
+        next_network = IPNetwork(str(IPAddress(last_network) + last_network.size) + "/24")
+
+    def sync_record(self, slice):
+        if not slice.tenant_id:
+            nova_fields = {'tenant_name': slice.name,
+                   'description': slice.description,
+                   'enabled': slice.enabled}
+            tenant = self.driver.create_tenant(**nova_fields)
+            slice.tenant_id = tenant.id
+
+            # XXX give caller an admin role at the tenant they've created
+            self.driver.add_user_role(slice.creator.kuser_id, tenant.id, 'admin')
+
+            # refresh credentials using this tenant
+            client_driver = self.driver.client_driver(tenant=tenant.name)
+
+            # create network
+            network = client_driver.create_network(slice.name)
+            slice.network_id = network['id']
+
+            # create router
+            router = client_driver.create_router(slice.name)
+            slice.router_id = router['id']
+
+            # create subnet
+            next_subnet = self.get_next_subnet()
+            cidr = str(next_subnet.cidr)
+            ip_version = next_subnet.version
+            start = str(next_subnet[2])
+            end = str(next_subnet[-2]) 
+            subnet = client_driver.create_subnet(name=slice.name,
+                                               network_id = network['id'],
+                                               cidr_ip = cidr,
+                                               ip_version = ip_version,
+                                               start = start,
+                                               end = end)
+            slice.subnet_id = subnet['id']
+            # add subnet as interface to slice's router
+            client_driver.add_router_interface(router['id'], subnet['id'])
+            # add external route
+            client_driver.add_external_route(subnet)
+
+
+        if slice.id and slice.tenant_id:
+            client_driver.update_tenant(slice.tenant_id,
+                                      description=slice.description,
+                                      enabled=slice.enabled)   
+
+        slice.save()
index d69fd5d..2d7f1f8 100644 (file)
@@ -1,25 +1,27 @@
 import os
 import base64
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.sliver import Sliver
 
 class SyncSliverIps(OpenStackSyncStep):
-       provides=[Sliver]
-       requested_interval=0
-       def fetch_pending(self):
-               slivers = Sliver.objects.filter(ip=None)
-               return slivers
+    provides=[Sliver]
+    requested_interval=0
 
-       def sync_record(self, sliver):
-               self.manager.init_admin(tenant=sliver.slice.name)
-               servers = self.manager.driver.shell.nova.servers.findall(id=sliver.instance_id)
-               if not servers:
-                       return
-               server = servers[0]
-               ips = server.addresses.get(sliver.slice.name, [])
-               if not ips:
-                       return
-               sliver.ip = ips[0]['addr']
-               sliver.save()
-               logger.info("saved sliver ip: %s %s" % (sliver, ips[0]))
+    def fetch_pending(self):
+        slivers = Sliver.objects.filter(ip=None)
+        return slivers
+
+    def sync_record(self, sliver):
+        driver = self.driver.client_driver(tenant=sliver.slice.name)  
+        servers = driver.shell.nova.servers.findall(id=sliver.instance_id)
+        if not servers:
+            return
+        server = servers[0]
+        ips = server.addresses.get(sliver.slice.name, [])
+        if not ips:
+            return
+        sliver.ip = ips[0]['addr']
+        sliver.save()
+        logger.info("saved sliver ip: %s %s" % (sliver, ips[0]))
index adab39d..900840d 100644 (file)
@@ -1,29 +1,34 @@
 import os
 import base64
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.sliver import Sliver
 
 class SyncSlivers(OpenStackSyncStep):
-       provides=[Sliver]
-       requested_interval=0
-       def sync_record(self, slice):
-               if not sliver.instance_id:
-                               nics = self.get_requested_networks(sliver.slice)
-                               file("/tmp/scott-manager","a").write("slice: %s\nreq: %s\n" % (str(sliver.slice.name), str(nics)))
-                               slice_memberships = SliceMembership.objects.filter(slice=sliver.slice)
-                               pubkeys = [sm.user.public_key for sm in slice_memberships if sm.user.public_key]
-                               pubkeys.append(sliver.creator.public_key)
-                               instance = self.driver.spawn_instance(name=sliver.name,
-                                                                       key_name = sliver.creator.keyname,
-                                                                       image_id = sliver.image.image_id,
-                                                                       hostname = sliver.node.name,
-                                                                       pubkeys = pubkeys,
-                                                                       nics = nics )
-                               sliver.instance_id = instance.id
-                               sliver.instance_name = getattr(instance, 'OS-EXT-SRV-ATTR:instance_name')
+    provides=[Sliver]
+    requested_interval=0
 
-               if sliver.instance_id and ("numberCores" in sliver.changed_fields):
-                       self.driver.update_instance_metadata(sliver.instance_id, {"cpu_cores": str(sliver.numberCores)})
+    def fetch_pending(self):
+        return Sliver.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
 
-               sliver.save()   
+    def sync_record(self, slice):
+        if not sliver.instance_id:
+            nics = self.get_requested_networks(sliver.slice)
+            file("/tmp/scott-manager","a").write("slice: %s\nreq: %s\n" % (str(sliver.slice.name), str(nics)))
+            slice_memberships = SliceMembership.objects.filter(slice=sliver.slice)
+            pubkeys = [sm.user.public_key for sm in slice_memberships if sm.user.public_key]
+            pubkeys.append(sliver.creator.public_key)
+            instance = self.driver.spawn_instance(name=sliver.name,
+                                key_name = sliver.creator.keyname,
+                                image_id = sliver.image.image_id,
+                                hostname = sliver.node.name,
+                                pubkeys = pubkeys,
+                                nics = nics )
+            sliver.instance_id = instance.id
+            sliver.instance_name = getattr(instance, 'OS-EXT-SRV-ATTR:instance_name')
+
+        if sliver.instance_id and ("numberCores" in sliver.changed_fields):
+            self.driver.update_instance_metadata(sliver.instance_id, {"cpu_cores": str(sliver.numberCores)})
+
+        sliver.save()    
index 3f509ef..25f093e 100644 (file)
@@ -1,35 +1,42 @@
 import os
 import base64
+import hashlib
+from django.db.models import F, Q
 from planetstack.config import Config
 from observer.openstacksyncstep import OpenStackSyncStep
 from core.models.user import User
 
 class SyncUsers(OpenStackSyncStep):
-       provides=[User]
-       requested_interval=0
-       def sync_record(self, user):
-               name = user.email[:user.email.find('@')]
-               user_fields = {'name': name,
-                                          'email': user.email,
-                                          'password': hashlib.md5(user.password).hexdigest()[:6],
-                                          'enabled': True}
-               if not user.kuser_id:
-                       keystone_user = self.driver.create_user(**user_fields)
-                       user.kuser_id = keystone_user.id
-               else:
-                       self.driver.update_user(user.kuser_id, user_fields)             
+    provides=[User]
+    requested_interval=0
 
-               if user.site:
-                       self.driver.add_user_role(user.kuser_id, user.site.tenant_id, 'user')
-                       if user.is_admin:
-                               self.driver.add_user_role(user.kuser_id, user.site.tenant_id, 'admin')
-                       else:
-                               # may have admin role so attempt to remove it
-                               self.driver.delete_user_role(user.kuser_id, user.site.tenant_id, 'admin')
+    def fetch_pending(self):
+        return User.objects.filter(Q(enacted__lt=F('updated')) | Q(enacted=None))
 
-               if user.public_key:
-                       self.init_caller(user, user.site.login_base)
-                       self.save_key(user.public_key, user.keyname)
-                       self.init_admin()
+    def sync_record(self, user):
+        name = user.email[:user.email.find('@')]
+        user_fields = {'name': name,
+                       'email': user.email,
+                       'password': hashlib.md5(user.password).hexdigest()[:6],
+                       'enabled': True}
+        if not user.kuser_id:
+            keystone_user = self.driver.create_user(**user_fields)
+            user.kuser_id = keystone_user.id
+        else:
+            self.driver.update_user(user.kuser_id, user_fields)        
 
-               user.save()
+        if user.site:
+            self.driver.add_user_role(user.kuser_id, user.site.tenant_id, 'user')
+            if user.is_admin:
+                self.driver.add_user_role(user.kuser_id, user.site.tenant_id, 'admin')
+            else:
+                # may have admin role so attempt to remove it
+                self.driver.delete_user_role(user.kuser_id, user.site.tenant_id, 'admin')
+
+        if user.public_key:
+            driver = self.driver.client_driver(caller=user, tenant=user.site.login_base) 
+            key_fields =  {'name': user.keyname,
+                           'public_key': user.public_key}
+            driver.create_keypair(**key_fields)
+
+        user.save()
index 68e9f99..4116d65 100644 (file)
@@ -1,61 +1,65 @@
 import os
 import base64
+from datetime import datetime
 from planetstack.config import Config
 
 class FailedDependency(Exception):
-       pass
+    pass
 
 class SyncStep:
-       """ A PlanetStack Sync step. 
-
-       Attributes:
-               psmodel         Model name the step synchronizes 
-               dependencies    list of names of models that must be synchronized first if the current model depends on them
-       """ 
-       slow=False
-       def get_prop(prop):
-               try:
-                       sync_config_dir = Config().sync_config_dir
-               except:
-                       sync_config_dir = '/etc/planetstack/sync'
-               prop_config_path = '/'.join(sync_config_dir,self.name,prop)
-               return open(prop_config_path).read().rstrip()
-
-       def __init__(self, **args):
-               """Initialize a sync step
-                  Keyword arguments:
-                               name -- Name of the step
-                               provides -- PlanetStack models sync'd by this step
-               """
-               dependencies = []
-               try:
-                       self.soft_deadline = int(self.get_prop('soft_deadline_seconds'))
-               except:
-                       self.soft_deadline = 5 # 5 seconds
-
-               return
-
-       def fetch_pending(self):
-               return Sliver.objects.filter(ip=None)
-       
-       def check_dependencies(self, obj):
-               for dep in self.dependencies:
-                       peer_object = getattr(obj, dep.name.lowercase())
-                       if (peer_object.pk==dep.pk):
-                               raise DependencyFailed
-
-       def call(self, failed=[]):
-               pending = self.fetch_pending()
-               for o in pending:
-                       if (not self.depends_on(o, failed)):
-                               try:
-                                       check_dependencies(o) # Raises exception if failed                                      
-                                       self.sync_record(o)
-                                       o.enacted = datetime.now() # Is this the same timezone? XXX
-                                       o.save(update_fields=['enacted'])
-                               except:
-                                       failed.append(o)
-               return failed
-
-       def __call__(self):
-               return self.call()
+    """ A PlanetStack Sync step. 
+
+    Attributes:
+        psmodel        Model name the step synchronizes 
+        dependencies    list of names of models that must be synchronized first if the current model depends on them
+    """ 
+    slow=False
+    def get_prop(prop):
+        try:
+            sync_config_dir = Config().sync_config_dir
+        except:
+            sync_config_dir = '/etc/planetstack/sync'
+        prop_config_path = '/'.join(sync_config_dir,self.name,prop)
+        return open(prop_config_path).read().rstrip()
+
+    def __init__(self, **args):
+        """Initialize a sync step
+           Keyword arguments:
+                   name -- Name of the step
+                provides -- PlanetStack models sync'd by this step
+        """
+        dependencies = []
+        self.driver = args.get('driver')
+        try:
+            self.soft_deadline = int(self.get_prop('soft_deadline_seconds'))
+        except:
+            self.soft_deadline = 5 # 5 seconds
+
+        return
+
+    def fetch_pending(self):
+        return []
+        #return Sliver.objects.filter(ip=None)
+    
+    def check_dependencies(self, obj, failed):
+        for dep in self.dependencies:
+            peer_object = getattr(obj, dep.lower())
+            if (peer_object.pk==failed.pk):
+                raise DependencyFailed
+
+    def call(self, failed=[]):
+        pending = self.fetch_pending()
+        for o in pending:
+            try:
+                for f in failed:
+                    self.check_dependencies(o,f) # Raises exception if failed                    
+                self.sync_record(o)
+                o.enacted = datetime.now() # Is this the same timezone? XXX
+                o.save(update_fields=['enacted'])
+            except:
+                failed.append(o)
+
+        return failed
+
+    def __call__(self, **args):
+        return self.call(**args)
index 34bf6f5..959cea3 100755 (executable)
@@ -9,7 +9,15 @@ import json
 from datetime import datetime
 from collections import defaultdict
 
-def toposort(g, steps):
+def toposort(g, steps=None):
+       if (not steps):
+               keys = set(g.keys())
+               values = set({})
+               for v in g.values():
+                       values=values | set(v)
+               
+               steps=list(keys|values)
+
        reverse = {}
 
        for k,v in g.items():
@@ -24,15 +32,15 @@ def toposort(g, steps):
                if not reverse.has_key(k):
                        sources.append(k)
 
-
        for k,v in reverse.iteritems():
                if (not v):
                        sources.append(k)
 
        order = []
        marked = []
+
        while sources:
-               n = sources.pop()
+               n = sources.pop(0)
                try:
                        for m in g[n]:
                                if m not in marked:
@@ -43,6 +51,12 @@ def toposort(g, steps):
                if (n in steps):
                        order.append(n)
 
+       order.reverse()
+
        return order
 
-print toposort({'a':'b','b':'c','c':'d','d':'c'},['d','c','b','a'])
+graph_file=open('model-deps').read()
+g = json.loads(graph_file)
+print toposort(g)
+
+#print toposort({'a':'b','b':'c','c':'d','d':'c'},['d','c','b','a'])
index 0e5fbf0..8224c17 100644 (file)
@@ -1,6 +1,14 @@
 import commands
+import hashlib
 from planetstack.config import Config
-from openstack.client import OpenStackClient
+
+try:
+    from openstack.client import OpenStackClient
+    has_openstack = True
+except:
+    has_openstack = False
+
+manager_enabled = Config().api_nova_enabled
 
 class OpenStackDriver:
 
@@ -18,6 +26,24 @@ class OpenStackDriver:
         else:
             self.shell = OpenStackClient()
 
+        self.enabled = manager_enabled
+        self.has_openstack = has_openstack
+
+    def client_driver(self, caller=None, tenant=None):
+        if caller:
+            auth = {'username': caller.email,
+                    'password': hashlib.md5(caller.password).hexdigest()[:6],
+                    'tenant': tenant}
+            client = OpenStackClient(**auth)
+        else:
+            client = OpenStackClient(tenant=tenant)
+        driver = OpenStackDriver(client=client)
+        return driver
+
+    def admin_driver(self, tenant=None):
+        client = OpenStackClient(tenant=tenant)
+        driver = OpenStackDriver(client=client) 
+
     def create_role(self, name):
         roles = self.shell.keystone.roles.findall(name=name)
         if not roles:
@@ -358,18 +384,18 @@ class OpenStackDriver:
 
         return (subnet_id, subnet)
 
-    def spawn_instance(self, name, key_name=None, hostname=None, image_id=None, security_group=None, pubkeys=[], nics=None):
+    def spawn_instance(self, name, key_name=None, hostname=None, image_id=None, security_group=None, pubkeys=[], nics=None, metadata=None):
         flavor_name = self.config.nova_default_flavor
         flavor = self.shell.nova.flavors.find(name=flavor_name)
         #if not image:
         #    image = self.config.nova_default_imave
         if not security_group:
-            security_group = self.config.nova_default_security_group 
+            security_group = self.config.nova_default_security_group
 
         files = {}
-        if pubkeys:    
+        if pubkeys:
             files['/root/.ssh/authorized_keys'] = "\n".join(pubkeys)
-       
+
         hints = {}
         availability_zone = None
         if hostname:
@@ -383,7 +409,8 @@ class OpenStackDriver:
                                             files=files,
                                             scheduler_hints=hints,
                                             availability_zone=availability_zone,
-                                            nics=nics)
+                                            nics=nics,
+                                            meta=metadata)
         return server
 
     def destroy_instance(self, id):
index 2fb4ff8..9ede33f 100644 (file)
@@ -315,9 +315,22 @@ class OpenStackManager:
 
     @require_enabled
     def save_sliver(self, sliver):
+        metadata_update = {}
+        if ("numberCores" in sliver.changed_fields):
+            metadata_update["cpu_cores"] = str(sliver.numberCores)
+
+        for tag in sliver.slice.tags.all():
+            if tag.name.startswith("sysctl-"):
+                metadata_update[tag.name] = tag.value
+
         if not sliver.instance_id:
             nics = self.get_requested_networks(sliver.slice)
-            file("/tmp/scott-manager","a").write("slice: %s\nreq: %s\n" % (str(sliver.slice.name), str(nics)))
+            for nic in nics:
+                # If a network hasn't been instantiated yet, then we'll fail
+                # during slice creation. Defer saving the sliver for now.
+                if not nic.get("net-id", None):
+                    sliver.save()   # in case it hasn't been saved yet
+                    return
             slice_memberships = SliceMembership.objects.filter(slice=sliver.slice)
             pubkeys = [sm.user.public_key for sm in slice_memberships if sm.user.public_key]
             pubkeys.append(sliver.creator.public_key)
@@ -326,12 +339,13 @@ class OpenStackManager:
                                    image_id = sliver.image.image_id,
                                    hostname = sliver.node.name,
                                    pubkeys = pubkeys,
-                                   nics = nics )
+                                   nics = nics,
+                                   metadata = metadata_update )
             sliver.instance_id = instance.id
             sliver.instance_name = getattr(instance, 'OS-EXT-SRV-ATTR:instance_name')
-
-        if sliver.instance_id and ("numberCores" in sliver.changed_fields):
-            self.driver.update_instance_metadata(sliver.instance_id, {"cpu_cores": str(sliver.numberCores)})
+        else:
+            if metadata_update:
+                self.driver.update_instance_metadata(sliver.instance_id, metadata_update)
 
         sliver.save()
         sliver.enacted = datetime.now()
index deaf2e3..6e0b26c 100644 (file)
@@ -29,4 +29,4 @@ default_flavor=m1.small
 default_security_group=default
 
 [observer]
-pl_dependency_graph='/opt/planetstack/model-deps'
+pl_dependency_graph=/opt/planetstack/model-deps
diff --git a/planetstack/tools/openstack-healthcheck.py b/planetstack/tools/openstack-healthcheck.py
new file mode 100755 (executable)
index 0000000..63534c8
--- /dev/null
@@ -0,0 +1,57 @@
+#! /usr/bin/python
+
+"""
+    Check the status of libvirt, openstack-nova-compute, and
+    quantum-openvswitch-agent. If these services are enabled and have failed,
+    then restart them.
+"""
+
+import os
+import sys
+import subprocess
+import time
+
+def get_systemd_status(service):
+    p=subprocess.Popen(["/bin/systemctl", "is-active", service], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    (out, err) = p.communicate()
+    out = out.strip()
+    return out
+
+libvirt_enabled = os.system("systemctl -q is-enabled libvirtd.service")==0
+nova_compute_enabled = os.system("systemctl -q is-enabled openstack-nova-compute.service")==0
+openvswitch_agent_enabled = os.system("systemctl -q is-enabled quantum-openvswitch-agent.service")==0
+
+print "enabled:"
+print "  libvirtd=", libvirt_enabled
+print "  openstack-nova-compute=", nova_compute_enabled
+print "  quantum-openvswitch-agent=", openvswitch_agent_enabled
+
+if (not libvirt_enabled) or (not nova_compute_enabled) or (not openvswitch_agent_enabled):
+    print "services are not enabled. exiting"
+    sys.exit(0)
+
+libvirt_status = get_systemd_status("libvirtd.service")
+nova_compute_status = get_systemd_status("openstack-nova-compute.service")
+openvswitch_agent_status = get_systemd_status("quantum-openvswitch-agent.service")
+
+print "status:"
+print "  libvirtd=", libvirt_status
+print "  openstack-nova-compute=", nova_compute_status
+print "  quantum-openvswitch-agent=", openvswitch_agent_status
+
+if (libvirt_status=="failed") or (nova_compute_status=="failed") or (openvswitch_agent_status=="failed"):
+    print "services have failed. doing the big restart"
+    os.system("systemctl stop openstack-nova-compute.service")
+    os.system("systemctl stop quantum-openvswitch-agent.service")
+    os.system("systemctl stop libvirtd.service")
+    time.sleep(5)
+    os.system("systemctl start libvirtd.service")
+    time.sleep(5)
+    os.system("systemctl start quantum-openvswitch-agent.service")
+    time.sleep(5)
+    os.system("systemctl start openstack-nova-compute.service")
+    print "done"
+
+
+
+
diff --git a/planetstack/util/logger.py b/planetstack/util/logger.py
new file mode 100644 (file)
index 0000000..91e47d0
--- /dev/null
@@ -0,0 +1,205 @@
+#!/usr/bin/python
+
+#----------------------------------------------------------------------
+# Copyright (c) 2008 Board of Trustees, Princeton University
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and/or hardware specification (the "Work") to
+# deal in the Work without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Work, and to permit persons to whom the Work
+# is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Work.
+#
+# THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
+# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+# OUT OF OR IN CONNECTION WITH THE WORK OR THE USE OR OTHER DEALINGS 
+# IN THE WORK.
+#----------------------------------------------------------------------
+
+import os, sys
+import traceback
+import logging, logging.handlers
+
+CRITICAL=logging.CRITICAL
+ERROR=logging.ERROR
+WARNING=logging.WARNING
+INFO=logging.INFO
+DEBUG=logging.DEBUG
+
+# a logger that can handle tracebacks 
+class Logger:
+    def __init__ (self,logfile=None,loggername=None,level=logging.INFO):
+        # default is to locate loggername from the logfile if avail.
+        if not logfile:
+            #loggername='console'
+            #handler=logging.StreamHandler()
+            #handler.setFormatter(logging.Formatter("%(levelname)s %(message)s"))
+            logfile = "/var/log/planetstack.log"
+
+        if not loggername:
+            loggername=os.path.basename(logfile)
+        try:
+            handler=logging.handlers.RotatingFileHandler(logfile,maxBytes=1000000, backupCount=5) 
+        except IOError:
+            # This is usually a permissions error becaue the file is
+            # owned by root, but httpd is trying to access it.
+            tmplogfile=os.getenv("TMPDIR", "/tmp") + os.path.sep + os.path.basename(logfile)
+            # In strange uses, 2 users on same machine might use same code,
+            # meaning they would clobber each others files
+            # We could (a) rename the tmplogfile, or (b)
+            # just log to the console in that case.
+            # Here we default to the console.
+            if os.path.exists(tmplogfile) and not os.access(tmplogfile,os.W_OK):
+                loggername = loggername + "-console"
+                handler = logging.StreamHandler()
+            else:
+                handler=logging.handlers.RotatingFileHandler(tmplogfile,maxBytes=1000000, backupCount=5) 
+        handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
+        self.logger=logging.getLogger(loggername)
+        self.logger.setLevel(level)
+        # check if logger already has the handler we're about to add
+        handler_exists = False
+        for l_handler in self.logger.handlers:
+            if l_handler.baseFilename == handler.baseFilename and \
+               l_handler.level == handler.level:
+                handler_exists = True 
+
+        if not handler_exists:
+            self.logger.addHandler(handler)
+
+        self.loggername=loggername
+
+    def setLevel(self,level):
+        self.logger.setLevel(level)
+
+    # shorthand to avoid having to import logging all over the place
+    def setLevelDebug(self):
+        self.logger.setLevel(logging.DEBUG)
+
+    def debugEnabled (self):
+        return self.logger.getEffectiveLevel() == logging.DEBUG
+
+    # define a verbose option with s/t like
+    # parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0)
+    # and pass the coresponding options.verbose to this method to adjust level
+    def setLevelFromOptVerbose(self,verbose):
+        if verbose==0:
+            self.logger.setLevel(logging.WARNING)
+        elif verbose==1:
+            self.logger.setLevel(logging.INFO)
+        elif verbose>=2:
+            self.logger.setLevel(logging.DEBUG)
+    # in case some other code needs a boolean
+    def getBoolVerboseFromOpt(self,verbose):
+        return verbose>=1
+    def getBoolDebugFromOpt(self,verbose):
+        return verbose>=2
+
+    ####################
+    def info(self, msg):
+        self.logger.info(msg)
+
+    def debug(self, msg):
+        self.logger.debug(msg)
+        
+    def warn(self, msg):
+        self.logger.warn(msg)
+
+    # some code is using logger.warn(), some is using logger.warning()
+    def warning(self, msg):
+        self.logger.warning(msg)
+   
+    def error(self, msg):
+        self.logger.error(msg)    
+    def critical(self, msg):
+        self.logger.critical(msg)
+
+    # logs an exception - use in an except statement
+    def log_exc(self,message):
+        self.error("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n"))
+        self.error("%s END TRACEBACK"%message)
+    
+    def log_exc_critical(self,message):
+        self.critical("%s BEG TRACEBACK"%message+"\n"+traceback.format_exc().strip("\n"))
+        self.critical("%s END TRACEBACK"%message)
+    
+    # for investigation purposes, can be placed anywhere
+    def log_stack(self,message):
+        to_log="".join(traceback.format_stack())
+        self.info("%s BEG STACK"%message+"\n"+to_log)
+        self.info("%s END STACK"%message)
+
+    def enable_console(self, stream=sys.stdout):
+        formatter = logging.Formatter("%(message)s")
+        handler = logging.StreamHandler(stream)
+        handler.setFormatter(formatter)
+        self.logger.addHandler(handler)
+
+
+info_logger = Logger(loggername='info', level=logging.INFO)
+debug_logger = Logger(loggername='debug', level=logging.DEBUG)
+warn_logger = Logger(loggername='warning', level=logging.WARNING)
+error_logger = Logger(loggername='error', level=logging.ERROR)
+critical_logger = Logger(loggername='critical', level=logging.CRITICAL)
+logger = info_logger
+########################################
+import time
+
+def profile(logger):
+    """
+    Prints the runtime of the specified callable. Use as a decorator, e.g.,
+    
+    @profile(logger)
+    def foo(...):
+        ...
+    """
+    def logger_profile(callable):
+        def wrapper(*args, **kwds):
+            start = time.time()
+            result = callable(*args, **kwds)
+            end = time.time()
+            args = map(str, args)
+            args += ["%s = %s" % (name, str(value)) for (name, value) in kwds.iteritems()]
+            # should probably use debug, but then debug is not always enabled
+            logger.info("PROFILED %s (%s): %.02f s" % (callable.__name__, ", ".join(args), end - start))
+            return result
+        return wrapper
+    return logger_profile
+
+
+if __name__ == '__main__': 
+    print 'testing logging into logger.log'
+    logger1=Logger('logger.log', loggername='std(info)')
+    logger2=Logger('logger.log', loggername='error', level=logging.ERROR)
+    logger3=Logger('logger.log', loggername='debug', level=logging.DEBUG)
+    
+    for (logger,msg) in [ (logger1,"std(info)"),(logger2,"error"),(logger3,"debug")]:
+        
+        print "====================",msg, logger.logger.handlers
+   
+        logger.enable_console()
+        logger.critical("logger.critical")
+        logger.error("logger.error")
+        logger.warn("logger.warning")
+        logger.info("logger.info")
+        logger.debug("logger.debug")
+        logger.setLevel(logging.DEBUG)
+        logger.debug("logger.debug again")
+    
+        @profile(logger)
+        def sleep(seconds = 1):
+            time.sleep(seconds)
+
+        logger.info('console.info')
+        sleep(0.5)
+        logger.setLevel(logging.DEBUG)
+        sleep(0.25)
+