periodic26.py
Go to the documentation of this file.
00001 #!/bin/env python
00002 
00003 #**********************************************************************
00004 # Copyright (c) 2013 InterWorking Labs, All Rights Reserved           *
00005 #                                                                     *
00006 #              RESTRICTED RIGHTS LEGEND                               *
00007 #                                                                     *
00008 # This software is provided with RESTRICTED RIGHTS.                   *
00009 #                                                                     *
00010 # Use, duplication, or disclosure by the U.S. Government is subject   *
00011 # to restrictions set forth in subparagraph (c)(1)(ii) of the Rights  *
00012 # in Technical Data and Computer Software clause at DFARS             *
00013 # 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial      *
00014 # Computer Software - Restricted Rights at 48 CFR 52.227-19, as       *
00015 # applicable.  The "Manufacturer" for purposes of these regulations   *
00016 # is InterWorking Labs, PO Box 66190, Scotts Valley, California 95060 *
00017 # U.S.A.                                                              *
00018 #**********************************************************************
00019 
00020 #**********************************************************************
00021 # $Id: periodic26.py 464 2013-12-03 00:43:56Z karl $
00022 #**********************************************************************
00023 
00024 # http://stackoverflow.com/questions/2398661/schedule-a-repeating-event-in-python-3
00025 
00026 have_argparse = False
00027 try:
00028     import argparse
00029     have_argparse = True
00030 except:
00031     import optparse
00032 
00033 import datetime
00034 
00035 import platform
00036 import sys
00037 import syslog
00038 import threading
00039 import time
00040 import traceback
00041 
00042 import mm2client
00043 import setfilters26 as setfilters
00044 
00045 #**********************************************************************
00046 # SetupJobs()
00047 #
00048 # Here is where we set up the jobs to be done and the schedule to
00049 # do them.
00050 #**********************************************************************
00051 def SetupJobs(Jobs, mm_hostname):
00052 
00053     #******************************
00054     # Do not change these...
00055     LANA_to_LANB = True
00056     LANB_to_LANA = False
00057     #******************************
00058 
00059     BandSettings = mm2client.Bands()
00060 
00061     # A Band is for our 100ms/1megabit path
00062     # B band for the 1000ms/1megabit path
00063     A_Band = 1
00064     B_Band = 2
00065 
00066     #**************************************************
00067     # Here we set rate limitation value that we will use
00068     #**************************************************
00069     OneMegabit = 1000000        # Some might argue that one megabit is 1048576 (2**20)
00070     OneKilobit = 100000         # Some might argue that one kilobit is 1024000
00071 
00072     #**************************************************
00073     # Here we set the two delay values.
00074     # Note: These are round trip times.
00075     #**************************************************
00076     LongRoundtrip = 1000
00077     ShortRoundtrip = 100
00078 
00079     # We will divide the round trip into two equal one-way parts.
00080     HalfLongRoundtrip = LongRoundtrip/2
00081     HalfShortRoundtrip = ShortRoundtrip/2
00082 
00083     #**************************************************
00084     # Set up our classification filters.
00085     # We will be using these as a two-way switch to
00086     # send selected packets first into the A band and then
00087     # into the B band and then back to the A band etc.
00088     # We will let any other forms of traffic flow via
00089     # Band 5 which will get no impairments.
00090     # There things that don't get picked up by the
00091     # filters below are almost certainly low traffic
00092     # things like OSPF.
00093     #**************************************************
00094     Filt_ToBandA = [setfilters.FiltSetting("arp", A_Band),
00095                     setfilters.FiltSetting("ipv4", A_Band),
00096                     setfilters.FiltSetting("ipv6", A_Band)
00097                     ]
00098 
00099     Filt_ToBandB = [setfilters.FiltSetting("arp", B_Band),
00100                     setfilters.FiltSetting("ipv4", B_Band),
00101                     setfilters.FiltSetting("ipv6", B_Band)
00102                     ]
00103 
00104     # Define A_Band to have 100ms round trip, 1megabit/second rate limit
00105     BandSettings.SetDelayAmount(A_Band, LANA_to_LANB, HalfShortRoundtrip)
00106     BandSettings.SetDelayAmount(A_Band, LANB_to_LANA, HalfShortRoundtrip)
00107     BandSettings.SetDelayReorder(A_Band, LANA_to_LANB, False)
00108     BandSettings.SetDelayReorder(A_Band, LANB_to_LANA, False)
00109     BandSettings.SetRateLimit(A_Band, LANA_to_LANB, OneMegabit)
00110     BandSettings.SetRateLimit(A_Band, LANB_to_LANA, OneMegabit)
00111 
00112     # Define B_Band with 1000ms round trip, 1kilobit/second rate limit
00113     BandSettings.SetDelayAmount(B_Band, LANA_to_LANB, HalfLongRoundtrip)
00114     BandSettings.SetDelayAmount(B_Band, LANB_to_LANA, HalfLongRoundtrip)
00115     BandSettings.SetDelayReorder(B_Band, LANA_to_LANB, False)
00116     BandSettings.SetDelayReorder(B_Band, LANB_to_LANA, False)
00117     BandSettings.SetRateLimit(B_Band, LANA_to_LANB, OneKilobit)
00118     BandSettings.SetRateLimit(B_Band, LANB_to_LANA, OneKilobit)
00119 
00120     #**************************************************
00121     # Create an initial job at start-time zero.
00122     # Here is where we establish our baseline.
00123     # Note that this baseline is a different thing than
00124     # the defaults, which are simply the absence of
00125     # filters and impairments.
00126     #**************************************************
00127     Jobs.AddRequest("Establish Baseline - 100ms RT delay, 1,000,000 bit/sec rate limit", mm_hostname, 0,
00128                     BandSettings, Filt_ToBandA, Filt_ToBandA)
00129 
00130     #**************************************************
00131     # Set up the repeating schedule of jobs
00132     #**************************************************
00133     interval = 60       # Seconds per item
00134     items_per_group = 2
00135     num_groups = 720    # Number of groups ==> 24 hours
00136 
00137     #Remember range() stops at < stop value not <=
00138     for secs in range(interval, items_per_group*num_groups*interval, items_per_group*interval):
00139         Jobs.AddRequest("Job %u: 1000ms RT delay, 100,000 bit/sec rate limit" % \
00140                             secs, mm_hostname, secs, None, Filt_ToBandB, Filt_ToBandB)
00141         nxtsecs = secs + interval
00142         Jobs.AddRequest("Job %u: 100ms RT delay, 1,000,000 bit/sec rate limit" % \
00143                             nxtsecs, mm_hostname, nxtsecs, None, Filt_ToBandA, Filt_ToBandA)
00144     #Jobs.PrintMe()
00145 
00146 #**********************************************************************
00147 # ShowMessage()
00148 #**********************************************************************
00149 UseSyslog = False # Set to True to send things to the syslog
00150 TeamName = "TEAM UNKNOWN" # Should be set to the actual team name
00151 
00152 def ShowMessage(*args):
00153     datetime.datetime.now().isoformat()
00154     msg = datetime.datetime.now().isoformat() \
00155           + ' "' + TeamName + '" ' \
00156           + " ".join(args)
00157     print msg
00158     if UseSyslog:
00159         syslog.syslog(syslog.LOG_INFO, msg)
00160 
00161 #**********************************************************************
00162 # RepeatedTimer
00163 #
00164 # Features:
00165 #  - Standard library only, no external dependencies.
00166 #  - Start() and stop() are safe to call multiple times even if the
00167 #    timer has already started/stopped.
00168 #  - Function to be called can have positional and named arguments.
00169 #  - You can change interval anytime, it will be effective after
00170 #    next run. Same for args, kwargs and even the function.
00171 #**********************************************************************
00172 class RepeatedTimer(object):
00173     def __init__(self, interval, function, *args, **kwargs):
00174         self._timer     = None
00175         self.function   = function
00176         self.interval   = interval
00177         self.args       = args
00178         self.kwargs     = kwargs
00179         self.is_running = False
00180         self.start()
00181 
00182     def _run(self):
00183         self.is_running = False
00184         self.start()
00185         self.function(*self.args, **self.kwargs)
00186 
00187     def start(self):
00188         if not self.is_running:
00189             self._timer = threading.Timer(self.interval, self._run)
00190             self._timer.start()
00191             self.is_running = True
00192 
00193     def stop(self):
00194         self._timer.cancel()
00195         self.is_running = False
00196         
00197 
00198 #**********************************************************************
00199 # DoRequest
00200 #**********************************************************************
00201 class DoRequest(object):
00202     def __init__(self, reqname, mm2host, dowhen, bands, a2b_filtmap, b2a_filtmap):
00203         """
00204         @param reqname: The name of the request, a single line of text.
00205         @type reqname: String
00206         @param mm2host: The name or IP address of the target host.
00207         @type mm2host: String
00208         @param dowhen: A datetime object indicating when to do perform this request.
00209         @type dowhen: datetime
00210         @param bands: A mm2client.Bands object containing the desired impairment settings.
00211                       May be None.
00212         @type bands: mm2client.Bands
00213         @param a2b_filtmap: A sequence of setfilters.FiltSetting objects containing the desired LanA to LanB filters settings.
00214                             May be an empty sequence.
00215         @type a2b_filtmap: A sequence of setfilters.FiltSetting objects.
00216         @param b2a_filtmap: A sequence of setfilters.FiltSetting objects containing the desired LanB to LanA filters settings.
00217                             May be an empty sequence.
00218         @type b2a_filtmap: A sequence of setfilters.FiltSetting objects.
00219         """
00220         self.__Name = reqname
00221         self.__MM2HostName = mm2host
00222         self.__DoWhen = dowhen
00223         self.__Bands = bands
00224         self.__A2BFiltmap = a2b_filtmap
00225         self.__B2AFiltmap = b2a_filtmap
00226         self.__Done = False
00227 
00228     def __lt__(self, other):
00229         return self.__DoWhen < other.__DoWhen
00230     
00231     def __le__(self, other):
00232         return self.__DoWhen <= other.__DoWhen
00233 
00234     def __eq__(self, other):
00235         return self.__DoWhen == other.__DoWhen
00236 
00237     def __ne__(self, other):
00238         return self.__DoWhen != other.__DoWhen
00239 
00240     def __gt__(self, other):
00241         return self.__DoWhen > other.__DoWhen
00242 
00243     def __ge__(self, other):
00244         return self.__DoWhen <= other.__DoWhen
00245 
00246     def __repr__(self):
00247         return repr({
00248                 "Name": self.__Name,
00249                 "MM2HostName": self.__MM2HostName,
00250                 "DoWhen": self.__DoWhen,
00251                 "Bands": repr(self.__Bands),
00252                 "A2BFiltMap": repr(self.__A2BFiltmap),
00253                 "B2AFiltMap": repr(self.__B2AFiltmap),
00254                 "Done": self.__Done
00255                 })
00256 
00257     def __str__(self):
00258         return self.__repr__()
00259 
00260     @property
00261     def Name(self):
00262         return self.__Name
00263 
00264     @property
00265     def MM2HostName(self):
00266         return self.__MM2HostName
00267 
00268     @property
00269     def DoWhen(self):
00270         return self.__DoWhen
00271 
00272     @property
00273     def Bands(self):
00274         return self.__Bands
00275 
00276     @property
00277     def A2BFiltmap(self):
00278         return self.__A2BFiltmap
00279 
00280     @property
00281     def B2AFiltmap(self):
00282         return self.__B2AFiltmap
00283 
00284     @property
00285     def IsDone(self):
00286         return self.__Done
00287 
00288     def IsReadyToGo(self, now):
00289         return (not self.__Done) and (self.__DoWhen <= now)
00290 
00291     def Run(self, now):
00292         global AllFilterNames
00293         assert self.IsReadyToGo(now)
00294         self.__Done = True
00295         SetMM(self.__MM2HostName, self.__Bands, 
00296               self.__A2BFiltmap, self.__B2AFiltmap, AllFilterNames)
00297 
00298 #**********************************************************************
00299 # RunList
00300 #**********************************************************************
00301 class RunList(object):
00302     def __init__(self, now, skipsecs):
00303         self.__WorkList = []
00304         self.__StartTime = now
00305         if (skipsecs is not None) and (skipsecs > 0):
00306             self.__StartTime = self.__StartTime - datetime.timedelta(0, skipsecs)
00307 
00308     def AddRequest(self, reqname, mm2host, startsec, bands,
00309                    a2b_filtmap, b2a_filtmap):
00310         dowhen = self.__StartTime + datetime.timedelta(0, startsec)
00311         self.__WorkList.append(DoRequest(reqname, mm2host, dowhen, bands,
00312                                          a2b_filtmap, b2a_filtmap))
00313         self.__WorkList.sort()
00314 
00315     def RunNextRequest(self, nowtime):
00316         # timedelta = nowtime - self.__StartTime
00317         # We are going to do a sequential scan of the work list to
00318         # find the first one that has not already been done and
00319         # that has reached its start time.
00320         # This check happens about once a second.
00321         for ndx in range(len(self.__WorkList)):
00322             req = self.__WorkList[ndx]
00323             if req.IsReadyToGo(nowtime):
00324                 ShowMessage("Starting task \"%s\"" % req.Name)
00325                 try:
00326                     req.Run(nowtime)
00327                 except Exception, err:
00328                     ShowMessage("Exception in Task \"%s\" - %s" % (req.Name, str(err)))
00329                 del self.__WorkList[ndx]
00330                 return True
00331         return False
00332 
00333     @property
00334     def LastRequestStartTime(self):
00335         if len(self.__WorkList) == 0:
00336                return datetime.datetime.now()
00337         return self.__WorkList[-1].DoWhen
00338 
00339     def PrintMe(self):
00340         for d in self.__WorkList:
00341             ShowMessage("RunRequest", str(d))
00342 
00343 #**********************************************************************
00344 # SetMM()
00345 #**********************************************************************
00346 def SetMM(mm_hostname, bands, a2bfilt_vals, b2afilt_vals, all_filter_names):
00347 
00348     if bands is not None:
00349         mm2client.ChangeBandsOnMM(bands, mm_hostname)
00350     if (a2bfilt_vals is not None) or (b2afilt_vals is not None):
00351         setfilters.SetFiltMap(mm_hostname, a2bfilt_vals, b2afilt_vals,
00352                               all_filter_names)
00353 
00354 #**********************************************************************
00355 # We will try to avoid redundant queries to fetch filter names by
00356 # caching it.
00357 # It is simply a list of strings.
00358 # A value of None means that it will be fetched each time rather than
00359 # cached.
00360 #**********************************************************************
00361 AllFilterNames = None
00362 
00363 #**********************************************************************
00364 #
00365 #**********************************************************************
00366 pvt = platform.python_version_tuple()
00367 if (int(pvt[0]) == 2) and (int(pvt[1]) < 7):
00368     def TotalSeconds(td):
00369         return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
00370 else:
00371     def TotalSeconds(td):
00372         return td.total_seconds()
00373 
00374 #**********************************************************************
00375 # main()
00376 #**********************************************************************
00377 def main():
00378     global UseSyslog
00379     global AllFilterNames
00380     global TeamName
00381 
00382     #******************************
00383     # jobtick() - Where we run the job
00384     #******************************
00385     def jobtick():
00386         Jobs.RunNextRequest(datetime.datetime.now())
00387 
00388     #******************************
00389     # Deal with the arguments
00390     #******************************
00391     if have_argparse:
00392         parser = argparse.ArgumentParser(description="Start impairment pattern.")
00393 
00394         parser.add_argument("-s", "--skip", type=int,
00395                             help="Skip ahead: start at N seconds.")
00396 
00397         parser.add_argument("-C", "--do_not_use_initial_defaults", action="store_true",
00398                             dest="no_initial_defaults",
00399                             help="Do not establish defaults before running scheduled tasks.")
00400 
00401         parser.add_argument("-D", "--initial_defaults_then_exit", action="store_true",
00402                             dest="initial_defaults_only",
00403                             help="Establish defaults and then exit, supersedes -C.")
00404 
00405         parser.add_argument("-l", "--loop", action="store_true",
00406                             help="Loop/Cycle forever.")
00407 
00408         parser.add_argument("-S", "--syslog", action="store_true",
00409                             help="Send reports to the system defined syslog server (level LOG_INFO).")
00410 
00411         parser.add_argument("-T", "--team", required=True, metavar="<team name>",
00412                             help="Team name.")
00413 
00414         parser.add_argument("mm_hostname",  metavar="<Mini Maxwell host name or IP address>",
00415                             help='The host name or IP address of the Mini Maxwell/Maxwell G.')
00416 
00417         pargs = parser.parse_args()
00418 
00419         TeamName = pargs.team
00420         if pargs.syslog:
00421             UseSyslog = True
00422 
00423         mm_hostname = pargs.mm_hostname
00424 
00425         do_initial_defaults_only = pargs.initial_defaults_only
00426         do_no_initial_defaults = pargs.no_initial_defaults
00427         do_skipsecs = pargs.skip
00428         do_loop = pargs.loop
00429 
00430     else:
00431         parser = optparse.OptionParser("""usage: %prog [-h] [-s SKIP] [-C] [-D] [-l] [-S] -T <team name>
00432                    <Mini Maxwell host name or IP address>""")
00433 
00434         parser.add_option("-s", "--skip", type=int,
00435                             help="Skip ahead: start at N seconds.")
00436 
00437         parser.add_option("-C", "--do_not_use_initial_defaults", action="store_true",
00438                           dest="no_initial_defaults",
00439                           help="Do not establish defaults before running scheduled tasks.")
00440 
00441         parser.add_option("-D", "--initial_defaults_then_exit", action="store_true",
00442                           dest="initial_defaults_only",
00443                           help="Establish defaults and then exit, supersedes -C.")
00444 
00445         parser.add_option("-l", "--loop", action="store_true",
00446                           help="Loop/Cycle forever.")
00447 
00448         parser.add_option("-S", "--syslog", action="store_true",
00449                           help="Send reports to the system defined syslog server (level LOG_INFO).")
00450 
00451         parser.add_option("-T", "--team", metavar="<team name>",
00452                           help="Team name.")
00453 
00454         (options, args) = parser.parse_args()
00455 
00456         TeamName = options.team
00457         if TeamName is None:
00458             parser.error("Team name must be supplied.")
00459 
00460         if options.syslog:
00461             UseSyslog = True
00462 
00463         try:
00464             mm_hostname = args[0]
00465         except:
00466             parser.error("Mini Maxwell host name or IP address must be supplied.")
00467 
00468         do_initial_defaults_only = options.initial_defaults_only
00469         if do_initial_defaults_only is None:
00470             do_initial_defaults_only = False
00471 
00472         do_no_initial_defaults = options.no_initial_defaults
00473         if do_no_initial_defaults is None:
00474             do_no_initial_defaults = False
00475 
00476         do_skipsecs = options.skip
00477 
00478         do_loop = options.loop
00479         if do_loop is None:
00480             do_loop = False
00481 
00482     if AllFilterNames is None:
00483         AllFilterNames = setfilters.GetAllFilterNames(mm_hostname)
00484 
00485     #**************************************************
00486     # Do we just set the defaults and then exit?
00487     #**************************************************
00488     if do_initial_defaults_only:
00489         # Coerce default filter settings and impairments to a known (default) state
00490         ShowMessage("Coercing filters and impairments to default state, then exiting")
00491         SetMM(mm_hostname, mm2client.Bands(), [], [], AllFilterNames)
00492         ShowMessage("Stopping - Complete")
00493         sys.exit()
00494 
00495     #**************************************************
00496     # Set default values (unless suppressed)
00497     #**************************************************
00498     if do_no_initial_defaults:
00499         ShowMessage("Not setting initial defaults")
00500     else:
00501         # Coerce default filter settings and impairments to a known (default) state
00502         ShowMessage("Coercing filters and impairments to default state")
00503         SetMM(mm_hostname, mm2client.Bands(), [], [], AllFilterNames)
00504 
00505     skipsecs = do_skipsecs
00506 
00507     #**************************************************
00508     # Main scheduling loop
00509     # Note: This loop does not repeat setting of the
00510     # default values.  But it does repeat the initial
00511     # (time zero) job.
00512     # Also note that any skipping of initial time is
00513     # done only on the first iteration of this loop.
00514     # When looping the first job of the new iteration
00515     # will start immediately.
00516     #**************************************************
00517     while True:
00518         StartTime = datetime.datetime.now()
00519 
00520         Jobs = RunList(StartTime, skipsecs)
00521         skipsecs = 0 # We only do the skip on the first cycle
00522 
00523         SetupJobs(Jobs, mm_hostname)
00524 
00525         # Come up to date with initial and skipped tasks.
00526         ShowMessage("Running initial and skipped tasks to establish state.")
00527         while Jobs.RunNextRequest(StartTime):
00528             pass # Yes, we want to pass, all the work is done in the RunNextRequest()
00529 
00530         ShowMessage("Beginning scheduled events")
00531 
00532         # Give ourselves a lifetime.
00533         # Add a few seconds to let the last job complete.
00534         #sleepsecs = (Jobs.LastRequestStartTime - datetime.datetime.now()).total_seconds() + 5
00535         sleepsecs = TotalSeconds(Jobs.LastRequestStartTime - datetime.datetime.now()) + 5
00536 
00537         # Begin the timer system with a one second interval
00538         rt = RepeatedTimer(1, jobtick)
00539         try:
00540             ShowMessage("Start running for %u seconds." % sleepsecs)
00541             time.sleep(sleepsecs)
00542         finally:
00543             rt.stop() # better in a try/finally block to make sure the program ends!
00544 
00545         if not do_loop:
00546             break
00547 
00548         ShowMessage("Beginning again")
00549 
00550     ShowMessage("Stopping - Finished")
00551 
00552 #**********************************************************************
00553 # Entry
00554 #**********************************************************************
00555 if __name__ == "__main__":
00556 
00557     try:
00558         main()
00559     except Exception, err:
00560         ShowMessage("Unhandled exception:", str(err))
00561         traceback.print_exc()
00562 
00563     except KeyboardInterrupt:
00564         ShowMessage("Keyboard Interruptception")


mini_maxwell
Author(s): Yusuke Furuta
autogenerated on Sun Jan 25 2015 12:37:43