blob: dc8cab6dfa4e3720a19d9bbd6768171a01f7d5c4 [file] [log] [blame]
# Copyright 2011 James McCauley
#
# This file is part of POX.
#
# POX is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# POX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with POX. If not, see <http://www.gnu.org/licenses/>.
"""
Some of POX's core API and functionality is here, largely in the POXCore
class (an instance of which is available as pox.core.core).
This includes things like component rendezvous, logging, system status
(up and down events), etc.
"""
# Set up initial log state
import logging
import inspect
import time
import os
_path = inspect.stack()[0][1]
_ext_path = _path[0:_path.rindex(os.sep)]
_ext_path = os.path.dirname(_ext_path) + os.sep
_path = os.path.dirname(_path) + os.sep
SQUELCH_TIME = 5
_squelch = ''
_squelchTime = 0
_squelchCount = 0
def getLogger (name=None, moreFrames=0):
"""
In general, you don't need to call this directly, and will use
core.getLogger() instead.
"""
if name is None:
s = inspect.stack()[1+moreFrames]
name = s[1]
if name.endswith('.py'):
name = name[0:-3]
elif name.endswith('.pyc'):
name = name[0:-4]
if name.startswith(_path):
name = name[len(_path):]
elif name.startswith(_ext_path):
name = name[len(_ext_path):]
name = name.replace('/', '.').replace('\\', '.') #FIXME: use os.path or whatever
# Remove double names ("topology.topology" -> "topology")
if name.find('.') != -1:
n = name.split('.')
if len(n) >= 2:
if n[-1] == n[-2]:
del n[-1]
name = '.'.join(n)
if name.endswith(".__init__"):
name = name.rsplit(".__init__",1)[0]
l = logging.getLogger(name)
g=globals()
if not hasattr(l, "print"):
def printmsg (*args, **kw):
#squelch = kw.get('squelch', True)
msg = ' '.join((str(s) for s in args))
s = inspect.stack()[1]
o = '['
if 'self' in s[0].f_locals:
o += s[0].f_locals['self'].__class__.__name__ + '.'
o += s[3] + ':' + str(s[2]) + '] '
o += msg
if o == _squelch:
if time.time() >= _squelchTime:
l.debug("[Previous message repeated %i more times]" % (g['_squelchCount']+1,))
g['_squelchCount'] = 0
g['_squelchTime'] = time.time() + SQUELCH_TIME
else:
g['_squelchCount'] += 1
else:
g['_squelch'] = o
if g['_squelchCount'] > 0:
l.debug("[Previous message repeated %i more times]" % (g['_squelchCount'],))
g['_squelchCount'] = 0
g['_squelchTime'] = time.time() + SQUELCH_TIME
l.debug(o)
setattr(l, "print", printmsg)
setattr(l, "msg", printmsg)
return l
log = (lambda : getLogger())()
from pox.lib.revent import *
# Now use revent's exception hook to put exceptions in event handlers into
# the log...
def _revent_exception_hook (source, event, args, kw, exc_info):
try:
c = source
t = event
if hasattr(c, "__class__"): c = c.__class__.__name__
if isinstance(t, Event): t = t.__class__.__name__
elif issubclass(t, Event): t = t.__name__
except:
pass
log.exception("Exception while handling %s!%s...\n" % (c,t))
import pox.lib.revent.revent
pox.lib.revent.revent.handleEventException = _revent_exception_hook
class GoingUpEvent (Event):
""" Fired when system is going up. """
pass
class GoingDownEvent (Event):
""" Fired when system is going down. """
pass
class UpEvent (Event):
""" Fired when system is up. """
pass
class DownEvent (Event):
""" Fired when system is down. """
pass
class ComponentRegistered (Event):
"""
This is raised by core whenever a new component is registered.
By watching this, a component can monitor whether other components it
depends on are available.
"""
def __init__ (self, name, component):
Event.__init__(self)
self.name = name
self.component = component
import pox.lib.recoco as recoco
class POXCore (EventMixin):
"""
A nexus of of the POX API.
pox.core.core is a reference to an instance of this class. This class
serves a number of functions.
An important one is that it can serve as a rendezvous point for
components. A component can register objects on core, and they can
then be accessed on the core object (e.g., if you register foo, then
there will then be a pox.core.core.foo). In many cases, this means you
won't need to import a module.
Another purpose to the central registration is that it decouples
functionality from a specific module. If myL2Switch and yourL2Switch
both register as "switch" and both provide the same API, then it doesn't
matter. Doing this with imports is a pain.
Additionally, a number of commmon API functions are vailable here.
"""
_eventMixin_events = set([
UpEvent,
DownEvent,
GoingUpEvent,
GoingDownEvent,
ComponentRegistered
])
def __init__ (self):
self.debug = False
self.running = True
self.components = {}
self.version = (0,0,0)
print "{0} / Copyright 2011 James McCauley".format(self.version_string)
self.scheduler = recoco.Scheduler(daemon=True)
@property
def version_string (self):
return "POX " + '.'.join(map(str, self.version))
def callDelayed (_self, _seconds, _func, *args, **kw):
"""
Calls the function at a later time.
This is just a wrapper around a recoco timer.
"""
t = recoco.Timer(_seconds, _func, args=args, kw=kw,
scheduler = _self.scheduler)
return t
def callLater (_self, _func, *args, **kw):
# first arg is `_self` rather than `self` in case the user wants
# to specify self as a keyword argument
"""
Call the given function with the given arguments within the context
of the co-operative threading environment.
It actually calls it sooner rather than later. ;)
Much of POX is written without locks because it's all thread-safe
with respect to itself, as it's written using the recoco co-operative
threading library. If you have a real thread outside of the
co-operative thread context, you need to be careful about calling
things within it. This function provides a rather simple way that
works for most situations: you give it a callable (like a method)
and some arguments, and it will call that callable with those
arguments from within the co-operative threader, taking care of
synchronization for you.
"""
_self.scheduler.callLater(_func, *args, **kw)
def raiseLater (_self, _obj, *args, **kw):
# first arg is `_self` rather than `self` in case the user wants
# to specify self as a keyword argument
"""
This is similar to callLater(), but provides an easy way to raise a
revent event from outide the co-operative context.
Rather than foo.raiseEvent(BarEvent, baz, spam), you just do
core.raiseLater(foo, BarEvent, baz, spam).
"""
_self.scheduler.callLater(_obj.raiseEvent, *args, **kw)
def getLogger (self, *args, **kw):
"""
Returns a logger. Pass it the name you want if you'd like to specify
one (e.g., core.getLogger("foo")). If you don't specify a name, it
will make one up based on the module name it is called from.
"""
return getLogger(moreFrames=1,*args, **kw)
def quit (self):
"""
Shut down POX.
"""
if self.running:
self.running = False
log.info("Going down...")
import gc
gc.collect()
self.raiseEvent(GoingDownEvent())
self.callLater(self.scheduler.quit)
for i in range(50):
if self.scheduler._hasQuit: break
gc.collect()
time.sleep(.1)
if not self.scheduler._allDone:
log.warning("Scheduler didn't quit in time")
self.raiseEvent(DownEvent())
log.info("Down.")
def goUp (self):
log.debug(self.version_string + " going up...")
import platform
py = "{impl} ({vers}/{build})".format(
impl=platform.python_implementation(),
vers=platform.python_version(),
build=platform.python_build()[1].replace(" "," "))
log.debug("Running on " + py)
self.raiseEvent(GoingUpEvent())
log.info(self.version_string + " is up.")
self.raiseEvent(UpEvent())
def hasComponent (self, name):
"""
Returns True if a component with the given name has been registered.
"""
return name in self.components
def registerNew (self, __componentClass, *args, **kw):
"""
Give it a class (and optional __init__ arguments), and it will
create an instance and register it using the class name. If the
instance has a _core_name property, it will use that instead.
It returns the new instance.
core.registerNew(FooClass, arg) is roughly equivalent to
core.register("FooClass", FooClass(arg)).
"""
name = __componentClass.__name__
obj = __componentClass(*args, **kw)
if hasattr(obj, '_core_name'):
# Default overridden
name = obj._core_name
self.register(name, obj)
return obj
def register (self, name, component):
"""
Makes the object "component" available as pox.core.core.name.
"""
#TODO: weak references?
if name in self.components:
log.warn("Warning: Registered '%s' multipled times" % (name,))
self.components[name] = component
self.raiseEventNoErrors(ComponentRegistered, name, component)
def listenToDependencies(self, sink, components):
"""
If a component depends on having other components
registered with core before it can boot, it can use this method to
check for registration, and listen to events on those dependencies.
Note that event handlers named with the _handle* pattern in the sink must
include the name of the desired source as a prefix. For example, if topology is a
dependency, a handler for topology's SwitchJoin event must be labeled:
def _handle_topology_SwitchJoin(...)
sink - the component waiting on dependencies
components - a list of dependent component names
Returns whether all of the desired components are registered.
"""
if components == None or len(components) == 0:
return True
got = set()
for c in components:
if self.hasComponent(c):
setattr(sink, c, getattr(self, c))
sink.listenTo(getattr(self, c), prefix=c)
got.add(c)
else:
setattr(sink, c, None)
for c in got:
components.remove(c)
if len(components) == 0:
log.debug(sink.__class__.__name__ + " ready")
return True
return False
def __getattr__ (self, name):
if name not in self.components:
raise AttributeError("'%s' not registered" % (name,))
return self.components[name]
core = POXCore()