#!/usr/bin/python

from debugger.debugger import FuzzTester
from debugger.deferred_io import DeferredIOWorker
import debugger.topology_generator as default_topology
from pox.lib.ioworker.io_worker import RecocoIOLoop
from experiment_config_lib import Controller
from pox.lib.recoco.recoco import Scheduler

import sys
import string
import subprocess
import argparse
import logging
logging.basicConfig(level=logging.DEBUG)

# We use python as our DSL for specifying experiment configuration  
# The module can define the following functions:
#   controllers(command_line_args=[]) => returns a list of pox.debugger.experiment_config_info.ControllerInfo objects
#   switches()                        => returns a list of pox.debugger.experiment_config_info.Switch objects

# TODO: merge with Mininet
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
             description="Run a debugger experiment.\n"+
                "Note: must precede controller args with --\n"+
                "Example usage:\n"+
                "$ %s -- ./pox/pox.py --no-cli openflow.of_01 --address=__address__ --port=__port__" % (sys.argv[0]) )
parser.add_argument("--config_file", help='optional experiment config file to load')
parser.add_argument('controller_args', metavar='controller arg', nargs='*',
                   help='arguments to pass to the controller(s)')
args = parser.parse_args()
  
if args.config_file:
  config = __import__(args.config_file)
else:
  config = object()

if hasattr(config, 'controllers'):
  controllers = config.controllers(args.controller_args)
else:
  controllers = [Controller(args.controller_args)] 

child_processes = []

# Boot the controllers
for c in controllers:
  command_line_args = map(lambda(x): string.replace(x, "__port__", str(c.port)),
                      map(lambda(x): string.replace(x, "__address__", str(c.address)), c.cmdline))
  print command_line_args
  child = subprocess.Popen(command_line_args)
  child_processes.append(child)
  
io_loop = RecocoIOLoop()

#if hasattr(config, 'switches'):
#  switches = config.switches()
#else:
#  switches = []
# HACK
create_worker = lambda(socket): DeferredIOWorker(io_loop.create_worker_for_socket(socket))

(panel, switch_impls) = default_topology.populate(controllers,
                                                   create_worker,
                                                   io_loop.remove_worker,
                                                   num_switches=1)
  
scheduler = Scheduler()
scheduler.schedule(io_loop)

# TODO: allow user to configure the fuzzer parameters, e.g. drop rate
debugger = FuzzTester(child_processes)
debugger.start(panel, switch_impls)

@@ -300,16 +300,30 @@
 eduler%0A%0A
+import signal%0A
 import s
@@ -1660,17 +1660,16 @@
 r_args)%5D
- 
 %0A%0Achild_
@@ -1682,16 +1682,509 @@
 ses = %5B%5D
+%0Ascheduler = None%0Adef kill_children(signal, frame):%0A  global child_processes%0A  global scheduler%0A%0A  print %3E%3E sys.stderr, %22Caught signal %25d, stopping sdndebug%22 %25 signal%0A  print %3E%3E sys.stderr, %22Killing child controllers...%22%0A  for child in child_processes:%0A    # SIGTERM for now%0A    child.terminate()%0A  print %3E%3E sys.stderr, %22Stopping Recoco Scheduler...%22%0A  if scheduler:%0A      scheduler.quit()%0A  sys.exit(0)%0A%0Asignal.signal(signal.SIGINT, kill_children)%0Asignal.signal(signal.SIGTERM, kill_children)
 %0A%0A# Boot
@@ -3018,16 +3018,27 @@
 heduler(
+daemon=True
 )%0Aschedu

handle SIGINT and SIGTERM to kill child controllers and Recoco Scheduler