# Copyright 2011-2013 Colin Scott
# Copyright 2011-2013 Andreas Wundsam
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
import sys
import os
import itertools
from copy import copy
import types
import signal
import tempfile
from config.experiment_config_lib import ControllerConfig
from sts.control_flow import Replayer, MCSFinder, EfficientMCSFinder
from sts.topology import FatTree, BufferedPatchPanel, MeshTopology
from sts.simulation_state import Simulation, SimulationConfig
from sts.replay_event import Event, InternalEvent, InputEvent
from sts.event_dag import EventDag
from sts.entities import Host, Controller
import logging
sys.path.append(os.path.dirname(__file__) + "/../../..")
_running_simulation = None
[docs]def handle_int(sigspec, frame):
  print >> sys.stderr, "Caught signal %d, stopping sdndebug" % sigspec
  if (_running_simulation is not None and
      _running_simulation.current_simulation is not None):
    _running_simulation.current_simulation.clean_up()
  raise RuntimeError("terminating on signal %d" % sigspec)
 
signal.signal(signal.SIGINT, handle_int)
signal.signal(signal.SIGTERM, handle_int)
[docs]class ReplayerTest(unittest.TestCase):
  tmp_basic_superlog = '/tmp/superlog_basic.tmp'
  tmp_controller_superlog = '/tmp/superlog_controller.tmp'
  tmp_dataplane_superlog = '/tmp/superlog_dataplane.tmp'
  tmp_migration_superlog = '/tmp/superlog_migration.tmp'
  # ------------------------------------------ #
  #        Basic Test                          #
  # ------------------------------------------ #
[docs]  def write_simple_superlog(self):
    ''' Make sure to delete afterwards! '''
    superlog = open(self.tmp_basic_superlog, 'w')
    e1 = str('''{"dependent_labels": ["e2"], "start_dpid": 8, "class": "LinkFailure",'''
             ''' "start_port_no": 3, "end_dpid": 15, "end_port_no": 2,'''
             ''' "label": "e1", "time": [0,0], "round": 0}''')
    superlog.write(e1 + '\n')
    e2 = str('''{"dependent_labels": [], "start_dpid": 8, "class": "LinkRecovery",'''
             ''' "start_port_no": 3, "end_dpid": 15, "end_port_no": 2, "label": "e2", "time": [0,0], "round": 0}''')
    superlog.write(e2 + '\n')
    e3 = str('''{"dependent_labels": ["e4"], "dpid": 8, "class": "SwitchFailure",'''
             ''' "label": "e3", "time": [0,0], "round": 0}''')
    superlog.write(e3 + '\n')
    e4 = str('''{"dependent_labels": [], "dpid": 8, "class": "SwitchRecovery",'''
             ''' "label": "e4", "time": [0,0], "round": 0}''')
    superlog.write(e4 + '\n')
    superlog.close()
 
[docs]  def setup_simple_simulation(self):
    controllers = []
    topology_class = FatTree
    topology_params = ""
    patch_panel_class = BufferedPatchPanel
    sim = SimulationConfig(controllers, topology_class, topology_params, patch_panel_class)
    global _running_simulation
    _running_simulation = sim
    return sim
 
[docs]  def test_basic(self):
    simulation = None
    try:
      self.write_simple_superlog()
      simulation_cfg = self.setup_simple_simulation()
      replayer = Replayer(simulation_cfg, self.tmp_basic_superlog)
      simulation = replayer.simulate()
    finally:
      os.unlink(self.tmp_basic_superlog)
      if simulation is not None:
        simulation.clean_up()
  # ------------------------------------------ #
  #        Controller Crash Test               #
  # ------------------------------------------ #
 
[docs]  def write_controller_crash_superlog(self):
    superlog = open(self.tmp_controller_superlog, 'w')
    e1 = str('''{"dependent_labels": ["e2"], "controller_id": "c1",'''
             ''' "class": "ControllerFailure", "label": "e1", "time": [0,0], "round": 0}''')
    superlog.write(e1 + '\n')
    e2 = str('''{"dependent_labels": [], "controller_id": "c1",'''
             ''' "class": "ControllerRecovery", "label": "e2", "time": [0,0], "round": 0}''')
    superlog.write(e2 + '\n')
    superlog.close()
 
