diff --git a/examples/ripcordtest.py b/examples/ripcordtest.py
index b27b08672161150eb59cb2c5cabefbd7eab2f652..41853b9a152a32da48e2dc7698aa3a788b41c105 100755
--- a/examples/ripcordtest.py
+++ b/examples/ripcordtest.py
@@ -11,44 +11,12 @@
 import sys
 from time import sleep
 
-from mininet.mininet import Switch, Controller, Host, lg
-from mininet.mininet import init, quietRun, checkRun, retry, MOVEINTF_DELAY
-
 from ripcord.topo import FatTreeTopo
 
-def make_veth_pair(intf1, intf2):
-    '''Create a veth pair connecting intf1 and intf2.
-
-    @param intf1 string, interface name
-    @param intf2 string, interface name
-    '''
-    # Delete any old interfaces with the same names
-    quietRun('ip link del ' + intf1)
-    quietRun('ip link del ' + intf2)
-    # Create new pair
-    cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
-    #lg.info('running command: %s\n' % cmd)
-    return checkRun(cmd)
-
-
-def move_intf(intf, node):
-    '''Move interface to node.
-
-    @param intf string interface name
-    @param node Node object
-
-    @return success boolean, did operation complete?
-    '''
-    cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid)
-    #lg.info('running command: %s\n' % cmd)
-    quietRun(cmd)
-    #lg.info(' output: %s\n' % output)
-    links = node.cmd('ip link show')
-    if not intf in links:
-        lg.error('*** Error: move_intf: % not successfully moved to %s:\n' %
-                 (intf, node.name))
-        return False
-    return True
+from mininet.net import Switch, Controller, Host, init
+from mininet.logging_mod import lg, set_loglevel
+from mininet.util import make_veth_pair, move_intf, retry, quietRun
+from mininet.util import MOVEINTF_DELAY
 
 
 class Mininet(object):
@@ -506,6 +474,7 @@ def __init__(self, ip, subnet_size):
 
 
 if __name__ == '__main__':
