periodic26.py
Go to the documentation of this file.
1 #!/bin/env python
2 
3 #**********************************************************************
4 # Copyright (c) 2013 InterWorking Labs, All Rights Reserved *
5 # *
6 # RESTRICTED RIGHTS LEGEND *
7 # *
8 # This software is provided with RESTRICTED RIGHTS. *
9 # *
10 # Use, duplication, or disclosure by the U.S. Government is subject *
11 # to restrictions set forth in subparagraph (c)(1)(ii) of the Rights *
12 # in Technical Data and Computer Software clause at DFARS *
13 # 252.227-7013 or subparagraphs (c)(1) and (2) of the Commercial *
14 # Computer Software - Restricted Rights at 48 CFR 52.227-19, as *
15 # applicable. The "Manufacturer" for purposes of these regulations *
16 # is InterWorking Labs, PO Box 66190, Scotts Valley, California 95060 *
17 # U.S.A. *
18 #**********************************************************************
19 
20 #**********************************************************************
21 # $Id: periodic26.py 464 2013-12-03 00:43:56Z karl $
22 #**********************************************************************
23 
24 # http://stackoverflow.com/questions/2398661/schedule-a-repeating-event-in-python-3
25 
26 have_argparse = False
27 try:
28  import argparse
29  have_argparse = True
30 except:
31  import optparse
32 
33 import datetime
34 
35 import platform
36 import sys
37 import syslog
38 import threading
39 import time
40 import traceback
41 
42 import mm2client
43 import setfilters26 as setfilters
44 
45 #**********************************************************************
46 # SetupJobs()
47 #
48 # Here is where we set up the jobs to be done and the schedule to
49 # do them.
50 #**********************************************************************
51 def SetupJobs(Jobs, mm_hostname):
52 
53  #******************************
54  # Do not change these...
55  LANA_to_LANB = True
56  LANB_to_LANA = False
57  #******************************
58 
59  BandSettings = mm2client.Bands()
60 
61  # A Band is for our 100ms/1megabit path
62  # B band for the 1000ms/1megabit path
63  A_Band = 1
64  B_Band = 2
65 
66  #**************************************************
67  # Here we set rate limitation value that we will use
68  #**************************************************
69  OneMegabit = 1000000 # Some might argue that one megabit is 1048576 (2**20)
70  OneKilobit = 100000 # Some might argue that one kilobit is 1024000
71 
72  #**************************************************
73  # Here we set the two delay values.
74  # Note: These are round trip times.
75  #**************************************************
76  LongRoundtrip = 1000
77  ShortRoundtrip = 100
78 
79  # We will divide the round trip into two equal one-way parts.
80  HalfLongRoundtrip = LongRoundtrip/2
81  HalfShortRoundtrip = ShortRoundtrip/2
82 
83  #**************************************************
84  # Set up our classification filters.
85  # We will be using these as a two-way switch to
86  # send selected packets first into the A band and then
87  # into the B band and then back to the A band etc.
88  # We will let any other forms of traffic flow via
89  # Band 5 which will get no impairments.
90  # There things that don't get picked up by the
91  # filters below are almost certainly low traffic
92  # things like OSPF.
93  #**************************************************
94  Filt_ToBandA = [setfilters.FiltSetting("arp", A_Band),
95  setfilters.FiltSetting("ipv4", A_Band),
96  setfilters.FiltSetting("ipv6", A_Band)
97  ]
98 
99  Filt_ToBandB = [setfilters.FiltSetting("arp", B_Band),
100  setfilters.FiltSetting("ipv4", B_Band),
101  setfilters.FiltSetting("ipv6", B_Band)
102  ]
103 
104  # Define A_Band to have 100ms round trip, 1megabit/second rate limit
105  BandSettings.SetDelayAmount(A_Band, LANA_to_LANB, HalfShortRoundtrip)
106  BandSettings.SetDelayAmount(A_Band, LANB_to_LANA, HalfShortRoundtrip)
107  BandSettings.SetDelayReorder(A_Band, LANA_to_LANB, False)
108  BandSettings.SetDelayReorder(A_Band, LANB_to_LANA, False)
109  BandSettings.SetRateLimit(A_Band, LANA_to_LANB, OneMegabit)
110  BandSettings.SetRateLimit(A_Band, LANB_to_LANA, OneMegabit)
111 
112  # Define B_Band with 1000ms round trip, 1kilobit/second rate limit
113  BandSettings.SetDelayAmount(B_Band, LANA_to_LANB, HalfLongRoundtrip)
114  BandSettings.SetDelayAmount(B_Band, LANB_to_LANA, HalfLongRoundtrip)
115  BandSettings.SetDelayReorder(B_Band, LANA_to_LANB, False)
116  BandSettings.SetDelayReorder(B_Band, LANB_to_LANA, False)
117  BandSettings.SetRateLimit(B_Band, LANA_to_LANB, OneKilobit)
118  BandSettings.SetRateLimit(B_Band, LANB_to_LANA, OneKilobit)
119 
120  #**************************************************
121  # Create an initial job at start-time zero.
122  # Here is where we establish our baseline.
123  # Note that this baseline is a different thing than
124  # the defaults, which are simply the absence of
125  # filters and impairments.
126  #**************************************************
127  Jobs.AddRequest("Establish Baseline - 100ms RT delay, 1,000,000 bit/sec rate limit", mm_hostname, 0,
128  BandSettings, Filt_ToBandA, Filt_ToBandA)
129 
130  #**************************************************
131  # Set up the repeating schedule of jobs
132  #**************************************************
133  interval = 60 # Seconds per item
134  items_per_group = 2
135  num_groups = 720 # Number of groups ==> 24 hours
136 
137  #Remember range() stops at < stop value not <=
138  for secs in range(interval, items_per_group*num_groups*interval, items_per_group*interval):
139  Jobs.AddRequest("Job %u: 1000ms RT delay, 100,000 bit/sec rate limit" % \
140  secs, mm_hostname, secs, None, Filt_ToBandB, Filt_ToBandB)
141  nxtsecs = secs + interval
142  Jobs.AddRequest("Job %u: 100ms RT delay, 1,000,000 bit/sec rate limit" % \
143  nxtsecs, mm_hostname, nxtsecs, None, Filt_ToBandA, Filt_ToBandA)
144  #Jobs.PrintMe()
145 
146 #**********************************************************************
147 # ShowMessage()
148 #**********************************************************************
149 UseSyslog = False # Set to True to send things to the syslog
150 TeamName = "TEAM UNKNOWN" # Should be set to the actual team name
151 
152 def ShowMessage(*args):
153  datetime.datetime.now().isoformat()
154  msg = datetime.datetime.now().isoformat() \
155  + ' "' + TeamName + '" ' \
156  + " ".join(args)
157  print msg
158  if UseSyslog:
159  syslog.syslog(syslog.LOG_INFO, msg)
160 
161 #**********************************************************************
162 # RepeatedTimer
163 #
164 # Features:
165 # - Standard library only, no external dependencies.
166 # - Start() and stop() are safe to call multiple times even if the
167 # timer has already started/stopped.
168 # - Function to be called can have positional and named arguments.
169 # - You can change interval anytime, it will be effective after
170 # next run. Same for args, kwargs and even the function.
171 #**********************************************************************
172 class RepeatedTimer(object):
173  def __init__(self, interval, function, *args, **kwargs):
174  self._timer = None
175  self.function = function
176  self.interval = interval
177  self.args = args
178  self.kwargs = kwargs
179  self.is_running = False
180  self.start()
181 
182  def _run(self):
183  self.is_running = False
184  self.start()
185  self.function(*self.args, **self.kwargs)
186 
187  def start(self):
188  if not self.is_running:
189  self._timer = threading.Timer(self.interval, self._run)
190  self._timer.start()
191  self.is_running = True
192 
193  def stop(self):
194  self._timer.cancel()
195  self.is_running = False
196 
197 
198 #**********************************************************************
199 # DoRequest
200 #**********************************************************************
201 class DoRequest(object):
202  def __init__(self, reqname, mm2host, dowhen, bands, a2b_filtmap, b2a_filtmap):
203  """
204  @param reqname: The name of the request, a single line of text.
205  @type reqname: String
206  @param mm2host: The name or IP address of the target host.
207  @type mm2host: String
208  @param dowhen: A datetime object indicating when to do perform this request.
209  @type dowhen: datetime
210  @param bands: A mm2client.Bands object containing the desired impairment settings.
211  May be None.
212  @type bands: mm2client.Bands
213  @param a2b_filtmap: A sequence of setfilters.FiltSetting objects containing the desired LanA to LanB filters settings.
214  May be an empty sequence.
215  @type a2b_filtmap: A sequence of setfilters.FiltSetting objects.
216  @param b2a_filtmap: A sequence of setfilters.FiltSetting objects containing the desired LanB to LanA filters settings.
217  May be an empty sequence.
218  @type b2a_filtmap: A sequence of setfilters.FiltSetting objects.
219  """
220  self.__Name = reqname
221  self.__MM2HostName = mm2host
222  self.__DoWhen = dowhen
223  self.__Bands = bands
224  self.__A2BFiltmap = a2b_filtmap
225  self.__B2AFiltmap = b2a_filtmap
226  self.__Done = False
227 
228  def __lt__(self, other):
229  return self.__DoWhen < other.__DoWhen
230 
231  def __le__(self, other):
232  return self.__DoWhen <= other.__DoWhen
233 
234  def __eq__(self, other):
235  return self.__DoWhen == other.__DoWhen
236 
237  def __ne__(self, other):
238  return self.__DoWhen != other.__DoWhen
239 
240  def __gt__(self, other):
241  return self.__DoWhen > other.__DoWhen
242 
243  def __ge__(self, other):
244  return self.__DoWhen <= other.__DoWhen
245 
246  def __repr__(self):
247  return repr({
248  "Name": self.__Name,
249  "MM2HostName": self.__MM2HostName,
250  "DoWhen": self.__DoWhen,
251  "Bands": repr(self.__Bands),
252  "A2BFiltMap": repr(self.__A2BFiltmap),
253  "B2AFiltMap": repr(self.__B2AFiltmap),
254  "Done": self.__Done
255  })
256 
257  def __str__(self):
258  return self.__repr__()
259 
260  @property
261  def Name(self):
262  return self.__Name
263 
264  @property
265  def MM2HostName(self):
266  return self.__MM2HostName
267 
268  @property
269  def DoWhen(self):
270  return self.__DoWhen
271 
272  @property
273  def Bands(self):
274  return self.__Bands
275 
276  @property
277  def A2BFiltmap(self):
278  return self.__A2BFiltmap
279 
280  @property
281  def B2AFiltmap(self):
282  return self.__B2AFiltmap
283 
284  @property
285  def IsDone(self):
286  return self.__Done
287 
288  def IsReadyToGo(self, now):
289  return (not self.__Done) and (self.__DoWhen <= now)
290 
291  def Run(self, now):
292  global AllFilterNames
293  assert self.IsReadyToGo(now)
294  self.__Done = True
295  SetMM(self.__MM2HostName, self.__Bands,
296  self.__A2BFiltmap, self.__B2AFiltmap, AllFilterNames)
297 
298 #**********************************************************************
299 # RunList
300 #**********************************************************************
301 class RunList(object):
302  def __init__(self, now, skipsecs):
303  self.__WorkList = []
304  self.__StartTime = now
305  if (skipsecs is not None) and (skipsecs > 0):
306  self.__StartTime = self.__StartTime - datetime.timedelta(0, skipsecs)
307 
308  def AddRequest(self, reqname, mm2host, startsec, bands,
309  a2b_filtmap, b2a_filtmap):
310  dowhen = self.__StartTime + datetime.timedelta(0, startsec)
311  self.__WorkList.append(DoRequest(reqname, mm2host, dowhen, bands,
312  a2b_filtmap, b2a_filtmap))
313  self.__WorkList.sort()
314 
315  def RunNextRequest(self, nowtime):
316  # timedelta = nowtime - self.__StartTime
317  # We are going to do a sequential scan of the work list to
318  # find the first one that has not already been done and
319  # that has reached its start time.
320  # This check happens about once a second.
321  for ndx in range(len(self.__WorkList)):
322  req = self.__WorkList[ndx]
323  if req.IsReadyToGo(nowtime):
324  ShowMessage("Starting task \"%s\"" % req.Name)
325  try:
326  req.Run(nowtime)
327  except Exception, err:
328  ShowMessage("Exception in Task \"%s\" - %s" % (req.Name, str(err)))
329  del self.__WorkList[ndx]
330  return True
331  return False
332 
333  @property
335  if len(self.__WorkList) == 0:
336  return datetime.datetime.now()
337  return self.__WorkList[-1].DoWhen
338 
339  def PrintMe(self):
340  for d in self.__WorkList:
341  ShowMessage("RunRequest", str(d))
342 
343 #**********************************************************************
344 # SetMM()
345 #**********************************************************************
346 def SetMM(mm_hostname, bands, a2bfilt_vals, b2afilt_vals, all_filter_names):
347 
348  if bands is not None:
349  mm2client.ChangeBandsOnMM(bands, mm_hostname)
350  if (a2bfilt_vals is not None) or (b2afilt_vals is not None):
351  setfilters.SetFiltMap(mm_hostname, a2bfilt_vals, b2afilt_vals,
352  all_filter_names)
353 
354 #**********************************************************************
355 # We will try to avoid redundant queries to fetch filter names by
356 # caching it.
357 # It is simply a list of strings.
358 # A value of None means that it will be fetched each time rather than
359 # cached.
360 #**********************************************************************
361 AllFilterNames = None
362 
363 #**********************************************************************
364 #
365 #**********************************************************************
366 pvt = platform.python_version_tuple()
367 if (int(pvt[0]) == 2) and (int(pvt[1]) < 7):
368  def TotalSeconds(td):
369  return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6
370 else:
371  def TotalSeconds(td):
372  return td.total_seconds()
373 
374 #**********************************************************************
375 # main()
376 #**********************************************************************
377 def main():
378  global UseSyslog
379  global AllFilterNames
380  global TeamName
381 
382  #******************************
383  # jobtick() - Where we run the job
384  #******************************
385  def jobtick():
386  Jobs.RunNextRequest(datetime.datetime.now())
387 
388  #******************************
389  # Deal with the arguments
390  #******************************
391  if have_argparse:
392  parser = argparse.ArgumentParser(description="Start impairment pattern.")
393 
394  parser.add_argument("-s", "--skip", type=int,
395  help="Skip ahead: start at N seconds.")
396 
397  parser.add_argument("-C", "--do_not_use_initial_defaults", action="store_true",
398  dest="no_initial_defaults",
399  help="Do not establish defaults before running scheduled tasks.")
400 
401  parser.add_argument("-D", "--initial_defaults_then_exit", action="store_true",
402  dest="initial_defaults_only",
403  help="Establish defaults and then exit, supersedes -C.")
404 
405  parser.add_argument("-l", "--loop", action="store_true",
406  help="Loop/Cycle forever.")
407 
408  parser.add_argument("-S", "--syslog", action="store_true",
409  help="Send reports to the system defined syslog server (level LOG_INFO).")
410 
411  parser.add_argument("-T", "--team", required=True, metavar="<team name>",
412  help="Team name.")
413 
414  parser.add_argument("mm_hostname", metavar="<Mini Maxwell host name or IP address>",
415  help='The host name or IP address of the Mini Maxwell/Maxwell G.')
416 
417  pargs = parser.parse_args()
418 
419  TeamName = pargs.team
420  if pargs.syslog:
421  UseSyslog = True
422 
423  mm_hostname = pargs.mm_hostname
424 
425  do_initial_defaults_only = pargs.initial_defaults_only
426  do_no_initial_defaults = pargs.no_initial_defaults
427  do_skipsecs = pargs.skip
428  do_loop = pargs.loop
429 
430  else:
431  parser = optparse.OptionParser("""usage: %prog [-h] [-s SKIP] [-C] [-D] [-l] [-S] -T <team name>
432  <Mini Maxwell host name or IP address>""")
433 
434  parser.add_option("-s", "--skip", type=int,
435  help="Skip ahead: start at N seconds.")
436 
437  parser.add_option("-C", "--do_not_use_initial_defaults", action="store_true",
438  dest="no_initial_defaults",
439  help="Do not establish defaults before running scheduled tasks.")
440 
441  parser.add_option("-D", "--initial_defaults_then_exit", action="store_true",
442  dest="initial_defaults_only",
443  help="Establish defaults and then exit, supersedes -C.")
444 
445  parser.add_option("-l", "--loop", action="store_true",
446  help="Loop/Cycle forever.")
447 
448  parser.add_option("-S", "--syslog", action="store_true",
449  help="Send reports to the system defined syslog server (level LOG_INFO).")
450 
451  parser.add_option("-T", "--team", metavar="<team name>",
452  help="Team name.")
453 
454  (options, args) = parser.parse_args()
455 
456  TeamName = options.team
457  if TeamName is None:
458  parser.error("Team name must be supplied.")
459 
460  if options.syslog:
461  UseSyslog = True
462 
463  try:
464  mm_hostname = args[0]
465  except:
466  parser.error("Mini Maxwell host name or IP address must be supplied.")
467 
468  do_initial_defaults_only = options.initial_defaults_only
469  if do_initial_defaults_only is None:
470  do_initial_defaults_only = False
471 
472  do_no_initial_defaults = options.no_initial_defaults
473  if do_no_initial_defaults is None:
474  do_no_initial_defaults = False
475 
476  do_skipsecs = options.skip
477 
478  do_loop = options.loop
479  if do_loop is None:
480  do_loop = False
481 
482  if AllFilterNames is None:
483  AllFilterNames = setfilters.GetAllFilterNames(mm_hostname)
484 
485  #**************************************************
486  # Do we just set the defaults and then exit?
487  #**************************************************
488  if do_initial_defaults_only:
489  # Coerce default filter settings and impairments to a known (default) state
490  ShowMessage("Coercing filters and impairments to default state, then exiting")
491  SetMM(mm_hostname, mm2client.Bands(), [], [], AllFilterNames)
492  ShowMessage("Stopping - Complete")
493  sys.exit()
494 
495  #**************************************************
496  # Set default values (unless suppressed)
497  #**************************************************
498  if do_no_initial_defaults:
499  ShowMessage("Not setting initial defaults")
500  else:
501  # Coerce default filter settings and impairments to a known (default) state
502  ShowMessage("Coercing filters and impairments to default state")
503  SetMM(mm_hostname, mm2client.Bands(), [], [], AllFilterNames)
504 
505  skipsecs = do_skipsecs
506 
507  #**************************************************
508  # Main scheduling loop
509  # Note: This loop does not repeat setting of the
510  # default values. But it does repeat the initial
511  # (time zero) job.
512  # Also note that any skipping of initial time is
513  # done only on the first iteration of this loop.
514  # When looping the first job of the new iteration
515  # will start immediately.
516  #**************************************************
517  while True:
518  StartTime = datetime.datetime.now()
519 
520  Jobs = RunList(StartTime, skipsecs)
521  skipsecs = 0 # We only do the skip on the first cycle
522 
523  SetupJobs(Jobs, mm_hostname)
524 
525  # Come up to date with initial and skipped tasks.
526  ShowMessage("Running initial and skipped tasks to establish state.")
527  while Jobs.RunNextRequest(StartTime):
528  pass # Yes, we want to pass, all the work is done in the RunNextRequest()
529 
530  ShowMessage("Beginning scheduled events")
531 
532  # Give ourselves a lifetime.
533  # Add a few seconds to let the last job complete.
534  #sleepsecs = (Jobs.LastRequestStartTime - datetime.datetime.now()).total_seconds() + 5
535  sleepsecs = TotalSeconds(Jobs.LastRequestStartTime - datetime.datetime.now()) + 5
536 
537  # Begin the timer system with a one second interval
538  rt = RepeatedTimer(1, jobtick)
539  try:
540  ShowMessage("Start running for %u seconds." % sleepsecs)
541  time.sleep(sleepsecs)
542  finally:
543  rt.stop() # better in a try/finally block to make sure the program ends!
544 
545  if not do_loop:
546  break
547 
548  ShowMessage("Beginning again")
549 
550  ShowMessage("Stopping - Finished")
551 
552 #**********************************************************************
553 # Entry
554 #**********************************************************************
555 if __name__ == "__main__":
556 
557  try:
558  main()
559  except Exception, err:
560  ShowMessage("Unhandled exception:", str(err))
561  traceback.print_exc()
562 
563  except KeyboardInterrupt:
564  ShowMessage("Keyboard Interruptception")
def ShowMessage(args)
Definition: periodic26.py:152
def __eq__(self, other)
Definition: periodic26.py:234
def __le__(self, other)
Definition: periodic26.py:231
def __ge__(self, other)
Definition: periodic26.py:243
def __ne__(self, other)
Definition: periodic26.py:237
def GetAllFilterNames(mm_hostname)
Definition: setfilters.py:34
def __lt__(self, other)
Definition: periodic26.py:228
def PrintMe(self)
Definition: periodic26.py:339
def __init__(self, now, skipsecs)
Definition: periodic26.py:302
def __init__(self, reqname, mm2host, dowhen, bands, a2b_filtmap, b2a_filtmap)
Definition: periodic26.py:202
def SetupJobs(Jobs, mm_hostname)
Definition: periodic26.py:51
def MM2HostName(self)
Definition: periodic26.py:265
def TotalSeconds(td)
Definition: periodic26.py:368
def RunNextRequest(self, nowtime)
Definition: periodic26.py:315
def __init__(self, interval, function, args, kwargs)
Definition: periodic26.py:173
def B2AFiltmap(self)
Definition: periodic26.py:281
def main()
Definition: periodic26.py:377
def Run(self, now)
Definition: periodic26.py:291
def SetMM(mm_hostname, bands, a2bfilt_vals, b2afilt_vals, all_filter_names)
Definition: periodic26.py:346
def ChangeBandsOnMM(bandsobj, mm2host)
Definition: mm2client.py:479
def __gt__(self, other)
Definition: periodic26.py:240
def LastRequestStartTime(self)
Definition: periodic26.py:334
def IsReadyToGo(self, now)
Definition: periodic26.py:288
def AddRequest(self, reqname, mm2host, startsec, bands, a2b_filtmap, b2a_filtmap)
Definition: periodic26.py:309
def SetFiltMap(mm2host, a2bfilt_vals, b2afilt_vals, all_filter_names=None)
Definition: setfilters.py:69
def A2BFiltmap(self)
Definition: periodic26.py:277


mini_maxwell
Author(s): Yusuke Furuta
autogenerated on Wed Jul 10 2019 03:47:09