]>
Commit | Line | Data |
---|---|---|
36d1dc45 RZ |
1 | # |
2 | # topolog.py | |
3 | # Library of helper functions for NetDEF Topology Tests | |
4 | # | |
5 | # Copyright (c) 2017 by | |
6 | # Network Device Education Foundation, Inc. ("NetDEF") | |
7 | # | |
8 | # Permission to use, copy, modify, and/or distribute this software | |
9 | # for any purpose with or without fee is hereby granted, provided | |
10 | # that the above copyright notice and this permission notice appear | |
11 | # in all copies. | |
12 | # | |
13 | # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES | |
14 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
15 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR | |
16 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY | |
17 | # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, | |
18 | # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | |
19 | # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE | |
20 | # OF THIS SOFTWARE. | |
21 | # | |
22 | ||
23 | """ | |
24 | Logging utilities for topology tests. | |
25 | ||
26 | This file defines our logging abstraction. | |
27 | """ | |
28 | ||
29 | import logging | |
49581587 CH |
30 | import os |
31 | import subprocess | |
32 | import sys | |
33 | ||
34 | if sys.version_info[0] > 2: | |
35 | import configparser | |
36 | else: | |
37 | import ConfigParser as configparser | |
38 | ||
39 | try: | |
40 | from xdist import is_xdist_controller | |
41 | except ImportError: | |
42 | def is_xdist_controller(): | |
43 | return False | |
44 | ||
45 | BASENAME = "topolog" | |
36d1dc45 RZ |
46 | |
47 | # Helper dictionary to convert Topogen logging levels to Python's logging. | |
48 | DEBUG_TOPO2LOGGING = { | |
787e7624 | 49 | "debug": logging.DEBUG, |
50 | "info": logging.INFO, | |
51 | "output": logging.INFO, | |
52 | "warning": logging.WARNING, | |
53 | "error": logging.ERROR, | |
54 | "critical": logging.CRITICAL, | |
36d1dc45 | 55 | } |
49581587 | 56 | FORMAT = "%(asctime)s.%(msecs)03d %(levelname)s: %(name)s: %(message)s" |
36d1dc45 | 57 | |
49581587 CH |
58 | handlers = {} |
59 | logger = logging.getLogger("topolog") | |
787e7624 | 60 | |
9427b78f | 61 | |
49581587 CH |
62 | def set_handler(l, target=None): |
63 | if target is None: | |
64 | h = logging.NullHandler() | |
65 | else: | |
66 | if isinstance(target, str): | |
67 | h = logging.FileHandler(filename=target, mode="w") | |
68 | else: | |
69 | h = logging.StreamHandler(stream=target) | |
70 | h.setFormatter(logging.Formatter(fmt=FORMAT)) | |
71 | # Don't filter anything at the handler level | |
72 | h.setLevel(logging.DEBUG) | |
73 | l.addHandler(h) | |
74 | return h | |
787e7624 | 75 | |
36d1dc45 | 76 | |
49581587 CH |
77 | def set_log_level(l, level): |
78 | "Set the logging level." | |
79 | # Messages sent to this logger only are created if this level or above. | |
80 | log_level = DEBUG_TOPO2LOGGING.get(level, level) | |
81 | l.setLevel(log_level) | |
787e7624 | 82 | |
77ebccac | 83 | |
49581587 CH |
84 | def get_logger(name, log_level=None, target=None): |
85 | l = logging.getLogger("{}.{}".format(BASENAME, name)) | |
36d1dc45 | 86 | |
49581587 CH |
87 | if log_level is not None: |
88 | set_log_level(l, log_level) | |
89 | ||
90 | if target is not None: | |
91 | set_handler(l, target) | |
92 | ||
93 | return l | |
94 | ||
95 | ||
96 | # nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running | |
97 | ||
98 | def get_test_logdir(nodeid=None): | |
99 | """Get log directory relative pathname.""" | |
100 | xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "") | |
101 | mode = os.getenv("PYTEST_XDIST_MODE", "no") | |
102 | ||
103 | if not nodeid: | |
104 | nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0] | |
105 | ||
106 | cur_test = nodeid.replace("[", "_").replace("]", "_") | |
107 | path, testname = cur_test.split("::") | |
108 | path = path[:-3].replace("/", ".") | |
109 | ||
110 | # We use different logdir paths based on how xdist is running. | |
111 | if mode == "each": | |
112 | return os.path.join(path, testname, xdist_worker) | |
113 | elif mode == "load": | |
114 | return os.path.join(path, testname) | |
115 | else: | |
116 | assert ( | |
117 | mode == "no" or | |
118 | mode == "loadfile" or | |
119 | mode == "loadscope" | |
120 | ), "Unknown dist mode {}".format(mode) | |
121 | ||
122 | return path | |
123 | ||
124 | ||
125 | def logstart(nodeid, location, rundir): | |
126 | """Called from pytest before module setup.""" | |
127 | ||
128 | mode = os.getenv("PYTEST_XDIST_MODE", "no") | |
129 | worker = os.getenv("PYTEST_TOPOTEST_WORKER", "") | |
130 | ||
131 | # We only per-test log in the workers (or non-dist) | |
132 | if not worker and mode != "no": | |
133 | return | |
134 | ||
135 | handler_id = nodeid + worker | |
136 | assert handler_id not in handlers | |
137 | ||
138 | rel_log_dir = get_test_logdir(nodeid) | |
139 | exec_log_dir = os.path.join(rundir, rel_log_dir) | |
140 | subprocess.check_call("mkdir -p {0} && chmod 1777 {0}".format(exec_log_dir), shell=True) | |
141 | exec_log_path = os.path.join(exec_log_dir, "exec.log") | |
142 | ||
143 | # Add test based exec log handler | |
144 | h = set_handler(logger, exec_log_path) | |
145 | handlers[handler_id] = h | |
146 | ||
147 | if worker: | |
148 | logger.info("Logging on worker %s for %s into %s", worker, handler_id, exec_log_path) | |
149 | else: | |
150 | logger.info("Logging for %s into %s", handler_id, exec_log_path) | |
151 | ||
152 | ||
153 | def logfinish(nodeid, location): | |
154 | """Called from pytest after module teardown.""" | |
155 | # This function may not be called if pytest is interrupted. | |
156 | ||
157 | worker = os.getenv("PYTEST_TOPOTEST_WORKER", "") | |
158 | handler_id = nodeid + worker | |
159 | ||
160 | if handler_id in handlers: | |
161 | # Remove test based exec log handler | |
162 | if worker: | |
163 | logger.info("Closing logs for %s", handler_id) | |
164 | ||
165 | h = handlers[handler_id] | |
166 | logger.removeHandler(handlers[handler_id]) | |
167 | h.flush() | |
168 | h.close() | |
169 | del handlers[handler_id] | |
787e7624 | 170 | |
36d1dc45 | 171 | |
49581587 CH |
172 | console_handler = set_handler(logger, None) |
173 | set_log_level(logger, "debug") |