aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python/HoststackUtil.py
blob: c3079466986930507e142fa1900763bccda08d4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# Copyright (c) 2020 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Host Stack util library."""
import json
from time import sleep
from robot.api import logger

from resources.libraries.python.Constants import Constants
from resources.libraries.python.ssh import exec_cmd, exec_cmd_no_error
from resources.libraries.python.PapiExecutor import PapiSocketExecutor
from resources.libraries.python.DUTSetup import DUTSetup

class HoststackUtil():
    """Utilities for Host Stack tests."""

    @staticmethod
    def get_vpp_echo_command(vpp_echo_attributes):
        """Construct the vpp_echo command using the specified attributes.

        :param vpp_echo_attributes: vpp_echo test program attributes.
        :type vpp_echo_attributes: dict
        :returns: Command line components of the vpp_echo command
            'name' - program name
            'args' - command arguments.
        :rtype: dict
        """
        # TODO: Use a python class instead of dictionary for the return type
        proto = vpp_echo_attributes[u"uri_protocol"]
        addr = vpp_echo_attributes[u"uri_ip4_addr"]
        port = vpp_echo_attributes[u"uri_port"]
        vpp_echo_cmd = {}
        vpp_echo_cmd[u"name"] = u"vpp_echo"
        vpp_echo_cmd[u"args"] = f"{vpp_echo_attributes[u'role']} " \
            f"socket-name {vpp_echo_attributes[u'vpp_api_socket']} " \
            f"{vpp_echo_attributes[u'json_output']} " \
            f"uri {proto}://{addr}/{port} " \
            f"nthreads {vpp_echo_attributes[u'nthreads']} " \
            f"mq-size {vpp_echo_attributes[u'mq_size']} " \
            f"nclients {vpp_echo_attributes[u'nclients']} " \
            f"quic-streams {vpp_echo_attributes[u'quic_streams']} " \
            f"time {vpp_echo_attributes[u'time']} " \
            f"fifo-size {vpp_echo_attributes[u'fifo_size']} " \
            f"TX={vpp_echo_attributes[u'tx_bytes']} " \
            f"RX={vpp_echo_attributes[u'rx_bytes']}"
        if vpp_echo_attributes[u"rx_results_diff"]:
            vpp_echo_cmd[u"args"] += u" rx-results-diff"
        if vpp_echo_attributes[u"tx_results_diff"]:
            vpp_echo_cmd[u"args"] += u" tx-results-diff"
        return vpp_echo_cmd

    @staticmethod
    def get_iperf3_command(iperf3_attributes):
        """Construct the iperf3 command using the specified attributes.

        :param iperf3_attributes: iperf3 test program attributes.
        :type iperf3_attributes: dict
        :returns: Command line components of the iperf3 command
            'env_vars' - environment variables
            'name' - program name
            'args' - command arguments.
        :rtype: dict
        """
        # TODO: Use a python class instead of dictionary for the return type
        iperf3_cmd = {}
        iperf3_cmd[u"env_vars"] = f"VCL_CONFIG={Constants.REMOTE_FW_DIR}/" \
            f"{Constants.RESOURCES_TPL_VCL}/" \
            f"{iperf3_attributes[u'vcl_config']}"
        if iperf3_attributes[u"ld_preload"]:
            iperf3_cmd[u"env_vars"] += \
                f" LD_PRELOAD={Constants.VCL_LDPRELOAD_LIBRARY}"
        if iperf3_attributes[u'transparent_tls']:
            iperf3_cmd[u"env_vars"] += u" LDP_ENV_TLS_TRANS=1"

        json_results = u" --json" if iperf3_attributes[u'json'] else u""
        ip_address = f" {iperf3_attributes[u'ip_address']}" if u"ip_address" \
                     in iperf3_attributes else u""
        iperf3_cmd[u"name"] = u"iperf3"
        iperf3_cmd[u"args"] = f"--{iperf3_attributes[u'role']}{ip_address} " \
                              f"--interval 0{json_results} " \
                              f"--version{iperf3_attributes[u'ip_version']}"

        if iperf3_attributes[u"role"] == u"server":
            iperf3_cmd[u"args"] += u" --one-off"
        else:
            iperf3_cmd[u"args"] += u" --get-server-output"
            if u"parallel" in iperf3_attributes:
                iperf3_cmd[u"args"] += \
                    f" --parallel {iperf3_attributes[u'parallel']}"
            if u"time" in iperf3_attributes:
                iperf3_cmd[u"args"] += \
                    f" --time {iperf3_attributes[u'time']}"
        return iperf3_cmd

    @staticmethod
    def set_hoststack_quic_fifo_size(node, fifo_size):
        """Set the QUIC protocol fifo size.

        :param node: Node to set the QUIC fifo size on.
        :param fifo_size: fifo size, passed to the quic set fifo-size command.
        :type node: dict
        :type fifo_size: str
        """
        cmd = f"quic set fifo-size {fifo_size}"
        PapiSocketExecutor.run_cli_cmd(node, cmd)

    @staticmethod
    def set_hoststack_quic_crypto_engine(node, quic_crypto_engine,
                                         fail_on_error=False):
        """Set the Hoststack QUIC crypto engine on node

        :param node: Node to enable/disable HostStack.
        :param quic_crypto_engine: type of crypto engine
        :type node: dict
        :type quic_crypto_engine: str
        """
        vpp_crypto_engines = {u"openssl", u"native", u"ipsecmb"}
        if quic_crypto_engine == u"nocrypto":
            logger.trace(u"No QUIC crypto engine.")
            return

        if quic_crypto_engine in vpp_crypto_engines:
            cmds = [u"quic set crypto api vpp",
                    f"set crypto handler aes-128-gcm {quic_crypto_engine}",
                    f"set crypto handler aes-256-gcm {quic_crypto_engine}"]
        elif quic_crypto_engine == u"picotls":
            cmds = [u"quic set crypto api picotls"]
        else:
            raise ValueError(f"Unknown QUIC crypto_engine {quic_crypto_engine}")

        for cmd in cmds:
            try:
                PapiSocketExecutor.run_cli_cmd(node, cmd)
            except AssertionError:
                if fail_on_error:
                    raise

    @staticmethod
    def get_hoststack_test_program_logs(node, program):
        """Get HostStack test program stdout log.

        :param node: DUT node.
        :param program: test program.
        :type node: dict
        :type program: dict
        """
        program_name = program[u"name"]
        cmd = f"sh -c \'cat /tmp/{program_name}_stdout.log\'"
        stdout_log, _ = exec_cmd_no_error(node, cmd, sudo=True, \
            message=f"Get {program_name} stdout log failed!")

        cmd = f"sh -c \'cat /tmp/{program_name}_stderr.log\'"
        stderr_log, _ = exec_cmd_no_error(node, cmd, sudo=True, \
            message=f"Get {program_name} stderr log failed!")
        return stdout_log, stderr_log

    @staticmethod
    def start_hoststack_test_program(node, namespace, core_list, program):
        """Start the specified HostStack test program.

        :param node: DUT node.
        :param namespace: Net Namespace to run program in.
        :param core_list: List of cpu's to pass to taskset to pin the test
            program to a different set of cores on the same numa node as VPP.
        :param program: Test program.
        :type node: dict
        :type namespace: str
        :type core_list: str
        :type program: dict
        :returns: Process ID
        :rtype: int
        :raises RuntimeError: If node subtype is not a DUT or startup failed.
        """
        if node[u"type"] != u"DUT":
            raise RuntimeError(u"Node type is not a DUT!")

        program_name = program[u"name"]
        DUTSetup.kill_program(node, program_name, namespace)

        if namespace == u"default":
            shell_cmd = u"sh -c"
        else:
            shell_cmd = f"ip netns exec {namespace} sh -c"

        env_vars = f"{program[u'env_vars']} " if u"env_vars" in program else u""
        args = program[u"args"]
        cmd = f"nohup {shell_cmd} \'{env_vars}taskset --cpu-list {core_list} " \
            f"{program_name} {args} >/tmp/{program_name}_stdout.log " \
            f"2>/tmp/{program_name}_stderr.log &\'"
        try:
            exec_cmd_no_error(node, cmd, sudo=True)
            return DUTSetup.get_pid(node, program_name)[0]
        except RuntimeError:
            stdout_log, stderr_log = \
                HoststackUtil.get_hoststack_test_program_logs(node,
                                                              program)
            raise RuntimeError(f"Start {program_name} failed!\nSTDERR:\n" \
                               f"{stderr_log}\nSTDOUT:\n{stdout_log}")
        return None

    @staticmethod
    def stop_hoststack_test_program(node, program, pid):
        """Stop the specified Hoststack test program.

        :param node: DUT node.
        :param program: Test program.
        :param pid: Process ID of test program.
        :type node: dict
        :type program: dict
        :type pid: int
        """
        program_name = program[u"name"]
        if program_name == u"nginx":
            cmd = u"nginx -s quit"
            errmsg = u"Quit nginx failed!"
        else:
            cmd = f'if [ -n "$(ps {pid} | grep {program_name})" ] ; ' \
                f'then kill -s SIGTERM {pid}; fi'
            errmsg = f"Kill {program_name} ({pid}) failed!"

        exec_cmd_no_error(node, cmd, message=errmsg, sudo=True)

    @staticmethod
    def hoststack_test_program_finished(node, program_pid):
        """Wait for the specified HostStack test program process to complete.

        :param node: DUT node.
        :param program_pid: test program pid.
        :type node: dict
        :type program_pid: str
        :raises RuntimeError: If node subtype is not a DUT.
        """
        if node[u"type"] != u"DUT":
            raise RuntimeError(u"Node type is not a DUT!")

        cmd = f"sh -c 'strace -qqe trace=none -p {program_pid}'"
        exec_cmd(node, cmd, sudo=True)
        # Wait a bit for stdout/stderr to be flushed to log files
        # TODO: see if sub-second sleep works e.g. sleep(0.1)
        sleep(1)

    @staticmethod
    def analyze_hoststack_test_program_output(
            node, role, nsim_attr, program):
        """Gather HostStack test program output and check for errors.

        The [defer_fail] return bool is used instead of failing immediately
        to allow the analysis of both the client and server instances of
        the test program for debugging a test failure.  When [defer_fail]
        is true, then the string returned is debug output instead of
        JSON formatted test program results.

        :param node: DUT node.
        :param role: Role (client|server) of test program.
        :param nsim_attr: Network Simulation Attributes.
        :param program: Test program.
        :param program_args: List of test program args.
        :type node: dict
        :type role: str
        :type nsim_attr: dict
        :type program: dict
        :returns: tuple of [defer_fail] bool and either JSON formatted hoststack
            test program output or failure debug output.
        :rtype: bool, str
        :raises RuntimeError: If node subtype is not a DUT.
        """
        if node[u"type"] != u"DUT":
            raise RuntimeError(u"Node type is not a DUT!")

        program_name = program[u"name"]
        program_stdout, program_stderr = \
            HoststackUtil.get_hoststack_test_program_logs(node, program)
        if len(program_stdout) == 0 and len(program_stderr) == 0:
            logger.trace(f"Retrying {program_name} log retrieval")
            program_stdout, program_stderr = \
               HoststackUtil.get_hoststack_test_program_logs(node, program)

        env_vars = f"{program[u'env_vars']} " if u"env_vars" in program else u""
        program_cmd = f"{env_vars}{program_name} {program[u'args']}"
        test_results = f"Test Results of '{program_cmd}':\n"

        if nsim_attr[u"output_nsim_enable"] or \
            nsim_attr[u"xc_nsim_enable"]:
            if nsim_attr[u"output_nsim_enable"]:
                feature_name = u"output"
            else:
                feature_name = u"cross-connect"
            test_results += \
                f"NSIM({feature_name}): delay " \
                f"{nsim_attr[u'delay_in_usec']} usecs, " \
                f"avg-pkt-size {nsim_attr[u'average_packet_size']}, " \
                f"bandwidth {nsim_attr[u'bw_in_bits_per_second']} " \
                f"bits/sec, pkt-drop-rate {nsim_attr[u'packets_per_drop']} " \
                f"pkts/drop\n"

        # TODO: Incorporate show error stats into results analysis
        test_results += \
            f"\n{role} VPP 'show errors' on host {node[u'host']}:\n" \
            f"{PapiSocketExecutor.run_cli_cmd(node, u'show error')}\n"

        if u"error" in program_stderr.lower():
            test_results += f"ERROR DETECTED:\n{program_stderr}"
            return (True, test_results)
        if not program_stdout:
            test_results += f"\nNo {program} test data retrieved!\n"
            ls_stdout, _ = exec_cmd_no_error(node, u"ls -l /tmp/*.log",
                                             sudo=True)
            test_results += f"{ls_stdout}\n"
            return (True, test_results)
        if program[u"name"] == u"vpp_echo":
            if u"JSON stats" in program_stdout and \
                    u'"has_failed": "0"' in program_stdout:
                json_start = program_stdout.find(u"{")
                #TODO: Fix parsing once vpp_echo produces valid
                # JSON output. Truncate for now.
                json_end = program_stdout.find(u',\n  "closing"')
                json_results = f"{program_stdout[json_start:json_end]}\n}}"
                program_json = json.loads(json_results)
            else:
                test_results += u"Invalid test data output!\n" + program_stdout
                return (True, test_results)
        elif program[u"name"] == u"iperf3":
            test_results += program_stdout
            iperf3_json = json.loads(program_stdout)
            program_json = iperf3_json[u"intervals"][0][u"sum"]
        else:
            test_results += u"Unknown HostStack Test Program!\n" + \
                            program_stdout
            return (True, program_stdout)
        return (False, json.dumps(program_json))

    @staticmethod
    def hoststack_test_program_defer_fail(server_defer_fail, client_defer_fail):
        """Return True if either HostStack test program fail was deferred.

        :param server_defer_fail: server no results value.
        :param client_defer_fail: client no results value.
        :type server_defer_fail: bool
        :type client_defer_fail: bool
        :rtype: bool
        """
        return server_defer_fail and client_defer_fail
