aboutsummaryrefslogtreecommitdiffstats
path: root/resources/libraries/python/CpuUtils.py
blob: 3cb4454e6c646f507afba531b1483a3c3838491d (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
# Copyright (c) 2016 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.

"""CPU utilities library."""

from resources.libraries.python.ssh import SSH

__all__ = ["CpuUtils"]


class CpuUtils(object):
    """CPU utilities"""

    # Number of threads per core.
    NR_OF_THREADS = 2

    @staticmethod
    def __str2int(string):
        """Conversion from string to integer, 0 in case of empty string.

        :param string: Input string.
        :type string: str
        :returns: Integer converted from string, 0 in case of ValueError.
        :rtype: int
        """
        try:
            return int(string)
        except ValueError:
            return 0

    @staticmethod
    def is_smt_enabled(cpu_info):
        """Uses CPU mapping to find out if SMT is enabled or not. If SMT is
        enabled, the L1d,L1i,L2,L3 setting is the same for two processors. These
        two processors are two threads of one core.

        :param cpu_info: CPU info, the output of "lscpu -p".
        :type cpu_info: list
        :returns: True if SMT is enabled, False if SMT is disabled.
        :rtype: bool
        """

        cpu_mems = [item[-4:] for item in cpu_info]
        cpu_mems_len = len(cpu_mems) / CpuUtils.NR_OF_THREADS
        count = 0
        for cpu_mem in cpu_mems[:cpu_mems_len]:
            if cpu_mem in cpu_mems[cpu_mems_len:]:
                count += 1
        return bool(count == cpu_mems_len)

    @staticmethod
    def get_cpu_layout_from_all_nodes(nodes):
        """Retrieve cpu layout from all nodes, assuming all nodes
           are Linux nodes.

        :param nodes: DICT__nodes from Topology.DICT__nodes.
        :type nodes: dict
        :raises RuntimeError: If the ssh command "lscpu -p" fails.
        """
        ssh = SSH()
        for node in nodes.values():
            ssh.connect(node)
            cmd = "lscpu -p"
            ret, stdout, stderr = ssh.exec_command(cmd)
#           parsing of "lscpu -p" output:
#           # CPU,Core,Socket,Node,,L1d,L1i,L2,L3
#           0,0,0,0,,0,0,0,0
#           1,1,0,0,,1,1,1,0
            if ret != 0:
                raise RuntimeError(
                    "Failed to execute ssh command, ret: {} err: {}".format(
                        ret, stderr))
            node['cpuinfo'] = list()
            for line in stdout.split("\n"):
                if len(line) > 0 and line[0] != "#":
                    node['cpuinfo'].append([CpuUtils.__str2int(x) for x in
                                            line.split(",")])

    @staticmethod
    def cpu_node_count(node):
        """Return count of numa nodes.

        :param node: Targeted node.
        :type node: dict
        :returns: Count of numa nodes.
        :rtype: int
        :raises RuntimeError: If node cpuinfo is not available.
        """
        cpu_info = node.get("cpuinfo")
        if cpu_info is not None:
            return node["cpuinfo"][-1][3] + 1
        else:
            raise RuntimeError("Node cpuinfo not available.")

    @staticmethod
    def cpu_list_per_node(node, cpu_node, smt_used=False):
        """Return node related list of CPU numbers.

        :param node: Node dictionary with cpuinfo.
        :param cpu_node: Numa node number.
        :param smt_used: True - we want to use SMT, otherwise false.
        :type node: dict
        :type cpu_node: int
        :type smt_used: bool
        :returns: List of cpu numbers related to numa from argument.
        :rtype: list of int
        :raises RuntimeError: If node cpuinfo is not available or if SMT is not
        enabled.
        """

        cpu_node = int(cpu_node)
        cpu_info = node.get("cpuinfo")
        if cpu_info is None:
            raise RuntimeError("Node cpuinfo not available.")

        smt_enabled = CpuUtils.is_smt_enabled(cpu_info)
        if not smt_enabled and smt_used:
            raise RuntimeError("SMT is not enabled.")

        cpu_list = []
        for cpu in cpu_info:
            if cpu[3] == cpu_node:
                cpu_list.append(cpu[0])

        if not smt_enabled or smt_enabled and smt_used:
            pass

        if smt_enabled and not smt_used:
            cpu_list_len = len(cpu_list)
            cpu_list = cpu_list[:cpu_list_len / CpuUtils.NR_OF_THREADS]

        return cpu_list

    @staticmethod
    def cpu_slice_of_list_per_node(node, cpu_node, skip_cnt=0, cpu_cnt=0,
                                   smt_used=False):
        """Return string of node related list of CPU numbers.

        :param node: Node dictionary with cpuinfo.
        :param cpu_node: Numa node number.
        :param skip_cnt: Skip first "skip_cnt" CPUs.
        :param cpu_cnt: Count of cpus to return, if 0 then return all.
        :param smt_used: True - we want to use SMT, otherwise false.
        :type node: dict
        :type cpu_node: int
        :type skip_cnt: int
        :type cpu_cnt: int
        :type smt_used: bool
        :returns: Cpu numbers related to numa from argument.
        :rtype: list
        :raises RuntimeError: If we require more cpus than available.
        """

        cpu_list = CpuUtils.cpu_list_per_node(node, cpu_node, smt_used)

        cpu_list_len = len(cpu_list)
        if cpu_cnt + skip_cnt > cpu_list_len:
            raise RuntimeError("cpu_cnt + skip_cnt > length(cpu list).")

        if cpu_cnt == 0:
            cpu_cnt = cpu_list_len - skip_cnt

        if smt_used:
            cpu_list_0 = cpu_list[:cpu_list_len / CpuUtils.NR_OF_THREADS]
            cpu_list_1 = cpu_list[cpu_list_len / CpuUtils.NR_OF_THREADS:]
            cpu_list = [cpu for cpu in cpu_list_0[skip_cnt:skip_cnt + cpu_cnt]]
            cpu_list_ex = [cpu for cpu in
                           cpu_list_1[skip_cnt:skip_cnt + cpu_cnt]]
            cpu_list.extend(cpu_list_ex)
        else:
            cpu_list = [cpu for cpu in cpu_list[skip_cnt:skip_cnt + cpu_cnt]]

        return cpu_list

    @staticmethod
    def cpu_list_per_node_str(node, cpu_node, skip_cnt=0, cpu_cnt=0, sep=",",
                              smt_used=False):
        """Return string of node related list of CPU numbers.

        :param node: Node dictionary with cpuinfo.
        :param cpu_node: Numa node number.
        :param skip_cnt: Skip first "skip_cnt" CPUs.
        :param cpu_cnt: Count of cpus to return, if 0 then return all.
        :param sep: Separator, default: 1,2,3,4,....
        :param smt_used: True - we want to use SMT, otherwise false.
        :type node: dict
        :type cpu_node: int
        :type skip_cnt: int
        :type cpu_cnt: int
        :type sep: str
        :type smt_used: bool
        :returns: Cpu numbers related to numa from argument.
        :rtype: str
        """

        cpu_list = CpuUtils.cpu_slice_of_list_per_node(node, cpu_node,
                                                       skip_cnt=skip_cnt,
                                                       cpu_cnt=cpu_cnt,
                                                       smt_used=smt_used)
        return sep.join(str(cpu) for cpu in cpu_list)

    @staticmethod
    def cpu_range_per_node_str(node, cpu_node, skip_cnt=0, cpu_cnt=0, sep="-",
                               smt_used=False):
        """Return string of node related range of CPU numbers, e.g. 0-4.

        :param node: Node dictionary with cpuinfo.
        :param cpu_node: Numa node number.
        :param skip_cnt: Skip first "skip_cnt" CPUs.
        :param cpu_cnt: Count of cpus to return, if 0 then return all.
        :param sep: Separator, default: "-".
        :param smt_used: True - we want to use SMT, otherwise false.
        :type node: dict
        :type cpu_node: int
        :type skip_cnt: int
        :type cpu_cnt: int
        :type sep: str
        :type smt_used: bool
        :returns: String of node related range of CPU numbers.
        :rtype: str
        """

        cpu_list = CpuUtils.cpu_slice_of_list_per_node(node, cpu_node,
                                                       skip_cnt=skip_cnt,
                                                       cpu_cnt=cpu_cnt,
                                                       smt_used=smt_used)
        if smt_used:
            cpu_list_len = len(cpu_list)
            cpu_list_0 = cpu_list[:cpu_list_len / CpuUtils.NR_OF_THREADS]
            cpu_list_1 = cpu_list[cpu_list_len / CpuUtils.NR_OF_THREADS:]
            cpu_range = "{}{}{},{}{}{}".format(cpu_list_0[0], sep,
                                               cpu_list_0[-1],
                                               cpu_list_1[0], sep,
                                               cpu_list_1[-1])
        else:
            cpu_range = "{}{}{}".format(cpu_list[0], sep, cpu_list[-1])

        return cpu_range
est_hton_2) { const u16 _vl_msg_id = 1; const u32 client_index = 3; vapi_type_msg_header2_t h; h._vl_msg_id = _vl_msg_id; h.client_index = client_index; vapi_type_msg_header2_t_hton (&h); ck_assert_int_eq (be16toh (h._vl_msg_id), _vl_msg_id); ck_assert_int_eq (h.client_index, client_index); } END_TEST; #define verify_hton_swap(expr, value) \ if (4 == sizeof (expr)) \ { \ ck_assert_int_eq (expr, htobe32 (value)); \ } \ else if (2 == sizeof (expr)) \ { \ ck_assert_int_eq (expr, htobe16 (value)); \ } \ else \ { \ ck_assert_int_eq (expr, value); \ } START_TEST (test_hton_4) { const int vla_count = 3; char x[sizeof (vapi_msg_bridge_domain_details) + vla_count * sizeof (vapi_type_bridge_domain_sw_if)]; vapi_msg_bridge_domain_details *d = (void *) x; int cnt = 1; d->header._vl_msg_id = cnt++; d->header.context = cnt++; d->payload.bd_id = cnt++; d->payload.mac_age = cnt++; d->payload.bvi_sw_if_index = cnt++; d->payload.n_sw_ifs = vla_count; int i; for (i = 0; i < vla_count; ++i) { vapi_type_bridge_domain_sw_if *det = &d->payload.sw_if_details[i]; det->context = cnt++; det->sw_if_index = cnt++; det->shg = cnt++; } ck_assert_int_eq (sizeof (x), vapi_calc_bridge_domain_details_msg_size (d)); vapi_msg_bridge_domain_details_hton (d); int tmp = 1; verify_hton_swap (d->header._vl_msg_id, tmp); ++tmp; ck_assert_int_eq (d->header.context, tmp); ++tmp; verify_hton_swap (d->payload.bd_id, tmp); ++tmp; verify_hton_swap (d->payload.mac_age, tmp); ++tmp; verify_hton_swap (d->payload.bvi_sw_if_index, tmp); ++tmp; ck_assert_int_eq (d->payload.n_sw_ifs, htobe32 (vla_count)); for (i = 0; i < vla_count; ++i) { vapi_type_bridge_domain_sw_if *det = &d->payload.sw_if_details[i]; verify_hton_swap (det->context, tmp); ++tmp; verify_hton_swap (det->sw_if_index, tmp); ++tmp; verify_hton_swap (det->shg, tmp); ++tmp; } vapi_msg_bridge_domain_details_ntoh (d); tmp = 1; ck_assert_int_eq (d->header._vl_msg_id, tmp); ++tmp; ck_assert_int_eq (d->header.context, tmp); ++tmp; ck_assert_int_eq (d->payload.bd_id, tmp); ++tmp; ck_assert_int_eq (d->payload.mac_age, tmp); ++tmp; ck_assert_int_eq (d->payload.bvi_sw_if_index, tmp); ++tmp; ck_assert_int_eq (d->payload.n_sw_ifs, vla_count); for (i = 0; i < vla_count; ++i) { vapi_type_bridge_domain_sw_if *det = &d->payload.sw_if_details[i]; ck_assert_int_eq (det->context, tmp); ++tmp; ck_assert_int_eq (det->sw_if_index, tmp); ++tmp; ck_assert_int_eq (det->shg, tmp); ++tmp; } ck_assert_int_eq (sizeof (x), vapi_calc_bridge_domain_details_msg_size (d)); } END_TEST; START_TEST (test_ntoh_1) { const u16 _vl_msg_id = 1; vapi_type_msg_header1_t h; h._vl_msg_id = _vl_msg_id; vapi_type_msg_header1_t_ntoh (&h); ck_assert_int_eq (htobe16 (h._vl_msg_id), _vl_msg_id); } END_TEST; START_TEST (test_ntoh_2) { const u16 _vl_msg_id = 1; const u32 client_index = 3; vapi_type_msg_header2_t h; h._vl_msg_id = _vl_msg_id; h.client_index = client_index; vapi_type_msg_header2_t_ntoh (&h); ck_assert_int_eq (htobe16 (h._vl_msg_id), _vl_msg_id); ck_assert_int_eq (h.client_index, client_index); } END_TEST; #define verify_ntoh_swap(expr, value) \ if (4 == sizeof (expr)) \ { \ ck_assert_int_eq (expr, be32toh (value)); \ } \ else if (2 == sizeof (expr)) \ { \ ck_assert_int_eq (expr, be16toh (value)); \ } \ else \ { \ ck_assert_int_eq (expr, value); \ } START_TEST (test_ntoh_4) { const int vla_count = 3; char x[sizeof (vapi_msg_bridge_domain_details) + vla_count * sizeof (vapi_type_bridge_domain_sw_if)]; vapi_msg_bridge_domain_details *d = (void *) x; int cnt = 1; d->header._vl_msg_id = cnt++; d->header.context = cnt++; d->payload.bd_id = cnt++; d->payload.mac_age = cnt++; d->payload.bvi_sw_if_index = cnt++; d->payload.n_sw_ifs = htobe32 (vla_count); int i; for (i = 0; i < vla_count; ++i) { vapi_type_bridge_domain_sw_if *det = &d->payload.sw_if_details[i]; det->context = cnt++; det->sw_if_index = cnt++; det->shg = cnt++; } vapi_msg_bridge_domain_details_ntoh (d); ck_assert_int_eq (sizeof (x), vapi_calc_bridge_domain_details_msg_size (d)); int tmp = 1; verify_ntoh_swap (d->header._vl_msg_id, tmp); ++tmp; ck_assert_int_eq (d->header.context, tmp); ++tmp; verify_ntoh_swap (d->payload.bd_id, tmp); ++tmp; verify_ntoh_swap (d->payload.mac_age, tmp); ++tmp; verify_ntoh_swap (d->payload.bvi_sw_if_index, tmp); ++tmp; ck_assert_int_eq (d->payload.n_sw_ifs, vla_count); for (i = 0; i < vla_count; ++i) { vapi_type_bridge_domain_sw_if *det = &d->payload.sw_if_details[i]; verify_ntoh_swap (det->context, tmp); ++tmp; verify_ntoh_swap (det->sw_if_index, tmp); ++tmp; verify_ntoh_swap (det->shg, tmp); ++tmp; } vapi_msg_bridge_domain_details_hton (d); tmp = 1; ck_assert_int_eq (d->header._vl_msg_id, tmp); ++tmp; ck_assert_int_eq (d->header.context, tmp); ++tmp; ck_assert_int_eq (d->payload.bd_id, tmp); ++tmp; ck_assert_int_eq (d->payload.mac_age, tmp); ++tmp; ck_assert_int_eq (d->payload.bvi_sw_if_index, tmp); ++tmp; ck_assert_int_eq (d->payload.n_sw_ifs, htobe32 (vla_count)); for (i = 0; i < vla_count; ++i) { vapi_type_bridge_domain_sw_if *det = &d->payload.sw_if_details[i]; ck_assert_int_eq (det->context, tmp); ++tmp; ck_assert_int_eq (det->sw_if_index, tmp); ++tmp; ck_assert_int_eq (det->shg, tmp); ++tmp; } } END_TEST; vapi_error_e show_version_cb (vapi_ctx_t ctx, void *caller_ctx, vapi_error_e rv, bool is_last, vapi_payload_show_version_reply * p) { ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (true, is_last); ck_assert_str_eq ("vpe", (char *) p->program); printf ("show_version_reply: program: `%s', version: `%s', build directory: " "`%s', build date: `%s'\n", p->program, p->version, p->build_directory, p->build_date); ++*(int *) caller_ctx; return VAPI_OK; } typedef struct { int called; int expected_retval; u32 *sw_if_index_storage; } test_create_loopback_ctx_t; vapi_error_e loopback_create_cb (vapi_ctx_t ctx, void *caller_ctx, vapi_error_e rv, bool is_last, vapi_payload_create_loopback_reply * p) { test_create_loopback_ctx_t *clc = caller_ctx; ck_assert_int_eq (clc->expected_retval, p->retval); *clc->sw_if_index_storage = p->sw_if_index; ++clc->called; return VAPI_OK; } typedef struct { int called; int expected_retval; u32 *sw_if_index_storage; } test_delete_loopback_ctx_t; vapi_error_e loopback_delete_cb (vapi_ctx_t ctx, void *caller_ctx, vapi_error_e rv, bool is_last, vapi_payload_delete_loopback_reply * p) { test_delete_loopback_ctx_t *dlc = caller_ctx; ck_assert_int_eq (dlc->expected_retval, p->retval); ++dlc->called; return VAPI_OK; } START_TEST (test_connect) { vapi_ctx_t ctx; vapi_error_e rv = vapi_ctx_alloc (&ctx); ck_assert_int_eq (VAPI_OK, rv); rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests, response_queue_size, VAPI_MODE_BLOCKING, true); ck_assert_int_eq (VAPI_OK, rv); rv = vapi_disconnect (ctx); ck_assert_int_eq (VAPI_OK, rv); vapi_ctx_free (ctx); } END_TEST; vapi_ctx_t ctx; void setup_blocking (void) { vapi_error_e rv = vapi_ctx_alloc (&ctx); ck_assert_int_eq (VAPI_OK, rv); rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests, response_queue_size, VAPI_MODE_BLOCKING, true); ck_assert_int_eq (VAPI_OK, rv); } void setup_nonblocking (void) { vapi_error_e rv = vapi_ctx_alloc (&ctx); ck_assert_int_eq (VAPI_OK, rv); rv = vapi_connect (ctx, app_name, api_prefix, max_outstanding_requests, response_queue_size, VAPI_MODE_NONBLOCKING, true); ck_assert_int_eq (VAPI_OK, rv); } void teardown (void) { vapi_disconnect (ctx); vapi_ctx_free (ctx); } START_TEST (test_show_version_1) { printf ("--- Basic show version message - reply test ---\n"); vapi_msg_show_version *sv = vapi_alloc_show_version (ctx); ck_assert_ptr_ne (NULL, sv); vapi_msg_show_version_hton (sv); vapi_error_e rv = vapi_send (ctx, sv); ck_assert_int_eq (VAPI_OK, rv); vapi_msg_show_version_reply *resp; size_t size; rv = vapi_recv (ctx, (void *) &resp, &size, 0, 0); ck_assert_int_eq (VAPI_OK, rv); int placeholder; show_version_cb (NULL, &placeholder, VAPI_OK, true, &resp->payload); vapi_msg_free (ctx, resp); } END_TEST; START_TEST (test_show_version_2) { int called = 0; printf ("--- Show version via blocking callback API ---\n"); const int attempts = response_queue_size * 4; int i = 0; for (i = 0; i < attempts; ++i) { vapi_msg_show_version *sv = vapi_alloc_show_version (ctx); ck_assert_ptr_ne (NULL, sv); vapi_error_e rv = vapi_show_version (ctx, sv, show_version_cb, &called); ck_assert_int_eq (VAPI_OK, rv); } ck_assert_int_eq (attempts, called); } END_TEST; typedef struct { bool last_called; size_t num_ifs; u32 *sw_if_indexes; bool *seen; int called; } sw_interface_dump_ctx; vapi_error_e sw_interface_dump_cb (struct vapi_ctx_s *ctx, void *callback_ctx, vapi_error_e rv, bool is_last, vapi_payload_sw_interface_details * reply) { sw_interface_dump_ctx *dctx = callback_ctx; ck_assert_int_eq (false, dctx->last_called); if (is_last) { ck_assert (NULL == reply); dctx->last_called = true; } else { ck_assert (NULL != reply); printf ("Interface dump entry: [%u]: %s\n", reply->sw_if_index, reply->interface_name); size_t i = 0; for (i = 0; i < dctx->num_ifs; ++i) { if (dctx->sw_if_indexes[i] == reply->sw_if_index) { ck_assert_int_eq (false, dctx->seen[i]); dctx->seen[i] = true; } } } ++dctx->called; return VAPI_OK; } START_TEST (test_loopbacks_1) { printf ("--- Create/delete loopbacks using blocking API ---\n"); const size_t num_ifs = 5; u8 mac_addresses[num_ifs][6]; clib_memset (&mac_addresses, 0, sizeof (mac_addresses)); u32 sw_if_indexes[num_ifs]; clib_memset (&sw_if_indexes, 0xff, sizeof (sw_if_indexes)); test_create_loopback_ctx_t clcs[num_ifs]; clib_memset (&clcs, 0, sizeof (clcs)); test_delete_loopback_ctx_t dlcs[num_ifs]; clib_memset (&dlcs, 0, sizeof (dlcs)); int i; for (i = 0; i < num_ifs; ++i) { memcpy (&mac_addresses[i], "\1\2\3\4\5\6", 6); mac_addresses[i][5] = i; clcs[i].sw_if_index_storage = &sw_if_indexes[i]; } for (i = 0; i < num_ifs; ++i) { vapi_msg_create_loopback *cl = vapi_alloc_create_loopback (ctx); memcpy (cl->payload.mac_address, mac_addresses[i], sizeof (cl->payload.mac_address)); vapi_error_e rv = vapi_create_loopback (ctx, cl, loopback_create_cb, &clcs[i]); ck_assert_int_eq (VAPI_OK, rv); } for (i = 0; i < num_ifs; ++i) { ck_assert_int_eq (1, clcs[i].called); printf ("Created loopback with MAC %02x:%02x:%02x:%02x:%02x:%02x --> " "sw_if_index %u\n", mac_addresses[i][0], mac_addresses[i][1], mac_addresses[i][2], mac_addresses[i][3], mac_addresses[i][4], mac_addresses[i][5], sw_if_indexes[i]); } bool seen[num_ifs]; sw_interface_dump_ctx dctx = { false, num_ifs, sw_if_indexes, seen, 0 }; vapi_msg_sw_interface_dump *dump; vapi_error_e rv; const int attempts = response_queue_size * 4; for (i = 0; i < attempts; ++i) { dctx.last_called = false; clib_memset (&seen, 0, sizeof (seen)); dump = vapi_alloc_sw_interface_dump (ctx); while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx))) ; ck_assert_int_eq (true, dctx.last_called); int j = 0; for (j = 0; j < num_ifs; ++j) { ck_assert_int_eq (true, seen[j]); } } clib_memset (&seen, 0, sizeof (seen)); for (i = 0; i < num_ifs; ++i) { vapi_msg_delete_loopback *dl = vapi_alloc_delete_loopback (ctx); dl->payload.sw_if_index = sw_if_indexes[i]; vapi_error_e rv = vapi_delete_loopback (ctx, dl, loopback_delete_cb, &dlcs[i]); ck_assert_int_eq (VAPI_OK, rv); } for (i = 0; i < num_ifs; ++i) { ck_assert_int_eq (1, dlcs[i].called); printf ("Deleted loopback with sw_if_index %u\n", sw_if_indexes[i]); } dctx.last_called = false; clib_memset (&seen, 0, sizeof (seen)); dump = vapi_alloc_sw_interface_dump (ctx); while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx))) ; ck_assert_int_eq (true, dctx.last_called); for (i = 0; i < num_ifs; ++i) { ck_assert_int_eq (false, seen[i]); } } END_TEST; START_TEST (test_show_version_3) { printf ("--- Show version via async callback ---\n"); int called = 0; vapi_error_e rv; vapi_msg_show_version *sv = vapi_alloc_show_version (ctx); ck_assert_ptr_ne (NULL, sv); while (VAPI_EAGAIN == (rv = vapi_show_version (ctx, sv, show_version_cb, &called))) ; ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (0, called); rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (1, called); called = 0; rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (0, called); } END_TEST; START_TEST (test_show_version_4) { printf ("--- Show version via async callback - multiple messages ---\n"); vapi_error_e rv; const size_t num_req = 5; int contexts[num_req]; clib_memset (contexts, 0, sizeof (contexts)); int i; for (i = 0; i < num_req; ++i) { vapi_msg_show_version *sv = vapi_alloc_show_version (ctx); ck_assert_ptr_ne (NULL, sv); while (VAPI_EAGAIN == (rv = vapi_show_version (ctx, sv, show_version_cb, &contexts[i]))) ; ck_assert_int_eq (VAPI_OK, rv); int j; for (j = 0; j < num_req; ++j) { ck_assert_int_eq (0, contexts[j]); } } rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); for (i = 0; i < num_req; ++i) { ck_assert_int_eq (1, contexts[i]); } clib_memset (contexts, 0, sizeof (contexts)); rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); for (i = 0; i < num_req; ++i) { ck_assert_int_eq (0, contexts[i]); } } END_TEST; START_TEST (test_loopbacks_2) { printf ("--- Create/delete loopbacks using non-blocking API ---\n"); vapi_error_e rv; const size_t num_ifs = 5; u8 mac_addresses[num_ifs][6]; clib_memset (&mac_addresses, 0, sizeof (mac_addresses)); u32 sw_if_indexes[num_ifs]; clib_memset (&sw_if_indexes, 0xff, sizeof (sw_if_indexes)); test_create_loopback_ctx_t clcs[num_ifs]; clib_memset (&clcs, 0, sizeof (clcs)); test_delete_loopback_ctx_t dlcs[num_ifs]; clib_memset (&dlcs, 0, sizeof (dlcs)); int i; for (i = 0; i < num_ifs; ++i) { memcpy (&mac_addresses[i], "\1\2\3\4\5\6", 6); mac_addresses[i][5] = i; clcs[i].sw_if_index_storage = &sw_if_indexes[i]; } for (i = 0; i < num_ifs; ++i) { vapi_msg_create_loopback *cl = vapi_alloc_create_loopback (ctx); memcpy (cl->payload.mac_address, mac_addresses[i], sizeof (cl->payload.mac_address)); while (VAPI_EAGAIN == (rv = vapi_create_loopback (ctx, cl, loopback_create_cb, &clcs[i]))) ; ck_assert_int_eq (VAPI_OK, rv); } rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); for (i = 0; i < num_ifs; ++i) { ck_assert_int_eq (1, clcs[i].called); printf ("Loopback with MAC %02x:%02x:%02x:%02x:%02x:%02x --> " "sw_if_index %u\n", mac_addresses[i][0], mac_addresses[i][1], mac_addresses[i][2], mac_addresses[i][3], mac_addresses[i][4], mac_addresses[i][5], sw_if_indexes[i]); } bool seen[num_ifs]; clib_memset (&seen, 0, sizeof (seen)); sw_interface_dump_ctx dctx = { false, num_ifs, sw_if_indexes, seen, 0 }; vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx); while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx))) ; for (i = 0; i < num_ifs; ++i) { ck_assert_int_eq (false, seen[i]); } clib_memset (&seen, 0, sizeof (seen)); ck_assert_int_eq (false, dctx.last_called); rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); for (i = 0; i < num_ifs; ++i) { ck_assert_int_eq (true, seen[i]); } clib_memset (&seen, 0, sizeof (seen)); ck_assert_int_eq (true, dctx.last_called); for (i = 0; i < num_ifs; ++i) { vapi_msg_delete_loopback *dl = vapi_alloc_delete_loopback (ctx); dl->payload.sw_if_index = sw_if_indexes[i]; while (VAPI_EAGAIN == (rv = vapi_delete_loopback (ctx, dl, loopback_delete_cb, &dlcs[i]))) ; ck_assert_int_eq (VAPI_OK, rv); } rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); for (i = 0; i < num_ifs; ++i) { ck_assert_int_eq (1, dlcs[i].called); printf ("Deleted loopback with sw_if_index %u\n", sw_if_indexes[i]); } clib_memset (&seen, 0, sizeof (seen)); dctx.last_called = false; dump = vapi_alloc_sw_interface_dump (ctx); while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, sw_interface_dump_cb, &dctx))) ; rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); for (i = 0; i < num_ifs; ++i) { ck_assert_int_eq (false, seen[i]); } clib_memset (&seen, 0, sizeof (seen)); ck_assert_int_eq (true, dctx.last_called); } END_TEST; vapi_error_e generic_cb (vapi_ctx_t ctx, void *callback_ctx, vapi_msg_id_t id, void *msg) { int *called = callback_ctx; ck_assert_int_eq (0, *called); ++*called; ck_assert_int_eq (id, vapi_msg_id_show_version_reply); ck_assert_ptr_ne (NULL, msg); vapi_msg_show_version_reply *reply = msg; ck_assert_str_eq ("vpe", (char *) reply->payload.program); return VAPI_OK; } START_TEST (test_show_version_5) { printf ("--- Receive show version using generic callback - nonblocking " "API ---\n"); vapi_error_e rv; vapi_msg_show_version *sv = vapi_alloc_show_version (ctx); ck_assert_ptr_ne (NULL, sv); vapi_msg_show_version_hton (sv); while (VAPI_EAGAIN == (rv = vapi_send (ctx, sv))) ; ck_assert_int_eq (VAPI_OK, rv); int called = 0; vapi_set_generic_event_cb (ctx, generic_cb, &called); ck_assert_int_eq (VAPI_OK, rv); rv = vapi_dispatch_one (ctx); ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (1, called); sv = vapi_alloc_show_version (ctx); ck_assert_ptr_ne (NULL, sv); vapi_msg_show_version_hton (sv); while (VAPI_EAGAIN == (rv = vapi_send (ctx, sv))) ; ck_assert_int_eq (VAPI_OK, rv); vapi_clear_generic_event_cb (ctx); rv = vapi_dispatch_one (ctx); ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (1, called); /* needs to remain unchanged */ } END_TEST; vapi_error_e show_version_no_cb (vapi_ctx_t ctx, void *caller_ctx, vapi_error_e rv, bool is_last, vapi_payload_show_version_reply * p) { ck_assert_int_eq (VAPI_ENORESP, rv); ck_assert_int_eq (true, is_last); ck_assert_ptr_eq (NULL, p); ++*(int *) caller_ctx; return VAPI_OK; } START_TEST (test_no_response_1) { printf ("--- Simulate no response to regular message ---\n"); vapi_error_e rv; vapi_msg_show_version *sv = vapi_alloc_show_version (ctx); ck_assert_ptr_ne (NULL, sv); sv->header._vl_msg_id = ~0; /* malformed ID causes vpp to drop the msg */ int called = 0; while (VAPI_EAGAIN == (rv = vapi_show_version (ctx, sv, show_version_no_cb, &called))) ; ck_assert_int_eq (VAPI_OK, rv); sv = vapi_alloc_show_version (ctx); ck_assert_ptr_ne (NULL, sv); while (VAPI_EAGAIN == (rv = vapi_show_version (ctx, sv, show_version_cb, &called))) ; ck_assert_int_eq (VAPI_OK, rv); rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (2, called); } END_TEST; vapi_error_e no_msg_cb (struct vapi_ctx_s *ctx, void *callback_ctx, vapi_error_e rv, bool is_last, vapi_payload_sw_interface_details * reply) { int *called = callback_ctx; ++*called; ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (true, is_last); ck_assert_ptr_eq (NULL, reply); return VAPI_OK; } START_TEST (test_no_response_2) { printf ("--- Simulate no response to dump message ---\n"); vapi_error_e rv; vapi_msg_sw_interface_dump *dump = vapi_alloc_sw_interface_dump (ctx); dump->header._vl_msg_id = ~0; /* malformed ID causes vpp to drop the msg */ int no_called = 0; while (VAPI_EAGAIN == (rv = vapi_sw_interface_dump (ctx, dump, no_msg_cb, &no_called))) ; ck_assert_int_eq (VAPI_OK, rv); rv = vapi_dispatch (ctx); ck_assert_int_eq (VAPI_OK, rv); ck_assert_int_eq (1, no_called); } END_TEST; START_TEST (test_unsupported) { printf ("--- Unsupported messages ---\n"); bool available = vapi_is_msg_available (ctx, vapi_msg_id_test_fake_msg); ck_assert_int_eq (false, available); } END_TEST; START_TEST (test_api_strings) { printf ("--- Invalid api strings ---\n"); /* test string 'TEST' * size = 5 * length = 4 */ const char str[] = "TEST"; u8 *vec_str = 0, *vstr = 0; char *cstr; vapi_msg_sw_interface_dump *dump = malloc (sizeof (vapi_msg_sw_interface_dump) + strlen (str)); clib_mem_init (0, 1 << 20); vl_api_c_string_to_api_string (str, &dump->payload.name_filter); /* Assert nul terminator NOT present */ ck_assert_int_eq (vl_api_string_len (&dump->payload.name_filter), strlen (str)); cstr = vl_api_from_api_to_new_c_string (&dump->payload.name_filter); ck_assert_ptr_ne (cstr, NULL); /* Assert nul terminator present */ ck_assert_int_eq (vec_len (cstr), sizeof (str)); ck_assert_int_eq (strlen (str), strlen (cstr)); vec_free (cstr); vstr = vl_api_from_api_to_new_vec (0 /* not really an API message */ , &dump->payload.name_filter); ck_assert_ptr_ne (vstr, NULL); /* Assert nul terminator NOT present */ ck_assert_int_eq (vec_len (vstr), strlen (str)); vec_free (vstr); /* vector conaining NON nul terminated string 'TEST' */ vec_add (vec_str, str, strlen (str)); clib_memset (dump->payload.name_filter.buf, 0, strlen (str)); dump->payload.name_filter.length = 0; vl_api_vec_to_api_string (vec_str, &dump->payload.name_filter); /* Assert nul terminator NOT present */ ck_assert_int_eq (vl_api_string_len (&dump->payload.name_filter), vec_len (vec_str)); cstr = vl_api_from_api_to_new_c_string (&dump->payload.name_filter); ck_assert_ptr_ne (cstr, NULL); /* Assert nul terminator present */ ck_assert_int_eq (vec_len (cstr), sizeof (str)); ck_assert_int_eq (strlen (str), strlen (cstr)); vec_free (cstr); vstr = vl_api_from_api_to_new_vec (0 /* not a real api msg */ , &dump->payload.name_filter); ck_assert_ptr_ne (vstr, NULL); /* Assert nul terminator NOT present */ ck_assert_int_eq (vec_len (vstr), strlen (str)); vec_free (vstr); } END_TEST; Suite * test_suite (void) { Suite *s = suite_create ("VAPI test"); TCase *tc_negative = tcase_create ("Negative tests"); tcase_add_test (tc_negative, test_invalid_values); suite_add_tcase (s, tc_negative); TCase *tc_swap = tcase_create ("Byteswap tests"); tcase_add_test (tc_swap, test_hton_1); tcase_add_test (tc_swap, test_hton_2); tcase_add_test (tc_swap, test_hton_4); tcase_add_test (tc_swap, test_ntoh_1); tcase_add_test (tc_swap, test_ntoh_2); tcase_add_test (tc_swap, test_ntoh_4); suite_add_tcase (s, tc_swap); TCase *tc_connect = tcase_create ("Connect"); tcase_add_test (tc_connect, test_connect); suite_add_tcase (s, tc_connect); TCase *tc_block = tcase_create ("Blocking API"); tcase_set_timeout (tc_block, 25); tcase_add_checked_fixture (tc_block, setup_blocking, teardown); tcase_add_test (tc_block, test_show_version_1); tcase_add_test (tc_block, test_show_version_2); tcase_add_test (tc_block, test_loopbacks_1); suite_add_tcase (s, tc_block); TCase *tc_nonblock = tcase_create ("Nonblocking API"); tcase_set_timeout (tc_nonblock, 25); tcase_add_checked_fixture (tc_nonblock, setup_nonblocking, teardown); tcase_add_test (tc_nonblock, test_show_version_3); tcase_add_test (tc_nonblock, test_show_version_4); tcase_add_test (tc_nonblock, test_show_version_5); tcase_add_test (tc_nonblock, test_loopbacks_2); tcase_add_test (tc_nonblock, test_no_response_1); tcase_add_test (tc_nonblock, test_no_response_2); suite_add_tcase (s, tc_nonblock); TCase *tc_unsupported = tcase_create ("Unsupported message"); tcase_add_checked_fixture (tc_unsupported, setup_blocking, teardown); tcase_add_test (tc_unsupported, test_unsupported); suite_add_tcase (s, tc_unsupported); TCase *tc_dynamic = tcase_create ("Dynamic message size"); tcase_add_test (tc_dynamic, test_api_strings); suite_add_tcase (s, tc_dynamic); return s; } int main (int argc, char *argv[]) { if (3 != argc) { printf ("Invalid argc==`%d'\n", argc); return EXIT_FAILURE; } app_name = argv[1]; api_prefix = argv[2]; printf ("App name: `%s', API prefix: `%s'\n", app_name, api_prefix); int number_failed; Suite *s; SRunner *sr; s = test_suite (); sr = srunner_create (s); srunner_run_all (sr, CK_NORMAL); number_failed = srunner_ntests_failed (sr); srunner_free (sr); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */