| #!/usr/bin/env python |
| # |
| # Copyright (c) 2020, The OpenThread Authors. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # 3. Neither the name of the copyright holder nor the |
| # names of its contributors may be used to endorse or promote products |
| # derived from this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| # POSSIBILITY OF SUCH DAMAGE. |
| # |
| """ |
| parse_topofile.py |
| ----------------- |
| This script is used to parse TopologyConfig.txt file to list vendor device info |
| when preparing for the Thread Certification testbed |
| |
| usage: parse_topofile.py [-h] [-f TOPO_FILE] [-c CASE_LIST [CASE_LIST ...]] |
| |
| parse TopologyConfig file and list all devices by case |
| |
| optional arguments: |
| -h, --help show this help message and exit |
| -f TOPO_FILE Topology config file (default: C:/GRL/Thread1.1/Thread |
| _Harness/TestScripts/TopologyConfig.txt) |
| -c CASE_LIST [CASE_LIST ...] |
| Test case list (e.g. 5.1.1 9.2.1, default: all) |
| |
| Examples: |
| 1. Get case 5.1.1 vendor info |
| cmd: python parse_topofile.py -f TopologyConfig.txt -c 5.1.1 |
| output: |
| case 5.1.1: |
| role-vendor pair: [('Leader', 'ARM'), ('Router_1', 'ARM')] |
| vendor devices : {'ARM': 2} |
| |
| Testbed needed vendor devices:{'ARM': 2} |
| |
| 2. Get case 5.1.1 and 5.2.1 vendor info |
| cmd: python parse_topofile.py -f TopologyConfig.txt -c 5.1.1 5.2.1 |
| output: |
| case 5.1.1: |
| role-vendor pair: [('Leader', 'ARM'), ('Router_1', 'ARM')] |
| vendor devices : {'ARM': 2} |
| case 5.2.1: |
| role-vendor pair: [('Leader', 'OpenThread'), ('REED_1', 'Kirale'), ('MED_1', 'SiLabs')] |
| vendor devices : {'OpenThread': 1, 'Kirale': 1, 'SiLabs': 1} |
| |
| Testbed needed vendor devices:{'ARM': 2, 'OpenThread': 1, 'Kirale': 1, 'SiLabs': 1} |
| |
| 3. Get all cases vendor info |
| cmd: python parse_topofile.py -f TopologyConfig.txt |
| output: |
| ... ... ... |
| |
| Testbed needed vendor devices:{'ARM': 4, 'NXP': 5, 'OpenThread': 5, 'Kirale': 6, 'SiLabs': 5, 'Any': 7} |
| |
| Notes: |
| The result is just for reference, the exact requirement per vendor may be equal or less than what is generated by the script. |
| |
| """ |
| |
| import argparse |
| import logging |
| import re |
| from collections import Counter |
| |
| logging.basicConfig(format='%(message)s', level=logging.DEBUG) |
| MAX_VENDOR_DEVICE = 32 |
| |
| |
| def device_calculate(topo_file, case_list): |
| testbed_vendor_dict = Counter() |
| with open(topo_file, 'r') as f: |
| for line in f: |
| |
| case_vendor_dict = Counter() |
| |
| if not line: |
| break |
| line = line.strip() |
| |
| # line example : |
| # 5.5.1-Leader:Kirale,Router_1:OpenThread |
| try: |
| if re.match(r'\s*#.*', line): |
| continue |
| |
| matched_case = re.match(r'(.*)-(.*)', line, re.M | re.I) |
| |
| if 'all' not in case_list and matched_case.group(1) not in case_list: |
| continue |
| |
| logging.info('case %s:' % matched_case.group(1)) |
| if 'all' not in case_list: |
| case_list.remove(matched_case.group(1)) |
| role_vendor_str = matched_case.group(2) |
| role_vendor_raw_list = re.split(',', role_vendor_str) |
| role_vendor_list = [] |
| |
| for device_pair in role_vendor_raw_list: |
| device_pair = re.split(':', device_pair) |
| role_vendor_list.append(tuple(device_pair)) |
| logging.info('\trole-vendor pair: %s' % role_vendor_list) |
| except Exception as e: |
| logging.info('Unrecognized format: %s\n%s' % (line, format(e))) |
| raise |
| |
| for _, vendor in role_vendor_list: |
| case_vendor_dict[vendor] += 1 |
| testbed_vendor_dict[vendor] = max(testbed_vendor_dict[vendor], case_vendor_dict[vendor]) |
| |
| logging.info('\tvendor devices : %s' % dict(case_vendor_dict)) |
| |
| if case_list and 'all' not in case_list: |
| logging.info('Case %s not found' % str(case_list)[1:-1]) |
| |
| # vendor 'Any' stands for any other vendors |
| # override 'Any' counter when overlapping with other vendors |
| count_any = MAX_VENDOR_DEVICE |
| for key in testbed_vendor_dict: |
| if key != 'Any': |
| count_any -= testbed_vendor_dict[key] |
| if 'Any' in testbed_vendor_dict: |
| testbed_vendor_dict['Any'] = count_any |
| |
| logging.info('\nTestbed needed vendor devices:%s' % dict(testbed_vendor_dict)) |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser(description='parse TopologyConfig file and list all devices by case') |
| parser.add_argument( |
| '-f', |
| dest='topo_file', |
| default='C:/GRL/Thread1.1/Thread_Harness/TestScripts/TopologyConfig.txt', |
| help='Topology config file (default: C:/GRL/Thread1.1/Thread_Harness/TestScripts/TopologyConfig.txt)', |
| ) |
| |
| parser.add_argument('-c', |
| dest='case_list', |
| nargs='+', |
| default=['all'], |
| help='Test case list (e.g. 5.1.1 9.2.1, default: all) ') |
| args = parser.parse_args() |
| device_calculate(args.topo_file, args.case_list) |
| |
| |
| if __name__ == '__main__': |
| main() |