+    set_loglevel('info')
     init()
     controller_params = ControllerParams(0x0a000000, 8) # 10.0.0.0/8
     mn = Mininet(FatTreeTopo(4), Switch, Host, NOXController,
diff --git a/mininet/cleanup b/mininet/cleanup
index 92a5cd1ff17821adaf1f1e94247c4590bf5aa6c7..8897a5668c156da1773176ac110f09cbb6690339 100755
--- a/mininet/cleanup
+++ b/mininet/cleanup
@@ -12,7 +12,7 @@ irreplaceable!
 from subprocess import Popen, PIPE
 import re
 
-from mininet import quietRun
+from mininet.util import quietRun
 
 def sh( cmd ): 
    "Print a command and send it to the shell"
diff --git a/mininet/logging_mod.py b/mininet/logging_mod.py
index 38035b7a08265babfce00a29fe7b958c84daf638..6c6e024fc3960ed5fec406b59b5eb3102cb0b348 100644
--- a/mininet/logging_mod.py
+++ b/mininet/logging_mod.py
@@ -1,15 +1,33 @@
-# Since StreamHandler automatically adds newlines, define a mod to more easily
-# support interactive mode when we want it, or errors-only logging for running
-# unit tests.
+'''Logging functions for Mininet.'''
 
-from logging import StreamHandler
+import logging
 import types
 
-# Modified from python2.5/__init__.py to not require newlines
-class StreamHandlerNoNewline(StreamHandler):
+LEVELS = {'debug': logging.DEBUG,
+          'info': logging.INFO,
+          'warning': logging.WARNING,
+          'error': logging.ERROR,
+          'critical': logging.CRITICAL}
+
+# change this to logging.INFO to get printouts when running unit tests
+LOG_LEVEL_DEFAULT = logging.WARNING
+
+#default: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+LOG_MSG_FORMAT = '%(message)s'
+
+
+
+# Modified from python2.5/__init__.py
+class StreamHandlerNoNewline(logging.StreamHandler):
+    '''StreamHandler that doesn't print newlines by default.
+
+    Since StreamHandler automatically adds newlines, define a mod to more
+    easily support interactive mode when we want it, or errors-only logging for
+    running unit tests.
+    '''
 
     def emit(self, record):
-        """
+        '''
         Emit a record.
 
         If a formatter is specified, it is used to format the record.
@@ -17,19 +35,65 @@ def emit(self, record):
         [N.B. this may be removed depending on feedback]. If exception
         information is present, it is formatted using
         traceback.print_exception and appended to the stream.
-        """
+        '''
         try:
             msg = self.format(record)
-            fs = "%s" # was "%s\n"
-            if not hasattr(types, "UnicodeType"): #if no unicode support...
+            fs = '%s' # was '%s\n'
+            if not hasattr(types, 'UnicodeType'): #if no unicode support...
                 self.stream.write(fs % msg)
             else:
                 try:
                     self.stream.write(fs % msg)
                 except UnicodeError:
-                    self.stream.write(fs % msg.encode("UTF-8"))
+                    self.stream.write(fs % msg.encode('UTF-8'))
             self.flush()
         except (KeyboardInterrupt, SystemExit):
             raise
         except:
-            self.handleError(record)
\ No newline at end of file
+            self.handleError(record)
+
+
+def set_loglevel(level_name = None):
+    '''Setup loglevel.
+
+    @param level_name level name from LEVELS
+    '''
+    level = LOG_LEVEL_DEFAULT
+    if level_name != None:
+        if level_name not in LEVELS:
+            raise Exception('unknown loglevel seen in set_loglevel')
+        else:
+            level = LEVELS.get(level_name, level)
+
+    lg.setLevel(level)
+    if len(lg.handlers) != 1:
+        raise Exception('lg.handlers length not zero in logging_mod')
+    lg.handlers[0].setLevel(level)
+
+
+def _setup_logging():
+    '''Setup logging for Mininet.'''
+    global lg
+
+    # create logger if first time
+    if 'lg' not in globals():
+        lg = logging.getLogger('mininet')
+        # create console handler
+        ch = StreamHandlerNoNewline()
+        # create formatter
+        formatter = logging.Formatter(LOG_MSG_FORMAT)
+        # add formatter to ch
+        ch.setFormatter(formatter)
+        # add ch to lg
+        lg.addHandler(ch)
+    else:
+        raise Exception('setup_logging called twice')
+
+    set_loglevel()
+
+
+# There has to be some better way to ensure we only ever have one logging
+# variable.  If this check isn't in, the order in which imports occur can
+# affect whether a program runs, because the variable lg may get rebound.
+if 'lg' not in globals():
+    _setup_logging()
diff --git a/mininet/mininet.py b/mininet/net.py
similarity index 87%
rename from mininet/mininet.py
rename to mininet/net.py
index eca9d9b1bc0a29ee4bc4a99fc846335748c0fcb0..674b9e093280f53c59c7a5bc9d59419a8c7edaf7 100755
--- a/mininet/mininet.py
+++ b/mininet/net.py
@@ -67,80 +67,19 @@
 12/13/09 Added support for custom controller and switch classes
 """
 
-from subprocess import call, check_call, Popen, PIPE, STDOUT
+from subprocess import call, Popen, PIPE, STDOUT
 from time import sleep
 import os, re, signal, sys, select
 flush = sys.stdout.flush
 from resource import setrlimit, RLIMIT_NPROC, RLIMIT_NOFILE
 
-import logging
-import sys
-
-from logging_mod import StreamHandlerNoNewline
+from mininet.logging_mod import lg, set_loglevel
+from mininet.util import run, checkRun, quietRun, makeIntfPair, moveIntf
+from mininet.util import createLink
 
 DATAPATHS = ['user', 'kernel']
 
-LEVELS = {'debug': logging.DEBUG,
-          'info': logging.INFO,
-          'warning': logging.WARNING,
-          'error': logging.ERROR,
-          'critical': logging.CRITICAL}
-
-# change this to get printouts when running unit tests
-LOG_LEVEL_DEFAULT = logging.WARNING
-
-#default: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
-LOG_MSG_FORMAT = "%(message)s"
-
-lg = None
-
-def setup_logging(loglevel):
-
-    global lg
 
-    # create logger
-    lg = logging.getLogger("mininet")
-    lg.setLevel(loglevel)
-    # create console handler and set level to debug
-    ch = StreamHandlerNoNewline()
-    ch.setLevel(loglevel)
-    # create formatter
-    formatter = logging.Formatter(LOG_MSG_FORMAT)
-    # add formatter to ch
-    ch.setFormatter(formatter)
-    # add ch to lg
-    lg.addHandler(ch)
-
-
-# Utility routines to make it easier to run commands
-
-def run( cmd ):
-   "Simple interface to subprocess.call()"
-   return call( cmd.split( ' ' ) )
-
-def checkRun( cmd ):
-   "Simple interface to subprocess.check_call()"
-   check_call( cmd.split( ' ' ) )
-   
-def quietRun( cmd ):
-   "Run a command, routing stderr to stdout, and return the output."
-   if isinstance( cmd, str ): cmd = cmd.split( ' ' )
-   popen = Popen( cmd, stdout=PIPE, stderr=STDOUT)
-   # We can't use Popen.communicate() because it uses 
-   # select(), which can't handle
-   # high file descriptor numbers! poll() can, however.
-   output = ''
-   readable = select.poll()
-   readable.register( popen.stdout )
-   while True:
-      while readable.poll(): 
-         data = popen.stdout.read( 1024 )
-         if len( data ) == 0: break
-         output += data
-      popen.poll()
-      if popen.returncode != None: break
-   return output
-   
 class Node( object ):
    """A virtual network node is simply a shell in a network namespace.
       We communicate with it using pipes."""
@@ -361,69 +300,7 @@ def sendCmd( self, cmd ):
    def monitor( self ):
       if not self.execed: return Node.monitor( self )
       else: return True, ''
-         
-# Interface management
-# 
-# Interfaces are managed as strings which are simply the
-# interface names, of the form "nodeN-ethM".
-#
-# To connect nodes, we create a pair of veth interfaces, and then place them
-# in the pair of nodes that we want to communicate. We then update the node's
-# list of interfaces and connectivity map.
-#
-# For the kernel datapath, switch interfaces
-# live in the root namespace and thus do not have to be
-# explicitly moved.
-
-MOVEINTF_DELAY = 0.0001
-
-def makeIntfPair( intf1, intf2 ):
-   "Make a veth pair of intf1 and intf2."
-   # Delete any old interfaces with the same names
-   quietRun( 'ip link del ' + intf1 )
-   quietRun( 'ip link del ' + intf2 )
-   # Create new pair
-   cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
-   return checkRun( cmd )
-   
-def moveIntf( intf, node, print_error = False ):
-   "Move intf to node."
-   cmd = 'ip link set ' + intf + ' netns ' + `node.pid`
-   quietRun( cmd )
-   links = node.cmd( 'ip link show' )
-   if not intf in links:
-      if print_error:
-          lg.error("*** Error: moveIntf: % not successfully moved to %s:\n" %
-                  (intf, node.name))
-      return False
-   return True
-
-def retry( n, retry_delay, fn, *args):
-   '''Try something N times before giving up.
-
-   @param n number of times to retry
-   @param retry_delay seconds wait this long between tries
-   @param fn function to call
-   @param args args to apply to function call
-   '''
-   tries = 0
-   while not apply( fn, args ) and tries < n:
-      sleep( retry_delay )
-      tries += 1
-   if tries >= n: 
-      lg.error("*** gave up after %i retries\n" % tries)
-      exit( 1 )
-   
-def createLink( node1, node2 ):
-   "Create a link node1-intf1 <---> node2-intf2."
-   intf1 = node1.newIntf()
-   intf2 = node2.newIntf()
-   makeIntfPair( intf1, intf2 )
-   if node1.inNamespace: retry( 3, MOVEINTF_DELAY, moveIntf, intf1, node1 )
-   if node2.inNamespace: retry( 3, MOVEINTF_DELAY, moveIntf, intf2, node2 )
-   node1.connection[ intf1 ] = ( node2, intf2 )
-   node2.connection[ intf2 ] = ( node1, intf1 )
-   return intf1, intf2
+
 
 # Handy utilities
  
@@ -506,7 +383,6 @@ def configureRoutedControlNetwork( controller, switches, ips):
       if pingTest( hosts=[ switch, controller ] ) != 0:
          lg.error("*** Error: control network test failed\n")
          exit( 1 )
-   lg.info("\n")
 
 def configHosts( hosts, ips ):
    """Configure a set of hosts, starting at IP address a.b.c.d"""
@@ -578,6 +454,7 @@ def start( self ):
       lg.info("*** Starting %s switches" % len(self.switches))
       for switch in self.switches:
          switch.start( self.controllers[ 0 ] )
+      lg.info("\n")
    def stop( self ):
       """Stop the controller(s), switches and hosts\n"""
       lg.info("*** Stopping hosts\n")
@@ -649,7 +526,6 @@ def treeNet( self, controller, depth, fanout, snames=None,
    def makeNet( self, controller ):
       root, switches, hosts = self.treeNet( controller,
          self.depth, self.fanout )
-      lg.info("\n")
       return switches, hosts
    
 # Grid network
@@ -903,12 +779,10 @@ def init():
    fixLimits()
 
 if __name__ == '__main__':
-   level = logging.INFO
    if len(sys.argv) > 1:
-      level_name = sys.argv[1]
-      level = LEVELS.get(level_name, level)
-   setup_logging(level)
-   #lg.basicConfig(level = level, format = LOG_MSG_FORMAT)
+      set_loglevel(sys.argv[1])
+   else:
+      set_loglevel('info')
 
    init()
    results = {}
@@ -920,7 +794,4 @@ def init():
       network = TreeNet( depth=2, fanout=4, kernel=k)
       result = network.run( pingTestVerbose )
       results[ datapath ] = result
-   lg.info("*** Test results: %s\n", results)
-else:
-   setup_logging(LOG_LEVEL_DEFAULT)
-   #lg.basicConfig(level = LOG_LEVEL_DEFAULT, format = LOG_MSG_FORMAT)
\ No newline at end of file
+   lg.info("*** Test results: %s\n", results)
\ No newline at end of file
diff --git a/mininet/test/test_nets.py b/mininet/test/test_nets.py
index 215b1867c5f4cca418dd566aae97d922497a01e7..1696106128de3fc994bea6327059433e0ec1dec7 100755
--- a/mininet/test/test_nets.py
+++ b/mininet/test/test_nets.py
@@ -7,7 +7,7 @@
 from time import sleep
 import unittest
 
-from mininet.mininet import init, TreeNet, LinearNet, pingTest, DATAPATHS
+from mininet.net import init, TreeNet, LinearNet, pingTest, DATAPATHS
 
 class testMinimal(unittest.TestCase):
     '''For each datapath type, test ping with a minimal topology.
diff --git a/mininet/util.py b/mininet/util.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b41165e5f0afba6fb541675ddf27f31cd79e047
--- /dev/null
+++ b/mininet/util.py
@@ -0,0 +1,169 @@
+#!/usr/bin/env python
+'''Utility functions for Mininet.'''
+
+from time import sleep
+import select
+from subprocess import call, check_call, Popen, PIPE, STDOUT
+
+from mininet.logging_mod import lg
+
+
+def run(cmd):
+    '''Simple interface to subprocess.call()
+
+     @param cmd list of command params
+    '''
+    return call(cmd.split(' '))
+
+
+def checkRun(cmd):
+    '''Simple interface to subprocess.check_call()
+
+    @param cmd list of command params
+    '''
+    check_call(cmd.split(' '))
+
+
+def quietRun(cmd):
+    '''Run a command, routing stderr to stdout, and return the output.
+
+    @param cmd list of command params
+    '''
+    if isinstance(cmd, str):
+        cmd = cmd.split(' ')
+    popen = Popen(cmd, stdout=PIPE, stderr=STDOUT)
+    # We can't use Popen.communicate() because it uses
+    # select(), which can't handle
+    # high file descriptor numbers! poll() can, however.
+    output = ''
+    readable = select.poll()
+    readable.register(popen.stdout)
+    while True:
+        while readable.poll():
+            data = popen.stdout.read(1024)
+            if len(data) == 0:
+                break
+            output += data
+        popen.poll()
+        if popen.returncode != None:
+            break
+    return output
+
+
+def make_veth_pair(intf1, intf2):
+    '''Create a veth pair connecting intf1 and intf2.
+
+    @param intf1 string, interface name
+    @param intf2 string, interface name
+    '''
+    # Delete any old interfaces with the same names
+    quietRun('ip link del ' + intf1)
+    quietRun('ip link del ' + intf2)
+    # Create new pair
+    cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
+    #lg.info('running command: %s\n' % cmd)
+    return checkRun(cmd)
+
+
+def move_intf(intf, node):
+    '''Move interface to node.
+
+    @param intf string interface name
+    @param node Node object
+
+    @return success boolean, did operation complete?
+    '''
+    cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid)
+    #lg.info('running command: %s\n' % cmd)
+    quietRun(cmd)
+    #lg.info(' output: %s\n' % output)
+    links = node.cmd('ip link show')
+    if not intf in links:
+        lg.error('*** Error: move_intf: % not successfully moved to %s:\n' %
+                 (intf, node.name))
+        return False
+    return True
+
+
+# Interface management
+#
+# Interfaces are managed as strings which are simply the
+# interface names, of the form 'nodeN-ethM'.
+#
+# To connect nodes, we create a pair of veth interfaces, and then place them
+# in the pair of nodes that we want to communicate. We then update the node's
+# list of interfaces and connectivity map.
+#
+# For the kernel datapath, switch interfaces
+# live in the root namespace and thus do not have to be
+# explicitly moved.
+
+def makeIntfPair(intf1, intf2):
+    '''Make a veth pair.
+
+    @param intf1 string, interface
+    @param intf2 string, interface
+    @return success boolean
+    '''
+    # Delete any old interfaces with the same names
+    quietRun('ip link del ' + intf1)
+    quietRun('ip link del ' + intf2)
+    # Create new pair
+    cmd = 'ip link add name ' + intf1 + ' type veth peer name ' + intf2
+    return checkRun( cmd )
+
+
+def moveIntf(intf, node, print_error = False):
+    '''Move interface to node.
+
+    @param intf string, interface
+    @param node Node object
+    @param print_error if true, print error
+    '''
+    cmd = 'ip link set ' + intf + ' netns ' + repr(node.pid)
+    quietRun(cmd)
+    links = node.cmd('ip link show')
+    if not intf in links:
+        if print_error:
+            lg.error('*** Error: moveIntf: % not successfully moved to %s:\n' %
+                     (intf, node.name))
+        return False
+    return True
+
+
+def retry(n, retry_delay, fn, *args):
+    '''Try something N times before giving up.
+
+    @param n number of times to retry
+    @param retry_delay seconds wait this long between tries
+    @param fn function to call
+    @param args args to apply to function call
+    '''
+    tries = 0
+    while not apply(fn, args) and tries < n:
+        sleep(retry_delay)
+        tries += 1
+    if tries >= n:
+        lg.error("*** gave up after %i retries\n" % tries)
+        exit( 1 )
+
+
+# delay between interface move checks in seconds
+MOVEINTF_DELAY = 0.0001
+
+def createLink(node1, node2):
+    '''Create a link between nodes, making an interface for each.
+
+    @param node1 Node object
+    @param node2 Node object
+    '''
+    intf1 = node1.newIntf()
+    intf2 = node2.newIntf()
+    makeIntfPair(intf1, intf2)
+    if node1.inNamespace:
+        retry(3, MOVEINTF_DELAY, moveIntf, intf1, node1)
+    if node2.inNamespace:
+        retry(3, MOVEINTF_DELAY, moveIntf, intf2, node2)
+    node1.connection[intf1] = (node2, intf2)
+    node2.connection[intf2] = (node1, intf1)
+    return intf1, intf2
\ No newline at end of file