o-du/phy
Intel O-RAN/X-RAN Generated Doxygen Documentation
master.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 
7 
8 """This script run test cases with O-DU and O-RU
9 """
10 import logging
11 import sys
12 import argparse
13 import re
14 import subprocess
15 import os
16 import shutil
17 from itertools import dropwhile
18 from datetime import datetime
19 import json
20 
21 nNumRbsPerSymF1 = [
22  # 5MHz 10MHz 15MHz 20 MHz 25 MHz 30 MHz 40 MHz 50MHz 60 MHz 70 MHz 80 MHz 90 MHz 100 MHz
23  [25, 52, 79, 106, 133, 160, 216, 270, 0, 0, 0, 0, 0], # Numerology 0 (15KHz)
24  [11, 24, 38, 51, 65, 78, 106, 133, 162, 0, 217, 245, 273], # Numerology 1 (30KHz)
25  [0, 11, 18, 24, 31, 38, 51, 65, 79, 0, 107, 121, 135] # Numerology 2 (60KHz)
26 ]
27 
28 nNumRbsPerSymF2 = [
29  # 50Mhz 100MHz 200MHz 400MHz
30  [66, 132, 264, 0], # Numerology 2 (60KHz)
31  [32, 66, 132, 264] # Numerology 3 (120KHz)
32 ]
33 
34 
35 nRChBwOptions_keys = ['5','10','15','20', '25', '30', '40', '50', '60','70', '80', '90', '100', '200', '400']
36 nRChBwOptions_values = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14]
37 nRChBwOptions = dict(zip(nRChBwOptions_keys, nRChBwOptions_values))
38 
39 nRChBwOptions_keys_mu2and3 = ['50', '100', '200', '400']
40 nRChBwOptions_values_mu2and3 = [0,1,2,3]
41 nRChBwOptions_mu2and3 = dict(zip(nRChBwOptions_keys_mu2and3, nRChBwOptions_values_mu2and3))
42 
43 # table of all test cases
44 # (cat, mu, bw, test case)
45 all_test_cases = [(0, 0, 5, 0),
46  (0, 0, 10, 0),
47  (0, 0, 20, 0),
48  (0, 1, 100, 0),
49  (0, 3, 100, 0),
50  (1, 1, 100, 0)]
51 #Cat B
52 """ all_test_cases = [(1, 1, 100, 0),
53  (1, 1, 100, 1),
54  (1, 1, 100, 101),
55  (1, 1, 100, 102),
56  (1, 1, 100, 103),
57  (1, 1, 100, 104),
58  (1, 1, 100, 105),
59  #(1, 1, 100, 106), 25G not enough
60  (1, 1, 100, 107),
61  (1, 1, 100, 108),
62  #(1, 1, 100, 109), 25G not enough
63  (1, 1, 100, 201),
64  #(1, 1, 100, 202), 25G not enough
65  (1, 1, 100, 203),
66  (1, 1, 100, 204),
67  (1, 1, 100, 205),
68  (1, 1, 100, 206),
69  (1, 1, 100, 211),
70  #(1, 1, 100, 212), 25G not enough
71  (1, 1, 100, 213),
72  (1, 1, 100, 214),
73  (1, 1, 100, 215),
74  (1, 1, 100, 216)
75 ]
76  """
77 dic_dir = dict({0:'DL', 1:'UL'})
78 dic_xu = dict({0:'o-du', 1:'o-ru'})
79 
80 def init_logger(console_level, logfile_level):
81  """Initializes console and logfile logger with given logging levels"""
82  # File logger
83  logging.basicConfig(filename="runtests.log",
84  filemode='w',
85  format="%(asctime)s: %(levelname)s: %(message)s",
86  level=logfile_level)
87  # Console logger
88  logger = logging.getLogger()
89  handler = logging.StreamHandler()
90  handler.setLevel(console_level)
91  formatter = logging.Formatter("%(levelname)s: %(message)s")
92  handler.setFormatter(formatter)
93  logger.addHandler(handler)
94 
95 def parse_args(args):
96  """Configures parser and parses command line configuration"""
97  # Parser configuration
98  parser = argparse.ArgumentParser(description="Run test cases: category numerology bandwidth test_num")
99 
100  parser.add_argument("--cat", type=int, default=0, help="Category: 0 (A) or 1 (B)", metavar="cat", dest="category")
101  parser.add_argument("--mu", type=int, default=0, help="numerology [0,1,3]", metavar="num", dest="numerology")
102  parser.add_argument("--bw", type=int, default=20, help="bandwidth [5,10,20,100]", metavar="bw", dest="bandwidth")
103  parser.add_argument("--testcase", type=int, default=0, help="test case number", metavar="testcase", dest="testcase")
104 
105  # Parse arguments
106  options = parser.parse_args(args)
107  #parser.print_help()
108  logging.debug("Options: category=%d num=%d bw=%d testcase=%d",
109  options.category, options.numerology, options.bandwidth, options.testcase)
110  return options
111 
112 def is_comment(s):
113  """ function to check if a line
114  starts with some character.
115  Here # for comment
116  """
117  # return true if a line starts with #
118  return s.startswith('#')
119 
120 class GetOutOfLoops( Exception ):
121  pass
122 
123 def compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, direction):
124  res = 0
125 
126  if mu < 3:
127  nDlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nDLBandwidth))]
128  nUlRB = nNumRbsPerSymF1[mu][nRChBwOptions.get(str(nULBandwidth))]
129  elif (mu >=2) & (mu <= 3):
130  nDlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nDLBandwidth))]
131  nUlRB = nNumRbsPerSymF2[mu - 2][nRChBwOptions_mu2and3.get(str(nULBandwidth))]
132  print(nDlRB, nUlRB)
133  else:
134  print("Incorrect arguments\n")
135  res = -1
136  return res
137 
138  if 'compression' in globals():
139  comp = compression
140  else:
141  comp = 0
142 
143  print("compare results: {} [compression {}]\n".format(dic_dir.get(direction), comp))
144 
145  #if cat == 1:
146  # print("WARNING: Skip checking IQs and BF Weights for CAT B for now\n");
147  # return res
148 
149  #get slot config
150  if nFrameDuplexType == 1:
151  SlotConfig = []
152  for i in range(nTddPeriod):
153  if i == 0:
154  SlotConfig.insert(i, sSlotConfig0)
155  elif i == 1:
156  SlotConfig.insert(i, sSlotConfig1)
157  elif i == 2:
158  SlotConfig.insert(i, sSlotConfig2)
159  elif i == 3:
160  SlotConfig.insert(i, sSlotConfig3)
161  elif i == 4:
162  SlotConfig.insert(i, sSlotConfig4)
163  elif i == 5:
164  SlotConfig.insert(i, sSlotConfig5)
165  elif i == 6:
166  SlotConfig.insert(i, sSlotConfig6)
167  elif i == 7:
168  SlotConfig.insert(i, sSlotConfig7)
169  elif i == 8:
170  SlotConfig.insert(i, sSlotConfig8)
171  elif i == 9:
172  SlotConfig.insert(i, sSlotConfig9)
173  else :
174  raise Exception('i should not exceed nTddPeriod %d. The value of i was: {}'.format(nTddPeriod, i))
175  #print(SlotConfig, type(sSlotConfig0))
176  try:
177 
178  if (direction == 1) & (cat == 1): #UL
179  flowId = ccNum*antNumUL
180  else:
181  flowId = ccNum*antNum
182 
183  for i in range(0, flowId):
184  #read ref and test files
185  tst = []
186  ref = []
187  if direction == 0:
188  # DL
189  nRB = nDlRB
190  file_tst = xran_path+"/app/logs/"+"o-ru-rx_log_ant"+str(i)+".txt"
191  file_ref = xran_path+"/app/logs/"+"o-du-play_ant"+str(i)+".txt"
192  elif direction == 1:
193  # UL
194  nRB = nUlRB
195  file_tst = xran_path+"/app/logs/"+"o-du-rx_log_ant"+str(i)+".txt"
196  file_ref = xran_path+"/app/logs/"+"o-ru-play_ant"+str(i)+".txt"
197  else:
198  raise Exception('Direction is not supported %d'.format(direction))
199 
200  print("test result :", file_tst)
201  print("test reference:", file_ref)
202  if os.path.exists(file_tst):
203  try:
204  file_tst = open(file_tst, 'r')
205  except OSError:
206  print ("Could not open/read file:", file_tst)
207  sys.exit()
208  else:
209  print(file_tst, "doesn't exist")
210  res = -1
211  return res
212  if os.path.exists(file_ref):
213  try:
214  file_ref = open(file_ref, 'r')
215  except OSError:
216  print ("Could not open/read file:", file_ref)
217  sys.exit()
218  else:
219  print(file_tst, "doesn't exist")
220  res = -1
221  return res
222 
223  tst = file_tst.readlines()
224  ref = file_ref.readlines()
225 
226  print(len(tst))
227  print(len(ref))
228 
229  file_tst.close();
230  file_ref.close();
231 
232  print(numSlots)
233 
234  for slot_idx in range(0, numSlots):
235  for sym_idx in range(0, 14):
236  if nFrameDuplexType==1:
237  #skip sym if TDD
238  if direction == 0:
239  #DL
240  sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]
241  if(sym_dir != 0):
242  continue
243  elif direction == 1:
244  #UL
245  sym_dir = SlotConfig[slot_idx%nTddPeriod][sym_idx]
246  if(sym_dir != 1):
247  continue
248 
249  #print("Check:","[",i,"]", slot_idx, sym_idx)
250  for line_idx in range(0, nRB*12):
251  offset = (slot_idx*nRB*12*14) + sym_idx*nRB*12 + line_idx
252  line_tst = tst[offset].rstrip()
253  line_ref = ref[offset].rstrip()
254  if comp == 1:
255  # discard LSB bits as BFP compression is not Bit Exact
256  tst_i_value = int(line_tst.split(" ")[0]) & 0xFF80
257  tst_q_value = int(line_tst.split(" ")[1]) & 0xFF80
258  ref_i_value = int(line_ref.split(" ")[0]) & 0xFF80
259  ref_q_value = int(line_ref.split(" ")[1]) & 0xFF80
260 
261  print("check:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")
262  if (tst_i_value != ref_i_value) or (tst_q_value != ref_q_value) :
263  print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst: ", tst_i_value, " ", tst_q_value, " " , "ref: ", ref_i_value, " ", ref_q_value, " ")
264  res = -1
265  raise GetOutOfLoops
266  else:
267  #if line_idx == 0:
268  #print("Check:", offset,"[",i,"]", slot_idx, sym_idx,":",line_tst, line_ref)
269  if line_ref != line_tst:
270  print("FAIL:","ant:[",i,"]:",offset, slot_idx, sym_idx, line_idx,":","tst:", line_tst, "ref:", line_ref)
271  res = -1
272  raise GetOutOfLoops
273  except GetOutOfLoops:
274  pass
275 
276  return res
277 
278 def parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg):
279  #parse config files
280  logging.info("parse config files %s\n", test_cfg[0])
281  lineList = list()
282  sep = '#'
283  with open(test_cfg[0],'r') as fh:
284  for curline in dropwhile(is_comment, fh):
285  my_line = curline.rstrip().split(sep, 1)[0].strip()
286  if my_line:
287  lineList.append(my_line)
288  global_env = {}
289  local_env = {}
290 
291  for line in lineList:
292  exe_line = line.replace(":", ",")
293  if exe_line.find("/") > 0 :
294  exe_line = exe_line.replace('./', "'")
295  exe_line = exe_line+"'"
296 
297  code = compile(str(exe_line), '<string>', 'exec')
298  exec (code, global_env, local_env)
299 
300  for k, v in local_env.items():
301  globals()[k] = v
302  print(k, v)
303 
304  return local_env
305 
306 def make_copy_mlog(cat, mu, bw, tcase, xran_path):
307  res = 0
308 
309  src_bin = xran_path+"/app/mlog-o-du-c0.bin"
310  src_csv = xran_path+"/app/mlog-o-du-hist.csv"
311  dst_bin = xran_path+"/app/mlog-o-du-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"
312  dst_csv = xran_path+"/app/mlog-o-du-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"
313 
314  try:
315  d_bin = shutil.copyfile(src_bin, dst_bin)
316  d_csv = shutil.copyfile(src_csv, dst_csv)
317  except IOError:
318  logging.info("MLog is not present\n")
319  res = 1
320  else:
321  logging.info("Mlog was copied\n")
322 
323  print("Destination path:", d_bin)
324  print("Destination path:", d_csv)
325 
326  d_bin = shutil.copyfile(src_bin, dst_bin)
327  d_csv = shutil.copyfile(src_csv, dst_csv)
328 
329  #print("After copying file:")
330  #print(os.listdir(xran_path+"/app/"))
331 
332  #print("Destination path:", d_bin)
333  #print("Destination path:", d_csv)
334 
335  src_bin = xran_path+"/app/mlog-o-ru-c0.bin"
336  src_csv = xran_path+"/app/mlog-o-ru-hist.csv"
337  dst_bin = xran_path+"/app/mlog-o-ru-c0-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".bin"
338  dst_csv = xran_path+"/app/mlog-o-ru-hist-cat"+str(cat)+"-mu"+str(mu)+"-bw"+str(bw)+"-tcase"+str(tcase)+".csv"
339 
340  d_bin = shutil.copyfile(src_bin, dst_bin)
341  d_csv = shutil.copyfile(src_csv, dst_csv)
342 
343  #print("After copying file:")
344  #print(os.listdir(xran_path+"/app/"))
345 
346  #print("Destination path:", d_bin)
347  #print("Destination path:", d_csv)
348 
349  try:
350  d_bin = shutil.copyfile(src_bin, dst_bin)
351  d_csv = shutil.copyfile(src_csv, dst_csv)
352  except IOError:
353  logging.info("MLog is not present\n")
354  res = 1
355  else:
356  logging.info("Mlog was copied\n")
357 
358  #print("After copying file:")
359  #print(os.listdir(xran_path+"/app/"))
360 
361  #print("Destination path:", d_bin)
362  #print("Destination path:", d_csv)
363 
364  return res
365 
366 
367 def run_tcase(cat, mu, bw, tcase, xran_path):
368  if cat == 1:
369  test_config = xran_path+"/app/usecase/cat_b/mu{0:d}_{1:d}mhz".format(mu, bw)
370  elif cat == 0 :
371  test_config = xran_path+"/app/usecase/mu{0:d}_{1:d}mhz".format(mu, bw)
372  else:
373  print("Incorrect arguments\n")
374 
375  if(tcase > 0) :
376  test_config = test_config+"/"+str(tcase)
377 
378  app = xran_path+"/app/build/sample-app"
379 
380  logging.debug("run: %s %s", app, test_config)
381  logging.debug("Started script: master.py, XRAN path %s", xran_path)
382 
383  test_cfg = []
384  #TODO: add detection of ETH ports
385  eth_cp_dev = ["0000:22:02.1", "0000:22:0a.1"]
386  eth_up_dev = ["0000:22:02.0", "0000:22:0a.0"]
387 
388  test_cfg.append(test_config+"/config_file_o_du.dat")
389  test_cfg.append(test_config+"/config_file_o_ru.dat")
390 
391  wd = os.getcwd()
392  os.chdir(xran_path+"/app/")
393 
394  processes = []
395  logfile_xu = []
396  log_file_name = []
397 
398  os.system('rm -rf ./logs')
399 
400  for i in range(2):
401  log_file_name.append("sampleapp_log_{}_cat_{}_mu{}_{}mhz_tst_{}.log".format(dic_xu.get(i),cat, mu, bw, tcase))
402  with open(log_file_name[i], "w") as f:
403  #, stdout=f, stderr=f
404  p = subprocess.Popen([app, test_cfg[i], eth_up_dev[i], eth_cp_dev[i]], stdout=f, stderr=f)
405  logfile_xu.insert(i, f)
406  processes.append((p, logfile_xu[i]))
407 
408  logging.info("Running O-DU and O-RU see output in: %s %s\n", logfile_xu[0].name, logfile_xu[1].name)
409  for p, f in processes:
410  p.wait()
411  p.communicate()[0]
412  if p.returncode != 0:
413  print("Application {} failed p.returncode:{}".format(dic_xu.get(i), p.returncode))
414  print("FAIL")
415  logging.info("FAIL\n")
416  logging.shutdown()
417  sys.exit(p.returncode)
418 
419  f.close()
420 
421  logging.info("O-DU and O-RU are done\n")
422 
423  make_copy_mlog(cat, mu, bw, tcase, xran_path)
424 
425  usecase_cfg = parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg)
426 
427  res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 0)
428  if res != 0:
429  os.chdir(wd)
430  print("FAIL")
431  return res
432 
433  res = compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, 1)
434  if res != 0:
435  os.chdir(wd)
436  print("FAIL")
437  return res
438 
439  os.chdir(wd)
440  print("PASS")
441  return res
442 
443 def main():
444  test_results = []
445  test_executed_total = 0
446  run_total = 0
447  cat = 0
448  mu = 0
449  bw = 0
450  tcase = 0
451  """Processes input files to produce IACA files"""
452  # Find path to XRAN
453  xran_path = os.getenv("XRAN_DIR")
454 
455  # Set up logging with given level (DEBUG, INFO, ERROR) for console end logfile
456  init_logger(logging.INFO, logging.DEBUG)
457  logging.info("Started script: master.py, XRAN path %s", xran_path)
458 
459  # Parse input arguments
460  if len(sys.argv) == 1 :
461  run_total = len(all_test_cases)
462  print(run_total)
463  print("Run All test cases {}\n".format(run_total))
464  else:
465  options = parse_args(sys.argv[1:])
466  cat = options.category
467  mu = options.numerology
468  bw = options.bandwidth
469  tcase = options.testcase
470 
471 
472  if (run_total):
473  for test_run_ix in range(0, run_total):
474  cat = all_test_cases[test_run_ix][0]
475  mu = all_test_cases[test_run_ix][1]
476  bw = all_test_cases[test_run_ix][2]
477  tcase = all_test_cases[test_run_ix][3]
478 
479  res = run_tcase(cat, mu, bw, tcase, xran_path)
480  if (res != 0):
481  test_results.append((cat, mu, bw, tcase,'FAIL'))
482  break;
483 
484  test_results.append((cat, mu, bw, tcase,'PASS'))
485  else:
486  res = run_tcase(cat, mu, bw, tcase, xran_path)
487  if (res != 0):
488  test_results.append((cat, mu, bw, tcase,'FAIL'))
489  test_results.append((cat, mu, bw, tcase,'PASS'))
490 
491  with open('testresult.txt', 'w') as reshandle:
492  json.dump(test_results, reshandle)
493 
494  return res
495 
496 if __name__ == '__main__':
497  START_TIME = datetime.now()
498  res = main()
499  END_TIME = datetime.now()
500  logging.debug("Start time: %s, end time: %s", START_TIME, END_TIME)
501  logging.info("Execution time: %s", END_TIME - START_TIME)
502  logging.shutdown()
503  sys.exit(res)
def is_comment(s)
Definition: master.py:112
def compare_resuts(cat, mu, bw, tcase, xran_path, test_cfg, direction)
Definition: master.py:123
def run_tcase(cat, mu, bw, tcase, xran_path)
Definition: master.py:367
def parse_dat_file(cat, mu, bw, tcase, xran_path, test_cfg)
Definition: master.py:278
def main()
Definition: master.py:443
def make_copy_mlog(cat, mu, bw, tcase, xran_path)
Definition: master.py:306
def init_logger(console_level, logfile_level)
Definition: master.py:80
def parse_args(args)
Definition: master.py:95