[docs]  def setup_controller_simulation(self):
    start_cmd = "./pox.py --verbose --no-cli sts.syncproto.pox_syncer --blocking=False openflow.of_01 --address=__address__ --port=__port__"
    controllers = [ControllerConfig(cwd='pox', start_cmd=start_cmd, address="127.0.0.1", port=8899, sync="tcp:localhost:18899")]
    topology_class = MeshTopology
    topology_params = "num_switches=2"
    patch_panel_class = BufferedPatchPanel
    return SimulationConfig(controllers,
                            topology_class,
                            topology_params,
                            patch_panel_class)
 
[docs]  def test_controller_crash(self):
    simulation = None
    try:
      self.write_controller_crash_superlog()
      simulation_cfg = self.setup_controller_simulation()
      replayer = Replayer(simulation_cfg, self.tmp_controller_superlog)
      simulation = replayer.simulate()
    finally:
      os.unlink(self.tmp_controller_superlog)
      if simulation is not None:
        simulation.clean_up()
  # ------------------------------------------ #
  #        Dataplane Trace Test                #
  # ------------------------------------------ #
 
[docs]  def write_dataplane_trace_superlog(self):
    superlog = open(self.tmp_dataplane_superlog, 'w')
    e1 = str('''{"dependent_labels": [],  "class": "TrafficInjection",'''
             ''' "label": "e1", "time": [0,0], "round": 0}''')
    superlog.write(e1 + '\n')
    e2 = str('''{"dependent_labels": [],  "class": "TrafficInjection",'''
             ''' "label": "e2", "time": [0,0], "round": 0}''')
    superlog.write(e2 + '\n')
    superlog.close()
 
[docs]  def setup_dataplane_simulation(self):
    controllers = []
    topology_class = MeshTopology
    topology_params = "num_switches=2"
    patch_panel_class = BufferedPatchPanel
    dataplane_trace_path = "./dataplane_traces/ping_pong_same_subnet.trace"
    return SimulationConfig(controllers, topology_class, topology_params,
                            patch_panel_class, dataplane_trace=dataplane_trace_path)
 
[docs]  def test_dataplane_injection(self):
    simulation = None
    try:
      self.write_dataplane_trace_superlog()
      simulation_cfg = self.setup_dataplane_simulation()
      replayer = Replayer(simulation_cfg, self.tmp_dataplane_superlog)
      simulation = replayer.simulate()
    finally:
      os.unlink(self.tmp_dataplane_superlog)
      if simulation is not None:
        simulation.clean_up()
  # ------------------------------------------ #
  #        Host Migration Test                 #
  # ------------------------------------------ #
 
[docs]  def write_migration_superlog(self):
    superlog = open(self.tmp_migration_superlog, 'w')
    e1 = str('''{"dependent_labels": ["e2"], "old_ingress_dpid": 7, "class": "HostMigration",'''
             ''' "old_ingress_port_no": 1, "new_ingress_dpid": 8, '''
             ''' "new_ingress_port_no": 99, "host_id": "host1","label": "e1", "time": [0,0], "round": 0}''')
    superlog.write(e1 + '\n')
    e2 = str('''{"dependent_labels": [], "old_ingress_dpid": 8, "class": "HostMigration",'''
             ''' "old_ingress_port_no": 99, "new_ingress_dpid": 7, '''
             ''' "new_ingress_port_no": 101, "host_id": "host1","label": "e2", "time": [0,0], "round": 0}''')
    superlog.write(e2 + '\n')
    superlog.close()
 
[docs]  def setup_migration_simulation(self):
    controllers = []
    topology_class = FatTree
    topology_params = ""
    patch_panel_class = BufferedPatchPanel
    return SimulationConfig(controllers, topology_class, topology_params,
                            patch_panel_class)
 
[docs]  def test_migration(self):
    simulation = None
    try:
      self.write_migration_superlog()
      simulation_cfg = self.setup_migration_simulation()
      replayer = Replayer(simulation_cfg, self.tmp_migration_superlog)
      simulation = replayer.simulate()
      latest_switch = simulation.topology.get_switch(7)
      latest_port = latest_switch.ports[101]
      (host, interface) = simulation.topology.get_connected_port(latest_switch,
                                                                 latest_port)
      self.assertTrue(type(host) == Host)
    finally:
      os.unlink(self.tmp_migration_superlog)
      if simulation is not None:
        simulation.clean_up()
  
if __name__ == '__main__':
  unittest.main()