api_main_t * am, vl_api_trace_which_t which, u32 nitems) { vl_api_trace_t *tp; int was_on = 0; switch (which) { case VL_API_TRACE_TX: tp = am->tx_trace; if (tp == 0) { vec_validate (am->tx_trace, 0); tp = am->tx_trace; } break; case VL_API_TRACE_RX: tp = am->rx_trace; if (tp == 0) { vec_validate (am->rx_trace, 0); tp = am->rx_trace; } break; default: return -1; } if (tp->enabled) { was_on = vl_msg_api_trace_onoff (am, which, 0); } if (tp->traces) { vl_msg_api_trace_free (am, which); } clib_memset (tp, 0, sizeof (*tp)); if (clib_arch_is_big_endian) { tp->endian = VL_API_BIG_ENDIAN; } else { tp->endian = VL_API_LITTLE_ENDIAN; } tp->nitems = nitems; if (was_on) { (void) vl_msg_api_trace_onoff (am, which, was_on); } return 0; } void vl_msg_api_barrier_sync (void) { } void vl_msg_api_barrier_release (void) { } always_inline void msg_handler_internal (api_main_t * am, void *the_msg, int trace_it, int do_it, int free_it) { u16 id = ntohs (*((u16 *) the_msg)); u8 *(*print_fp) (void *, void *); if (id < vec_len (am->msg_handlers) && am->msg_handlers[id]) { if (trace_it) vl_msg_api_trace (am, am->rx_trace, the_msg); if (am->msg_print_flag) { fformat (stdout, "[%d]: %s\n", id, am->msg_names[id]); print_fp = (void *) am->msg_print_handlers[id]; if (print_fp == 0) { fformat (stdout, " [no registered print fn]\n"); } else { (*print_fp) (the_msg, stdout); } } if (do_it) { if (!am->is_mp_safe[id]) { vl_msg_api_barrier_trace_context (am->msg_names[id]); vl_msg_api_barrier_sync (); } (*am->msg_handlers[id]) (the_msg); if (!am->is_mp_safe[id]) vl_msg_api_barrier_release (); } } else { clib_warning ("no handler for msg id %d", id); } if (free_it) vl_msg_api_free (the_msg); } static u32 elog_id_for_msg_name (vlib_main_t * vm, const char *msg_name) { uword *p, r; static uword *h; u8 *name_copy; if (!h) h = hash_create_string (0, sizeof (uword)); p = hash_get_mem (h, msg_name); if (p) return p[0]; r = elog_string (&vm->elog_main, "%s", msg_name); name_copy = format (0, "%s%c", msg_name, 0); hash_set_mem (h, name_copy, r); return r; } /* This is only to be called from a vlib/vnet app */ void vl_msg_api_handler_with_vm_node (api_main_t * am, void *the_msg, vlib_main_t * vm, vlib_node_runtime_t * node) { u16 id = ntohs (*((u16 *) the_msg)); u8 *(*handler) (void *, void *, void *); if (PREDICT_FALSE (vm->elog_trace_api_messages)) { /* *INDENT-OFF* */ ELOG_TYPE_DECLARE (e) = { .format = "api-msg: %s", .format_args = "T4", }; /* *INDENT-ON* */ struct { u32 c; } *ed; ed = ELOG_DATA (&vm->elog_main, e); if (id < vec_len (am->msg_names)) ed->c = elog_id_for_msg_name (vm, (const char *) am->msg_names[id]); else ed->c = elog_id_for_msg_name (vm, "BOGUS"); } if (id < vec_len (am->msg_handlers) && am->msg_handlers[id]) { handler = (void *) am->msg_handlers[id]; if (am->rx_trace && am->rx_trace->enabled) vl_msg_api_trace (am, am->rx_trace, the_msg); if (!am->is_mp_safe[id]) { vl_msg_api_barrier_trace_context (am->msg_names[id]); vl_msg_api_barrier_sync (); } (*handler) (the_msg, vm, node); if (!am->is_mp_safe[id]) vl_msg_api_barrier_release (); } else { clib_warning ("no handler for msg id %d", id); } /* * Special-case, so we can e.g. bounce messages off the vnet * main thread without copying them... */ if (!(am->message_bounce[id])) vl_msg_api_free (the_msg); if (PREDICT_FALSE (vm->elog_trace_api_messages)) { /* *INDENT-OFF* */ ELOG_TYPE_DECLARE (e) = { .format = "api-msg-done: %s", .format_args = "T4", }; /* *INDENT-ON* */ struct { u32 c; } *ed; ed = ELOG_DATA (&vm->elog_main, e); if (id < vec_len (am->msg_names)) ed->c = elog_id_for_msg_name (vm, (const char *) am->msg_names[id]); else ed->c = elog_id_for_msg_name (vm, "BOGUS"); } } void vl_msg_api_handler (void *the_msg) { api_main_t *am = &api_main; msg_handler_internal (am, the_msg, (am->rx_trace && am->rx_trace->enabled) /* trace_it */ , 1 /* do_it */ , 1 /* free_it */ ); } void vl_msg_api_handler_no_free (void *the_msg) { api_main_t *am = &api_main; msg_handler_internal (am, the_msg, (am->rx_trace && am->rx_trace->enabled) /* trace_it */ , 1 /* do_it */ , 0 /* free_it */ ); } void vl_msg_api_handler_no_trace_no_free (void *the_msg) { api_main_t *am = &api_main; msg_handler_internal (am, the_msg, 0 /* trace_it */ , 1 /* do_it */ , 0 /* free_it */ ); } /* * Add a trace record to the API message trace buffer, if * API message tracing is enabled. Handy for adding sufficient * data to the trace to reproduce autonomous state, as opposed to * state downloaded via control-plane API messages. Example: the NAT * application creates database entries based on packet traffic, not * control-plane messages. * */ void vl_msg_api_trace_only (void *the_msg) { api_main_t *am = &api_main; msg_handler_internal (am, the_msg, (am->rx_trace && am->rx_trace->enabled) /* trace_it */ , 0 /* do_it */ , 0 /* free_it */ ); } void vl_msg_api_cleanup_handler (void *the_msg) { api_main_t *am = &api_main; u16 id = ntohs (*((u16 *) the_msg)); if (PREDICT_FALSE (id >= vec_len (am->msg_cleanup_handlers))) { clib_warning ("_vl_msg_id too large: %d\n", id); return; } if (am->msg_cleanup_handlers[id]) (*am->msg_cleanup_handlers[id]) (the_msg); vl_msg_api_free (the_msg); } /* * vl_msg_api_replay_handler */ void vl_msg_api_replay_handler (void *the_msg) { api_main_t *am = &api_main; u16 id = ntohs (*((u16 *) the_msg)); if (PREDICT_FALSE (id >= vec_len (am->msg_handlers))) { clib_warning ("_vl_msg_id too large: %d\n", id); return; } /* do NOT trace the message... */ if (am->msg_handlers[id]) (*am->msg_handlers[id]) (the_msg); /* do NOT free the message buffer... */ } u32 vl_msg_api_get_msg_length (void *msg_arg) { return vl_msg_api_get_msg_length_inline (msg_arg); } /* * vl_msg_api_socket_handler */ void vl_msg_api_socket_handler (void *the_msg) { api_main_t *am = &api_main; msg_handler_internal (am, the_msg, (am->rx_trace && am->rx_trace->enabled) /* trace_it */ , 1 /* do_it */ , 0 /* free_it */ ); } #define foreach_msg_api_vector \ _(msg_names) \ _(msg_handlers) \ _(msg_cleanup_handlers) \ _(msg_endian_handlers) \ _(msg_print_handlers) \ _(api_trace_cfg) \ _(message_bounce) \ _(is_mp_safe) void vl_msg_api_config (vl_msg_api_msg_config_t * c) { api_main_t *am = &api_main; /* * This happens during the java core tests if the message * dictionary is missing newly added xxx_reply_t messages. * Should never happen, but since I shot myself in the foot once * this way, I thought I'd make it easy to debug if I ever do * it again... (;-)... */ if (c->id == 0) { if (c->name) clib_warning ("Trying to register %s with a NULL msg id!", c->name); else clib_warning ("Trying to register a NULL msg with a NULL msg id!"); clib_warning ("Did you forget to call setup_message_id_table?"); return; } #define _(a) vec_validate (am->a, c->id); foreach_msg_api_vector; #undef _ if (am->msg_handlers[c->id] && am->msg_handlers[c->id] != c->handler) clib_warning ("BUG: re-registering 'vl_api_%s_t_handler'." "Handler was %llx, replaced by %llx", c->name, am->msg_handlers[c->id], c->handler); am->msg_names[c->id] = c->name; am->msg_handlers[c->id] = c->handler; am->msg_cleanup_handlers[c->id] = c->cleanup; am->msg_endian_handlers[c->id] = c->endian; am->msg_print_handlers[c->id] = c->print; am->message_bounce[c->id] = c->message_bounce; am->is_mp_safe[c->id] = c->is_mp_safe; am->api_trace_cfg[c->id].size = c->size; am->api_trace_cfg[c->id].trace_enable = c->traced; am->api_trace_cfg[c->id].replay_enable = c->replay; } /* * vl_msg_api_set_handlers * preserve the old API for a while */ void vl_msg_api_set_handlers (int id, char *name, void *handler, void *cleanup, void *endian, void *print, int size, int traced) { vl_msg_api_msg_config_t cfg; vl_msg_api_msg_config_t *c = &cfg; clib_memset (c, 0, sizeof (*c)); c->id = id; c->name = name; c->handler = handler; c->cleanup = cleanup; c->endian = endian; c->print = print; c->traced = traced; c->replay = 1; c->message_bounce = 0; c->is_mp_safe = 0; vl_msg_api_config (c); } void vl_msg_api_clean_handlers (int msg_id) { vl_msg_api_msg_config_t cfg; vl_msg_api_msg_config_t *c = &cfg; clib_memset (c, 0, sizeof (*c)); c->id = msg_id; vl_msg_api_config (c); } void vl_msg_api_set_cleanup_handler (int msg_id, void *fp) { api_main_t *am = &api_main; ASSERT (msg_id > 0); vec_validate (am->msg_cleanup_handlers, msg_id); am->msg_cleanup_handlers[msg_id] = fp; } void vl_msg_api_queue_handler (svm_queue_t * q) { uword msg; while (!svm_queue_sub (q, (u8 *) & msg, SVM_Q_WAIT, 0)) vl_msg_api_handler ((void *) msg); } vl_api_trace_t * vl_msg_api_trace_get (api_main_t * am, vl_api_trace_which_t which) { switch (which) { case VL_API_TRACE_RX: return am->rx_trace; case VL_API_TRACE_TX: return am->tx_trace; default: return 0; } } void vl_noop_handler (void *mp) { } static u8 post_mortem_dump_enabled; void vl_msg_api_post_mortem_dump_enable_disable (int enable) { post_mortem_dump_enabled = enable; } void vl_msg_api_post_mortem_dump (void) { api_main_t *am = &api_main; FILE *fp; char filename[64]; int rv; if (post_mortem_dump_enabled == 0) return; snprintf (filename, sizeof (filename), "/tmp/api_post_mortem.%d", getpid ()); fp = fopen (filename, "w"); if (fp == NULL) { rv = write (2, "Couldn't create ", 16); rv = write (2, filename, strlen (filename)); rv = write (2, "\n", 1); return; } rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp); fclose (fp); if (rv < 0) { rv = write (2, "Failed to save post-mortem API trace to ", 40); rv = write (2, filename, strlen (filename)); rv = write (2, "\n", 1); } } /* Layered message handling support */ void vl_msg_api_register_pd_handler (void *fp, u16 msg_id_host_byte_order) { api_main_t *am = &api_main; /* Mild idiot proofing */ if (msg_id_host_byte_order > 10000) clib_warning ("msg_id_host_byte_order endian issue? %d arg vs %d", msg_id_host_byte_order, clib_net_to_host_u16 (msg_id_host_byte_order)); vec_validate (am->pd_msg_handlers, msg_id_host_byte_order); am->pd_msg_handlers[msg_id_host_byte_order] = fp; } int vl_msg_api_pd_handler (void *mp, int rv) { api_main_t *am = &api_main; int (*fp) (void *, int); u16 msg_id; if (clib_arch_is_little_endian) msg_id = clib_net_to_host_u16 (*((u16 *) mp)); else msg_id = *((u16 *) mp); if (msg_id >= vec_len (am->pd_msg_handlers) || am->pd_msg_handlers[msg_id] == 0) return rv; fp = am->pd_msg_handlers[msg_id]; rv = (*fp) (mp, rv); return rv; } void vl_msg_api_set_first_available_msg_id (u16 first_avail) { api_main_t *am = &api_main; am->first_available_msg_id = first_avail; } u16 vl_msg_api_get_msg_ids (const char *name, int n) { api_main_t *am = &api_main; u8 *name_copy; vl_api_msg_range_t *rp; uword *p; u16 rv; if (am->msg_range_by_name == 0) am->msg_range_by_name = hash_create_string (0, sizeof (uword)); name_copy = format (0, "%s%c", name, 0); p = hash_get_mem (am->msg_range_by_name, name_copy); if (p) { clib_warning ("WARNING: duplicate message range registration for '%s'", name_copy); vec_free (name_copy); return ((u16) ~ 0); } if (n < 0 || n > 1024) { clib_warning ("WARNING: bad number of message-IDs (%d) requested by '%s'", n, name_copy); vec_free (name_copy); return ((u16) ~ 0); } vec_add2 (am->msg_ranges, rp, 1); rv = rp->first_msg_id = am->first_available_msg_id; am->first_available_msg_id += n; rp->last_msg_id = am->first_available_msg_id - 1; rp->name = name_copy; hash_set_mem (am->msg_range_by_name, name_copy, rp - am->msg_ranges); return rv; } void vl_msg_api_add_msg_name_crc (api_main_t * am, const char *string, u32 id) { uword *p; if (am->msg_index_by_name_and_crc == 0) am->msg_index_by_name_and_crc = hash_create_string (0, sizeof (uword)); p = hash_get_mem (am->msg_index_by_name_and_crc, string); if (p) { clib_warning ("attempt to redefine '%s' ignored...", string); return; } hash_set_mem (am->msg_index_by_name_and_crc, string, id); } void vl_msg_api_add_version (api_main_t * am, const char *string, u32 major, u32 minor, u32 patch) { api_version_t version = {.major = major,.minor = minor,.patch = patch }; ASSERT (strlen (string) < 64); strncpy (version.name, string, 64 - 1); vec_add1 (am->api_version_list, version); } u32 vl_msg_api_get_msg_index (u8 * name_and_crc) { api_main_t *am = &api_main; uword *p; if (am->msg_index_by_name_and_crc) { p = hash_get_mem (am->msg_index_by_name_and_crc, name_and_crc); if (p) return p[0]; } return ~0; } void * vl_msg_push_heap (void) { api_main_t *am = &api_main; pthread_mutex_lock (&am->vlib_rp->mutex); return svm_push_data_heap (am->vlib_rp); } void vl_msg_pop_heap (void *oldheap) { api_main_t *am = &api_main; svm_pop_heap (oldheap); pthread_mutex_unlock (&am->vlib_rp->mutex); } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */