aboutsummaryrefslogtreecommitdiffstats
path: root/extras
AgeCommit message (Collapse)AuthorFilesLines
2020-08-31vpp_config: update node variants to skx and iclRay Kinsella1-2/+2
Node variant names have changed to skx and icl. Type: fix Signed-off-by: Ray Kinsella <mdr@ashroe.eu> Change-Id: I5be832aa59e4e440df53869077d801c011e3defa
2020-08-31af_xdp: AF_XDP input pluginBenoît Ganne2-0/+105
Type: feature Change-Id: I85aa4ad6b68c1aa0e51938002dc691a4b11c545c Signed-off-by: Damjan Marion <damarion@cisco.com> Signed-off-by: Benoît Ganne <bganne@cisco.com>
2020-08-11vppapigen: add support for empty optionsOle Troan1-1/+1
Type: improvement Signed-off-by: Ole Troan <ot@cisco.com> Change-Id: I59323447bee7bb4f0563251a5df80cbefb9bd89e Signed-off-by: Ole Troan <ot@cisco.com>
2020-08-06bash: fix pip install issue with jjb-sandbox-env functionDave Wallace1-1/+2
- In a new system (e.g. ubuntu 18.04 docker image), the pip3 installation of jenkins-job-builder fails unless pip3 wheel is completed on a previous pip3 install command. Type: fix Change-Id: I4354c851161b9c470d44951dc8b722ffc87f56f7 Signed-off-by: Dave Wallace <dwallacelf@gmail.com>
2020-08-06misc: harmonize namesDave Barach1-2/+2
Type: fix Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Ibad744788e200ce012ad88ff59c2c34920742454
2020-07-02nat: nat66 to pluginOle Troan3-10/+9
Type: refactor Change-Id: I8c1f0c02a4522c1f9e461ddadd59938579ec00c6 Signed-off-by: Ole Troan <ot@cisco.com>
2020-06-25misc: add gdb macrosDave Barach1-0/+100
These gdb macros should prove very helpul when poking around in core files. Pifi (pool_is_free_index) is not straighforward. Best to work it out once. Others: bitmap_get = clib_bitmap_get vl = vec_len pe = pool_elts node_name_from_index, as described vnet_buffer_opaque, prints the primary buffer opaque vnet_buffer_opaque2, prints the secondary buffer opaque Fix vppinfra unit-test compile error Type: improvement Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Id2a2391a47e5a07cf3757f473e3805cc04784161
2020-06-21vpp_config: Updates for 20.05John DeNisco8-98/+142
Added external dependancies package. Updated the startup.conf template. Fix extras/scripts/check_commit_msg so it accepts '_' characters in feature names. Type: fix Signed-off-by: John DeNisco <jdenisco@cisco.com> Change-Id: I69c33a24b30d31e5b8081021030bff88cbaeace9 Signed-off-by: Dave Barach <dave@barachs.net>
2020-06-02bash: functions to set up csit and sandbox envAndrew Yourtchenko1-6/+115
Type: test Change-Id: Iceaebfe2faf29a893b9571069212e951273c3d2b Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com> Signed-off-by: John DeNisco <jdenisco@cisco.com> Signed-off-by: Dave Wallace <dwallacelf@gmail.com>
2020-05-28misc: Avoid sample.md from in the VPP docs, clean up the extraneous copyAndrew Yourtchenko2-82/+0
I had cleaned up the sample.md previously for 20.01, but when later that was merged to master, the file was renamed. So, fix this issue. Change-Id: I6347685af216901fbfdd445606735b9bf79f8fe5 Type: docs Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com> (cherry picked from commit e2e3c38be6d91c3deb15373992cd5a764e773060)
2020-05-27ethernet: fix DMAC check and skip unnecessary ones (VPP-1868)John Lo1-2/+4
Fix and optimize DMAC check in ethernet-input node to utilize NIC or driver which support L3 DMAC-filtering mode so that DMAC check can be bypassed safely for interfaces/sub-interfaces in L3 mode. Checking of interface in L3-DMAC-filtering state to avoid DMAC check require the following: a) Fix interface driver init sequence for devices which supports L3 DMAC-filtering to indicate its capability and initialize interface to L3 DMAC-filtering state. b) Fix ethernet_set_flags() function and its associated callback flags_change() functions registered by various drivers in interface infra to provide proper L3 DMAC filtering status. Maintain interface/sub-interface L3 config count so DMAC checks can be bypassed if L3 forwarding is not setup on any main/sub-interfaces. Type: fix Ticket: VPP-1868 Signed-off-by: John Lo <loj@cisco.com> Change-Id: I204d90459c13e9e486cfcba4e64e3d479bc9f2ae
2020-05-19vppapigen: handle new api file for crcchekcerOle Troan1-0/+11
Type: fix Signed-off-by: Ole Troan <ot@cisco.com> Change-Id: I1b3ae8b348eb3cba628a55ea3e72e85b9a4288d5
2020-05-18libmemif: cleanup typosPaul Vinciguerra7-42/+42
cleaned up typos in documetation while doing a read through. Type: style Change-Id: Id8abbd8d6297ee10490c12a8e62097224f7ec1e1 Signed-off-by: Paul Vinciguerra <pvinci@vinciconsulting.com>
2020-05-15misc: removed executable bits from source filesRay Kinsella1-0/+0
Identified and removed executable bit from source files in the tree. find . -perm 755 -name *.[ch] -exec chmod a-x {} \; Type: improvement Signed-off-by: Ray Kinsella <mdr@ashroe.eu> Change-Id: I00710d59fcc46ce5be5233109af4c8077daff74b
2020-05-12misc: remove useless assignmentDave Barach1-1/+0
Type: fix Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: I7708587804bc979fa9c46fb11f96d264821e2357
2020-05-09vppapigen: api crc checkerOle Troan2-0/+343
crcchecker is a tool for enforcement of the binary API. 1. Production APIs should never change. 2. An API can be deprecated across three release cycles. Release 1: API is marked as deprecated. option deprecated="vyy.mm"; option replaced_by="new_api_2"; Release 2: both old and new APIs are supported Release 3: the deprecated API is deleted. 3. APIs that are experimental / not released are not checked. An API message can be individually marked as in progress, by: option status="in_progress"; In the API definition. The definition of a "production API" is if the major version in the API file is > 0. extras/scripts/crcchecker.py --check-patchset # returns -1 if backwards incompatible extras/scripts/crcchecker.py --dump-manifest extras/scripts/crcchecker.py --git-revision v20.01 <files> extras/scripts/crcchecker.py -- diff <oldfile> <newfile> This patch integrates the tool in "make checkstyle-api". A future patch is required to integrate the tool in the verify job. I.e. this patch does not enable enforcement through Jenkins. Change-Id: I5f13c0976d8a12a58131b3e270f2dc9c00dc7d8c Type: feature Signed-off-by: Ole Troan <ot@cisco.com>
2020-05-08build: various improvementsDamjan Marion4-26/+91
- add option to install only host tools - add option to specify lib and runtime dir Type: improvement Change-Id: I6356b52df459120fc9b0127948bae7679fb10e52 Signed-off-by: Damjan Marion <damarion@cisco.com>
2020-05-07misc: deprecate elftoolDamjan Marion6-3/+500
Type: make Change-Id: I34fec1c40ef660177c266517eaf41d60827609f4 Signed-off-by: Damjan Marion <damarion@cisco.com>
2020-05-06docs: clean up make docs jobPaul Vinciguerra2-2/+2
Type: docs Change-Id: I9b5e5137eb4c1e89f6e8d7a278cd11a0fd496471 Signed-off-by: Paul Vinciguerra <pvinci@vinciconsulting.com>
2020-04-30build: rework x86 CPU variantsDamjan Marion1-2/+2
Type: improvement Change-Id: Ief243f88e654e578ef9b8060fcf535b364aececb Signed-off-by: Damjan Marion <damarion@cisco.com>
2020-04-28gomemif: introduce gomemifJakub Grajciar14-0/+2744
golang native memif driver Type: feature Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com> Change-Id: I693156a44011c80025245d25134f5bf5db6eba82 Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
2020-04-28vppinfra: improve test coverageDave Barach1-1/+4
Bonus corner-case bugfix in bitmap.h, found during the exercise. Issue dates from 2001 or thereabouts. Please review this specific change carefully. lcov_post: filter system include directories and generated files in build-root Type: improvement Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Iaa0b63e9dc571dfe3d992197ac49ba4d93403c61
2020-04-28vlib: startup multi-arch variant configurationRay Kinsella1-0/+10
Support for startup node multi-arch variant selection through startup.conf. This is to facilitate unit, functional testing and benchmarking of non-default multi-arch variant node code path. Also added parameters to make test, to specific using multi-arch variants in unit testing. Type: improvement Signed-off-by: Ray Kinsella <mdr@ashroe.eu> Change-Id: I94fd332bb629683b7a7dd770ee9f615a9a424060
2020-04-27vlib: deprecate i2c and cjDave Barach4-0/+661
i2c follows its only use case - the original 82599 driver - into extras/deprecated. cj is/was an emergency debug tool unused in several years. Move to extras/deprecated/vlib Type: refactor Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Ib55b65373f62630db295c562974bd8f2456c3107
2020-04-24vppinfra: finish deprecating qsort.cDave Barach1-0/+269
Minor change to vec_sort_with_function(...): don't depend on the qsort implementation to deal with null, zero-long, or 1-long vectors Type: fix Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: I7bd7b0421673d2a025363089562aa7c6266fba66
2020-04-23misc: Markdown cleanups for the 20.01 releaseAndrew Yourtchenko2-0/+84
Type: docs Signed-off-by: Andrew Yourtchenko <ayourtch@gmail.com> Change-Id: I821197364a2fee9b52b1f014288b1f5e9e3c494c (cherry picked from commit fc98203b5d06f19d613766815660e76c9f216f09)
2020-04-22vppinfra: move unused code to extras/deprecated/vppinfraDave Barach36-0/+12360
Type: improvement Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: Id28299a188feefa1899d835fd499f018af95d81b
2020-04-15pg: set vnet buffer flags in pg streamsDave Barach2-0/+38
See .../extras/pg/checksum_offload.pg for a nontrivial example, which deliberately sets bogus ip and udp checksums in the generated packets, then fixes the mess with (software emulated) hardware checksum offload. Validated via "pcap dispatch trace on max 1000 buffer-trace pg-input 100". Packets stuffed into loop1-output have the configured bogus ip and udp checksums. vnet_calc_checksums_inline(...) fixes the checksums, which are correct when packets visit loop1-tx. The packet generator is a dumb robot in this regard. If you ask for a ridiculous flag combination - example: ip4 and ip6 - your mileage may vary. Type: feature Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: I6d9e790f373bcd9e445a89113ca8e4c8f9aa9419
2020-04-09vom: Fixes for g++-9neale ranns1-6/+5
Type: improvement Signed-off-by: neale ranns <nranns@cisco.com> Change-Id: I356251b750fcab05ff91e0295e96a8451e8b2f88
2020-04-06misc: fix python sonarcloud BLOCKER level issuesPaul Vinciguerra2-58/+58
Fix of the top 11 python issues flagged as BLOCKER. Ticket: VPP-1856 Type: fix Change-Id: Icf4691e62f4a69d6ee196b6d6e2ab52d961b5c76 Signed-off-by: Paul Vinciguerra <pvinci@vinciconsulting.com>
2020-04-03misc: fix sonarcloud html issueDave Wallace1-1/+1
Type: fix Change-Id: I3691c310fa6336bf2c103c42bccd94c27aab4878 Signed-off-by: Dave Wallace <dwallacelf@gmail.com>
2020-03-27acl: API cleanupJakub Grajciar3-18/+14
Use consistent API types. Type: fix Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com> Change-Id: I09fa6c1b6917936351bd376b56c414ce24488095 Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
2020-03-26acl: revert acl: api cleanupOle Trøan3-22/+19
This reverts commit aad1ee149403994194cf37cef4530b042ba7df3a. Reason for revert: Verify failure. Doesn't build. Type: fix Change-Id: I91b1b26ac43edde4853e4561a0083d0b3a06efee Signed-off-by: Ole Troan <ot@cisco.com>
2020-03-26acl: API cleanupJakub Grajciar3-47/+45
Use consistent API types. Type: fix Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com> Change-Id: If90d753f129312400c4c3669bb86289d0c3e0d99 Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
2020-03-21libmemif: don't consume rx queue interrupt, if memif_rx_burst failsJan Cavojsky3-5/+16
Type: fix Signed-off-by: Jan Cavojsky <jan.cavojsky@pantheon.tech> Change-Id: Ic6c15c88c80c4045c290cd78533be6f070c7470a
2020-03-18build: add snap packaging (experimental)Dave Barach2-0/+66
Type: feature Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: I5a5efde5378f63d89d82d71ae009c7595aaa800c
2020-03-16vxlan: vxlan/vxlan.api API cleanupJakub Grajciar2-36/+49
Use consistent API types. Type: fix Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com> Change-Id: I7f6f37ec6eed780322e2488d6eb0f5681945ba09 Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
2020-03-06misc: Fix vpp-selinux-policy dependency on CentOS 8Renato Botelho do Couto1-1/+6
policycoreutils-python is now called python3-policycoreutils on CentOS 8. Type: fix Signed-off-by: Renato Botelho do Couto <renato@netgate.com> Change-Id: I46264c66a2a719d546e0926f3bd716e986461963
2020-02-26lldp: API cleanupJakub Grajciar2-10/+8
Use consistent API types. Type: fix Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com> Change-Id: I2fe6b56dc0f224f438ed5f14c0f276d94afbaea8 Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
2020-02-26span: API cleanupJakub Grajciar1-9/+5
Use consistent API types. Type: fix Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com> Change-Id: Ifddac07b15b116574de3a97f7c80d7bf6a3171f7 Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com>
2020-02-05stats: fix state counter removalVladimir Isaev2-13/+9
Avoid using vec_del1() for directory vector to keep indexes valid all the time. There are state counters for each slave in LACP bond mode which can be dynamically created and removed. Vector index is used to access these counters. But also vec_del1() is used to remove counter from vector. This function changes the index of the last element, so after this we are unable to access ex-last element using old index. As a result it is not possible to add-del-add two interfaces to the LACP bond: DBGvpp# create bond mode lacp BondEthernet0 DBGvpp# create packet-generator interface pg1 DBGvpp# create packet-generator interface pg2 DBGvpp# bond add BondEthernet0 pg1 DBGvpp# bond add BondEthernet0 pg2 DBGvpp# bond del pg1 DBGvpp# bond del pg2 DBGvpp# bond add BondEthernet0 pg1 DBGvpp# bond add BondEthernet0 pg2 bond add: /if/lacp/1/3/partner-state is already register Type: fix Signed-off-by: Vladimir Isaev <visaev@netgate.com> Change-Id: I2c86e13905eefdef6233369cd4ab5c1b53d123bd
2020-02-05misc: add new type for commit messageMohsin Kazmi1-1/+1
Type: style Change-Id: Ibfa8bd1c0987fd2a5050be6c454f665666eb0210 Signed-off-by: Mohsin Kazmi <sykazmi@cisco.com>
2020-01-30misc: deprecate dpdk hqosDamjan Marion6-0/+3523
Not in functional state for a long time ... Type: refactor Change-Id: I2cc1525a6d49518cbc94faf6afbf0d2d0d515f56 Signed-off-by: Damjan Marion <damarion@cisco.com>
2020-01-30misc: deprecate netmap and ixge driversDamjan Marion13-0/+6398
Both are out of sync for long time... Type: refactor Change-Id: I7de3170d35330fc172501d87655dfef91998b8fe Signed-off-by: Damjan Marion <damarion@cisco.com>
2020-01-30misc: move configs to extras/configsDamjan Marion8-0/+0
Type: refactor Change-Id: I64665b290e2c42bbd9b0e877e9e4b028090b0ede Signed-off-by: Damjan Marion <damarion@cisco.com>
2020-01-27classify: pcap / packet trace debug CLI bugsDave Barach1-1/+3
"classify filter trace ... " and "classify filter pcap ..." are mutually exclusive. vnet_pcap_dispatch_trace_configure needs to check for set->table_indices == NULL. Type: fix Ticket: VPP-1827 Signed-off-by: Dave Barach <dave@barachs.net> Change-Id: I43733364087ffb0a43de92e450955033431d559d
2020-01-27libmemif: memif_control_fd_update always pass context from libmemif_mainJakub Grajciar3-17/+23
Event polling instance is always identified by libmemif main private context. Fixes event polling handled by libmemif. Type: fix Signed-off-by: Jakub Grajciar <jgrajcia@cisco.com> Change-Id: I51dcdb279b18f8ce97bad3b2695848e0b25a232d
2020-01-03build: Fix Subject-line feature list extractionJon Loeliger1-1/+2
Bang on sed until a better incantation appears. Change-Id: Ib8ad0996b6325db0fe983c86dd7dc553c9d388c4 Type: fix Fixes: 26ce6ca1fe6f524a9049444fe8d55042fd7530a6 Signed-off-by: Jon Loeliger <jdl@netgate.com>
2019-12-19bash: add -j <jobs> option to vpp-make-testDave Wallace1-7/+18
- Also cleanup local var declarations - remove extraneous export statement Type: test Change-Id: I22198a65d1785ba53d1de22b2aa498cab910701b Signed-off-by: Dave Wallace <dwallacelf@gmail.com>
2019-12-17misc: Add CentOS 8 package supportRenato Botelho do Couto1-4/+10
Type: feature Added missing dependencies on RPM spec file and install-dep Ticket: VPP-1800 Signed-off-by: Renato Botelho do Couto <renato@netgate.com> Change-Id: I91d39c94b3f03c213249dff42b264718ef772bdb
"p">(self[PASS] + self[SKIP] + self[SKIP_CPU_SHORTAGE]) \ == self.testcase_suite.countTestCases() def no_tests_run(self): return 0 == len(self[TEST_RUN]) def process_result(self, test_id, result): self[result].append(test_id) def suite_from_failed(self): rerun_ids = set([]) for testcase in self.testcase_suite: tc_id = testcase.id() if tc_id not in self[PASS] + self[SKIP] + self[SKIP_CPU_SHORTAGE]: rerun_ids.add(tc_id) if rerun_ids: return suite_from_failed(self.testcase_suite, rerun_ids) def get_testcase_names(self, test_id): # could be tearDownClass (test_ipsec_esp.TestIpsecEsp1) setup_teardown_match = re.match( r'((tearDownClass)|(setUpClass)) \((.+\..+)\)', test_id) if setup_teardown_match: test_name, _, _, testcase_name = setup_teardown_match.groups() if len(testcase_name.split('.')) == 2: for key in self.testcases_by_id.keys(): if key.startswith(testcase_name): testcase_name = key break testcase_name = self._get_testcase_doc_name(testcase_name) else: test_name = self._get_test_description(test_id) testcase_name = self._get_testcase_doc_name(test_id) return testcase_name, test_name def _get_test_description(self, test_id): if test_id in self.testcases_by_id: desc = get_test_description(descriptions, self.testcases_by_id[test_id]) else: desc = test_id return desc def _get_testcase_doc_name(self, test_id): if test_id in self.testcases_by_id: doc_name = get_testcase_doc_name(self.testcases_by_id[test_id]) else: doc_name = test_id return doc_name def test_runner_wrapper(suite, keep_alive_pipe, stdouterr_queue, finished_pipe, result_pipe, logger): sys.stdout = stdouterr_queue sys.stderr = stdouterr_queue VppTestCase.parallel_handler = logger.handlers[0] result = VppTestRunner(keep_alive_pipe=keep_alive_pipe, descriptions=descriptions, verbosity=verbose, result_pipe=result_pipe, failfast=failfast, print_summary=False).run(suite) finished_pipe.send(result.wasSuccessful()) finished_pipe.close() keep_alive_pipe.close() class TestCaseWrapper(object): def __init__(self, testcase_suite, manager): self.keep_alive_parent_end, self.keep_alive_child_end = Pipe( duplex=False) self.finished_parent_end, self.finished_child_end = Pipe(duplex=False) self.result_parent_end, self.result_child_end = Pipe(duplex=False) self.testcase_suite = testcase_suite self.stdouterr_queue = manager.StreamQueue(ctx=get_context()) self.logger = get_parallel_logger(self.stdouterr_queue) self.child = Process(target=test_runner_wrapper, args=(testcase_suite, self.keep_alive_child_end, self.stdouterr_queue, self.finished_child_end, self.result_child_end, self.logger) ) self.child.start() self.last_test_temp_dir = None self.last_test_vpp_binary = None self._last_test = None self.last_test_id = None self.vpp_pid = None self.last_heard = time.time() self.core_detected_at = None self.testcases_by_id = {} self.testclasess_with_core = {} for testcase in self.testcase_suite: self.testcases_by_id[testcase.id()] = testcase self.result = TestResult(testcase_suite, self.testcases_by_id) @property def last_test(self): return self._last_test @last_test.setter def last_test(self, test_id): self.last_test_id = test_id if test_id in self.testcases_by_id: testcase = self.testcases_by_id[test_id] self._last_test = testcase.shortDescription() if not self._last_test: self._last_test = str(testcase) else: self._last_test = test_id def add_testclass_with_core(self): if self.last_test_id in self.testcases_by_id: test = self.testcases_by_id[self.last_test_id] class_name = unittest.util.strclass(test.__class__) test_name = "'{}' ({})".format(get_test_description(descriptions, test), self.last_test_id) else: test_name = self.last_test_id class_name = re.match(r'((tearDownClass)|(setUpClass)) ' r'\((.+\..+)\)', test_name).groups()[3] if class_name not in self.testclasess_with_core: self.testclasess_with_core[class_name] = ( test_name, self.last_test_vpp_binary, self.last_test_temp_dir) def close_pipes(self): self.keep_alive_child_end.close() self.finished_child_end.close() self.result_child_end.close() self.keep_alive_parent_end.close() self.finished_parent_end.close() self.result_parent_end.close() def was_successful(self): return self.result.was_successful() @property def cpus_used(self): return self.testcase_suite.cpus_used def get_assigned_cpus(self): return self.testcase_suite.get_assigned_cpus() def stdouterr_reader_wrapper(unread_testcases, finished_unread_testcases, read_testcases): read_testcase = None while read_testcases.is_set() or unread_testcases: if finished_unread_testcases: read_testcase = finished_unread_testcases.pop() unread_testcases.remove(read_testcase) elif unread_testcases: read_testcase = unread_testcases.pop() if read_testcase: data = '' while data is not None: sys.stdout.write(data) data = read_testcase.stdouterr_queue.get() read_testcase.stdouterr_queue.close() finished_unread_testcases.discard(read_testcase) read_testcase = None def handle_failed_suite(logger, last_test_temp_dir, vpp_pid): if last_test_temp_dir: # Need to create link in case of a timeout or core dump without failure lttd = os.path.basename(last_test_temp_dir) failed_dir = os.getenv('FAILED_DIR') link_path = '%s%s-FAILED' % (failed_dir, lttd) if not os.path.exists(link_path): os.symlink(last_test_temp_dir, link_path) logger.error("Symlink to failed testcase directory: %s -> %s" % (link_path, lttd)) # Report core existence core_path = get_core_path(last_test_temp_dir) if os.path.exists(core_path): logger.error( "Core-file exists in test temporary directory: %s!" % core_path) check_core_path(logger, core_path) logger.debug("Running 'file %s':" % core_path) try: info = check_output(["file", core_path]) logger.debug(info) except CalledProcessError as e: logger.error("Subprocess returned with return code " "while running `file' utility on core-file " "returned: " "rc=%s", e.returncode) except OSError as e: logger.error("Subprocess returned with OS error while " "running 'file' utility " "on core-file: " "(%s) %s", e.errno, e.strerror) except Exception as e: logger.exception("Unexpected error running `file' utility " "on core-file") logger.error("gdb %s %s" % (os.getenv('VPP_BIN', 'vpp'), core_path)) if vpp_pid: # Copy api post mortem api_post_mortem_path = "/tmp/api_post_mortem.%d" % vpp_pid if os.path.isfile(api_post_mortem_path): logger.error("Copying api_post_mortem.%d to %s" % (vpp_pid, last_test_temp_dir)) shutil.copy2(api_post_mortem_path, last_test_temp_dir) def check_and_handle_core(vpp_binary, tempdir, core_crash_test): if is_core_present(tempdir): if debug_core: print('VPP core detected in %s. Last test running was %s' % (tempdir, core_crash_test)) print(single_line_delim) spawn_gdb(vpp_binary, get_core_path(tempdir)) print(single_line_delim) elif compress_core: print("Compressing core-file in test directory `%s'" % tempdir) os.system("gzip %s" % get_core_path(tempdir)) def handle_cores(failed_testcases): for failed_testcase in failed_testcases: tcs_with_core = failed_testcase.testclasess_with_core if tcs_with_core: for test, vpp_binary, tempdir in tcs_with_core.values(): check_and_handle_core(vpp_binary, tempdir, test) def process_finished_testsuite(wrapped_testcase_suite, finished_testcase_suites, failed_wrapped_testcases, results): results.append(wrapped_testcase_suite.result) finished_testcase_suites.add(wrapped_testcase_suite) stop_run = False if failfast and not wrapped_testcase_suite.was_successful(): stop_run = True if not wrapped_testcase_suite.was_successful(): failed_wrapped_testcases.add(wrapped_testcase_suite) handle_failed_suite(wrapped_testcase_suite.logger, wrapped_testcase_suite.last_test_temp_dir, wrapped_testcase_suite.vpp_pid) return stop_run def run_forked(testcase_suites): wrapped_testcase_suites = set() solo_testcase_suites = [] # suites are unhashable, need to use list results = [] unread_testcases = set() finished_unread_testcases = set() manager = StreamQueueManager() manager.start() tests_running = 0 free_cpus = list(available_cpus) def on_suite_start(tc): nonlocal tests_running nonlocal free_cpus tests_running = tests_running + 1 def on_suite_finish(tc): nonlocal tests_running nonlocal free_cpus tests_running = tests_running - 1 assert tests_running >= 0 free_cpus.extend(tc.get_assigned_cpus()) def run_suite(suite): nonlocal manager nonlocal wrapped_testcase_suites nonlocal unread_testcases nonlocal free_cpus suite.assign_cpus(free_cpus[:suite.cpus_used]) free_cpus = free_cpus[suite.cpus_used:] wrapper = TestCaseWrapper(suite, manager) wrapped_testcase_suites.add(wrapper) unread_testcases.add(wrapper) on_suite_start(suite) def can_run_suite(suite): return (tests_running < max_concurrent_tests and (suite.cpus_used <= len(free_cpus) or suite.cpus_used > max_vpp_cpus)) while free_cpus and testcase_suites: a_suite = testcase_suites[0] if a_suite.is_tagged_run_solo: a_suite = testcase_suites.pop(0) solo_testcase_suites.append(a_suite) continue if can_run_suite(a_suite): a_suite = testcase_suites.pop(0) run_suite(a_suite) else: break if tests_running == 0 and solo_testcase_suites: a_suite = solo_testcase_suites.pop(0) run_suite(a_suite) read_from_testcases = threading.Event() read_from_testcases.set() stdouterr_thread = threading.Thread(target=stdouterr_reader_wrapper, args=(unread_testcases, finished_unread_testcases, read_from_testcases)) stdouterr_thread.start() failed_wrapped_testcases = set() stop_run = False try: while wrapped_testcase_suites: finished_testcase_suites = set() for wrapped_testcase_suite in wrapped_testcase_suites: while wrapped_testcase_suite.result_parent_end.poll(): wrapped_testcase_suite.result.process_result( *wrapped_testcase_suite.result_parent_end.recv()) wrapped_testcase_suite.last_heard = time.time() while wrapped_testcase_suite.keep_alive_parent_end.poll(): wrapped_testcase_suite.last_test, \ wrapped_testcase_suite.last_test_vpp_binary, \ wrapped_testcase_suite.last_test_temp_dir, \ wrapped_testcase_suite.vpp_pid = \ wrapped_testcase_suite.keep_alive_parent_end.recv() wrapped_testcase_suite.last_heard = time.time() if wrapped_testcase_suite.finished_parent_end.poll(): wrapped_testcase_suite.finished_parent_end.recv() wrapped_testcase_suite.last_heard = time.time() stop_run = process_finished_testsuite( wrapped_testcase_suite, finished_testcase_suites, failed_wrapped_testcases, results) or stop_run continue fail = False if wrapped_testcase_suite.last_heard + test_timeout < \ time.time(): fail = True wrapped_testcase_suite.logger.critical( "Child test runner process timed out " "(last test running was `%s' in `%s')!" % (wrapped_testcase_suite.last_test, wrapped_testcase_suite.last_test_temp_dir)) elif not wrapped_testcase_suite.child.is_alive(): fail = True wrapped_testcase_suite.logger.critical( "Child test runner process unexpectedly died " "(last test running was `%s' in `%s')!" % (wrapped_testcase_suite.last_test, wrapped_testcase_suite.last_test_temp_dir)) elif wrapped_testcase_suite.last_test_temp_dir and \ wrapped_testcase_suite.last_test_vpp_binary: if is_core_present( wrapped_testcase_suite.last_test_temp_dir): wrapped_testcase_suite.add_testclass_with_core() if wrapped_testcase_suite.core_detected_at is None: wrapped_testcase_suite.core_detected_at = \ time.time() elif wrapped_testcase_suite.core_detected_at + \ core_timeout < time.time(): wrapped_testcase_suite.logger.critical( "Child test runner process unresponsive and " "core-file exists in test temporary directory " "(last test running was `%s' in `%s')!" % (wrapped_testcase_suite.last_test, wrapped_testcase_suite.last_test_temp_dir)) fail = True if fail: wrapped_testcase_suite.child.terminate() try: # terminating the child process tends to leave orphan # VPP process around if wrapped_testcase_suite.vpp_pid: os.kill(wrapped_testcase_suite.vpp_pid, signal.SIGTERM) except OSError: # already dead pass wrapped_testcase_suite.result.crashed = True wrapped_testcase_suite.result.process_result( wrapped_testcase_suite.last_test_id, ERROR) stop_run = process_finished_testsuite( wrapped_testcase_suite, finished_testcase_suites, failed_wrapped_testcases, results) or stop_run for finished_testcase in finished_testcase_suites: # Somewhat surprisingly, the join below may # timeout, even if client signaled that # it finished - so we note it just in case. join_start = time.time() finished_testcase.child.join(test_finished_join_timeout) join_end = time.time() if join_end - join_start >= test_finished_join_timeout: finished_testcase.logger.error( "Timeout joining finished test: %s (pid %d)" % (finished_testcase.last_test, finished_testcase.child.pid)) finished_testcase.close_pipes() wrapped_testcase_suites.remove(finished_testcase) finished_unread_testcases.add(finished_testcase) finished_testcase.stdouterr_queue.put(None) on_suite_finish(finished_testcase) if stop_run: while testcase_suites: results.append(TestResult(testcase_suites.pop(0))) elif testcase_suites: a_suite = testcase_suites.pop(0) while a_suite and a_suite.is_tagged_run_solo: solo_testcase_suites.append(a_suite) if testcase_suites: a_suite = testcase_suites.pop(0) else: a_suite = None if a_suite and can_run_suite(a_suite): run_suite(a_suite) if solo_testcase_suites and tests_running == 0: a_suite = solo_testcase_suites.pop(0) run_suite(a_suite) time.sleep(0.1) except Exception: for wrapped_testcase_suite in wrapped_testcase_suites: wrapped_testcase_suite.child.terminate() wrapped_testcase_suite.stdouterr_queue.put(None) raise finally: read_from_testcases.clear() stdouterr_thread.join(test_timeout) manager.shutdown() handle_cores(failed_wrapped_testcases) return results class TestSuiteWrapper(unittest.TestSuite): cpus_used = 0 def __init__(self): return super().__init__() def addTest(self, test): self.cpus_used = max(self.cpus_used, test.get_cpus_required()) super().addTest(test) def assign_cpus(self, cpus): self.cpus = cpus def _handleClassSetUp(self, test, result): if not test.__class__.skipped_due_to_cpu_lack: test.assign_cpus(self.cpus) super()._handleClassSetUp(test, result) def get_assigned_cpus(self): return self.cpus class SplitToSuitesCallback: def __init__(self, filter_callback): self.suites = {} self.suite_name = 'default' self.filter_callback = filter_callback self.filtered = TestSuiteWrapper() def __call__(self, file_name, cls, method): test_method = cls(method) if self.filter_callback(file_name, cls.__name__, method): self.suite_name = file_name + cls.__name__ if self.suite_name not in self.suites: self.suites[self.suite_name] = TestSuiteWrapper() self.suites[self.suite_name].is_tagged_run_solo = False self.suites[self.suite_name].addTest(test_method) if test_method.is_tagged_run_solo(): self.suites[self.suite_name].is_tagged_run_solo = True else: self.filtered.addTest(test_method) test_option = "TEST" def parse_test_option(): f = os.getenv(test_option, None) filter_file_name = None filter_class_name = None filter_func_name = None if f: if '.' in f: parts = f.split('.') if len(parts) > 3: raise Exception("Unrecognized %s option: %s" % (test_option, f)) if len(parts) > 2: if parts[2] not in ('*', ''): filter_func_name = parts[2] if parts[1] not in ('*', ''): filter_class_name = parts[1] if parts[0] not in ('*', ''): if parts[0].startswith('test_'): filter_file_name = parts[0] else: filter_file_name = 'test_%s' % parts[0] else: if f.startswith('test_'): filter_file_name = f else: filter_file_name = 'test_%s' % f if filter_file_name: filter_file_name = '%s.py' % filter_file_name return filter_file_name, filter_class_name, filter_func_name def filter_tests(tests, filter_cb): result = TestSuiteWrapper() for t in tests: if isinstance(t, unittest.suite.TestSuite): # this is a bunch of tests, recursively filter... x = filter_tests(t, filter_cb) if x.countTestCases() > 0: result.addTest(x) elif isinstance(t, unittest.TestCase): # this is a single test parts = t.id().split('.') # t.id() for common cases like this: # test_classifier.TestClassifier.test_acl_ip # apply filtering only if it is so if len(parts) == 3: if not filter_cb(parts[0], parts[1], parts[2]): continue result.addTest(t) else: # unexpected object, don't touch it result.addTest(t) return result class FilterByTestOption: def __init__(self, filter_file_name, filter_class_name, filter_func_name): self.filter_file_name = filter_file_name self.filter_class_name = filter_class_name self.filter_func_name = filter_func_name def __call__(self, file_name, class_name, func_name): if self.filter_file_name: fn_match = fnmatch.fnmatch(file_name, self.filter_file_name) if not fn_match: return False if self.filter_class_name and class_name != self.filter_class_name: return False if self.filter_func_name and func_name != self.filter_func_name: return False return True class FilterByClassList: def __init__(self, classes_with_filenames): self.classes_with_filenames = classes_with_filenames def __call__(self, file_name, class_name, func_name): return '.'.join([file_name, class_name]) in self.classes_with_filenames def suite_from_failed(suite, failed): failed = {x.rsplit('.', 1)[0] for x in failed} filter_cb = FilterByClassList(failed) suite = filter_tests(suite, filter_cb) return suite class AllResults(dict): def __init__(self): super(AllResults, self).__init__() self.all_testcases = 0 self.results_per_suite = [] self[PASS] = 0 self[FAIL] = 0 self[ERROR] = 0 self[SKIP] = 0 self[SKIP_CPU_SHORTAGE] = 0 self[TEST_RUN] = 0 self.rerun = [] self.testsuites_no_tests_run = [] def add_results(self, result): self.results_per_suite.append(result) result_types = [PASS, FAIL, ERROR, SKIP, TEST_RUN, SKIP_CPU_SHORTAGE] for result_type in result_types: self[result_type] += len(result[result_type]) def add_result(self, result): retval = 0 self.all_testcases += result.testcase_suite.countTestCases() self.add_results(result) if result.no_tests_run(): self.testsuites_no_tests_run.append(result.testcase_suite) if result.crashed: retval = -1 else: retval = 1 elif not result.was_successful(): retval = 1 if retval != 0: self.rerun.append(result.testcase_suite) return retval def print_results(self): print('') print(double_line_delim) print('TEST RESULTS:') def indent_results(lines): lines = list(filter(None, lines)) maximum = max(lines, key=lambda x: x.index(":")) maximum = 4 + maximum.index(":") for l in lines: padding = " " * (maximum - l.index(":")) print(f"{padding}{l}") indent_results([ f'Scheduled tests: {self.all_testcases}', f'Executed tests: {self[TEST_RUN]}', f'Passed tests: {colorize(self[PASS], GREEN)}', f'Skipped tests: {colorize(self[SKIP], YELLOW)}' if self[SKIP] else None, f'Not Executed tests: {colorize(self.not_executed, RED)}' if self.not_executed else None, f'Failures: {colorize(self[FAIL], RED)}' if self[FAIL] else None, f'Errors: {colorize(self[ERROR], RED)}' if self[ERROR] else None, 'Tests skipped due to lack of CPUS: ' f'{colorize(self[SKIP_CPU_SHORTAGE], YELLOW)}' if self[SKIP_CPU_SHORTAGE] else None ]) if self.all_failed > 0: print('FAILURES AND ERRORS IN TESTS:') for result in self.results_per_suite: failed_testcase_ids = result[FAIL] errored_testcase_ids = result[ERROR] old_testcase_name = None if failed_testcase_ids: for failed_test_id in failed_testcase_ids: new_testcase_name, test_name = \ result.get_testcase_names(failed_test_id) if new_testcase_name != old_testcase_name: print(' Testcase name: {}'.format( colorize(new_testcase_name, RED))) old_testcase_name = new_testcase_name print(' FAILURE: {} [{}]'.format( colorize(test_name, RED), failed_test_id)) if errored_testcase_ids: for errored_test_id in errored_testcase_ids: new_testcase_name, test_name = \ result.get_testcase_names(errored_test_id) if new_testcase_name != old_testcase_name: print(' Testcase name: {}'.format( colorize(new_testcase_name, RED))) old_testcase_name = new_testcase_name print(' ERROR: {} [{}]'.format( colorize(test_name, RED), errored_test_id)) if self.testsuites_no_tests_run: print('TESTCASES WHERE NO TESTS WERE SUCCESSFULLY EXECUTED:') tc_classes = set() for testsuite in self.testsuites_no_tests_run: for testcase in testsuite: tc_classes.add(get_testcase_doc_name(testcase)) for tc_class in tc_classes: print(' {}'.format(colorize(tc_class, RED))) if self[SKIP_CPU_SHORTAGE]: print() print(colorize(' SOME TESTS WERE SKIPPED BECAUSE THERE ARE NOT' ' ENOUGH CPUS AVAILABLE', YELLOW)) print(double_line_delim) print('') @property def not_executed(self): return self.all_testcases - self[TEST_RUN] @property def all_failed(self): return self[FAIL] + self[ERROR] def parse_results(results): """ Prints the number of scheduled, executed, not executed, passed, failed, errored and skipped tests and details about failed and errored tests. Also returns all suites where any test failed. :param results: :return: """ results_per_suite = AllResults() crashed = False failed = False for result in results: result_code = results_per_suite.add_result(result) if result_code == 1: failed = True elif result_code == -1: crashed = True results_per_suite.print_results() if crashed: return_code = -1 elif failed: return_code = 1 else: return_code = 0 return return_code, results_per_suite.rerun def parse_digit_env(env_var, default): value = os.getenv(env_var, default) if value != default: if value.isdigit(): value = int(value) else: print('WARNING: unsupported value "%s" for env var "%s",' 'defaulting to %s' % (value, env_var, default)) value = default return value if __name__ == '__main__': verbose = parse_digit_env("V", 0) test_timeout = parse_digit_env("TIMEOUT", 600) # default = 10 minutes test_finished_join_timeout = 15 retries = parse_digit_env("RETRIES", 0) debug = os.getenv("DEBUG", "n").lower() in ["gdb", "gdbserver", "attach"] debug_core = os.getenv("DEBUG", "").lower() == "core" compress_core = framework.BoolEnvironmentVariable("CORE_COMPRESS") if os.getenv("VPP_IN_GDB", "n").lower() in ["1", "y", "yes"]: start_vpp_in_gdb() exit() step = framework.BoolEnvironmentVariable("STEP") force_foreground = framework.BoolEnvironmentVariable("FORCE_FOREGROUND") run_interactive = debug or step or force_foreground max_concurrent_tests = 0 print(f"OS reports {num_cpus} available cpu(s).") test_jobs = os.getenv("TEST_JOBS", "1").lower() # default = 1 process if test_jobs == 'auto': if run_interactive: max_concurrent_tests = 1 print('Interactive mode required, running tests consecutively.') else: max_concurrent_tests = num_cpus print(f"Running at most {max_concurrent_tests} python test " "processes concurrently.") else: try: test_jobs = int(test_jobs) except ValueError as e: raise ValueError("Invalid TEST_JOBS value specified, valid " "values are a positive integer or 'auto'") from e if test_jobs <= 0: raise ValueError("Invalid TEST_JOBS value specified, valid " "values are a positive integer or 'auto'") max_concurrent_tests = int(test_jobs) print(f"Running at most {max_concurrent_tests} python test processes " "concurrently as set by 'TEST_JOBS'.") print(f"Using at most {max_vpp_cpus} cpus for VPP threads.") if run_interactive and max_concurrent_tests > 1: raise NotImplementedError( 'Running tests interactively (DEBUG is gdb[server] or ATTACH or ' 'STEP is set) in parallel (TEST_JOBS is more than 1) is not ' 'supported') parser = argparse.ArgumentParser(description="VPP unit tests") parser.add_argument("-f", "--failfast", action='store_true', help="fast failure flag") parser.add_argument("-d", "--dir", action='append', type=str, help="directory containing test files " "(may be specified multiple times)") args = parser.parse_args() failfast = args.failfast descriptions = True print("Running tests using custom test runner.") filter_file, filter_class, filter_func = parse_test_option() print("Active filters: file=%s, class=%s, function=%s" % ( filter_file, filter_class, filter_func)) filter_cb = FilterByTestOption(filter_file, filter_class, filter_func) ignore_path = os.getenv("VENV_PATH", None) cb = SplitToSuitesCallback(filter_cb) for d in args.dir: print("Adding tests from directory tree %s" % d) discover_tests(d, cb, ignore_path) # suites are not hashable, need to use list suites = [] tests_amount = 0 for testcase_suite in cb.suites.values(): tests_amount += testcase_suite.countTestCases() if testcase_suite.cpus_used > max_vpp_cpus: # here we replace test functions with lambdas to just skip them # but we also replace setUp/tearDown functions to do nothing # so that the test can be "started" and "stopped", so that we can # still keep those prints (test description - SKIP), which are done # in stopTest() (for that to trigger, test function must run) for t in testcase_suite: for m in dir(t): if m.startswith('test_'): setattr(t, m, lambda: t.skipTest("not enough cpus")) setattr(t.__class__, 'setUpClass', lambda: None) setattr(t.__class__, 'tearDownClass', lambda: None) setattr(t, 'setUp', lambda: None) setattr(t, 'tearDown', lambda: None) t.__class__.skipped_due_to_cpu_lack = True suites.append(testcase_suite) print("%s out of %s tests match specified filters" % ( tests_amount, tests_amount + cb.filtered.countTestCases())) if not running_extended_tests: print("Not running extended tests (some tests will be skipped)") attempts = retries + 1 if attempts > 1: print("Perform %s attempts to pass the suite..." % attempts) if run_interactive and suites: # don't fork if requiring interactive terminal print('Running tests in foreground in the current process') full_suite = unittest.TestSuite() free_cpus = list(available_cpus) cpu_shortage = False for suite in suites: if suite.cpus_used <= max_vpp_cpus: suite.assign_cpus(free_cpus[:suite.cpus_used]) else: suite.assign_cpus([]) cpu_shortage = True full_suite.addTests(suites) result = VppTestRunner(verbosity=verbose, failfast=failfast, print_summary=True).run(full_suite) was_successful = result.wasSuccessful() if not was_successful: for test_case_info in result.failed_test_cases_info: handle_failed_suite(test_case_info.logger, test_case_info.tempdir, test_case_info.vpp_pid) if test_case_info in result.core_crash_test_cases_info: check_and_handle_core(test_case_info.vpp_bin_path, test_case_info.tempdir, test_case_info.core_crash_test) if cpu_shortage: print() print(colorize('SOME TESTS WERE SKIPPED BECAUSE THERE ARE NOT' ' ENOUGH CPUS AVAILABLE', YELLOW)) print() sys.exit(not was_successful) else: print('Running each VPPTestCase in a separate background process' f' with at most {max_concurrent_tests} parallel python test ' 'process(es)') exit_code = 0 while suites and attempts > 0: results = run_forked(suites) exit_code, suites = parse_results(results) attempts -= 1 if exit_code == 0: print('Test run was successful') else: print('%s attempt(s) left.' % attempts) sys.exit(exit_code)