]>
Commit | Line | Data |
---|---|---|
50f96b10 | 1 | /* Copyright (c) 2011, 2012, 2013, 2014, 2017 Nicira, Inc. |
daff3353 EJ |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
3f636c7e | 17 | #undef NDEBUG |
daff3353 EJ |
18 | #include <math.h> |
19 | #include <stdlib.h> | |
b598f214 | 20 | #include "bundle.h" |
daff3353 | 21 | #include "flow.h" |
b598f214 | 22 | #include "openvswitch/ofp-actions.h" |
64c96779 | 23 | #include "openvswitch/ofpbuf.h" |
eadd1644 | 24 | #include "ovstest.h" |
3f636c7e | 25 | #include "util.h" |
daff3353 EJ |
26 | |
27 | #define N_FLOWS 50000 | |
91fc374a | 28 | #define MAX_MEMBERS 8 /* Maximum supported by this test framework. */ |
daff3353 | 29 | |
91fc374a BP |
30 | struct member { |
31 | ofp_port_t member_id; | |
daff3353 EJ |
32 | |
33 | bool enabled; | |
34 | size_t flow_count; | |
35 | }; | |
36 | ||
91fc374a BP |
37 | struct member_group { |
38 | size_t n_members; | |
39 | struct member members[MAX_MEMBERS]; | |
daff3353 EJ |
40 | }; |
41 | ||
91fc374a BP |
42 | static struct member * |
43 | member_lookup(struct member_group *sg, ofp_port_t member_id) | |
daff3353 EJ |
44 | { |
45 | size_t i; | |
46 | ||
91fc374a BP |
47 | for (i = 0; i < sg->n_members; i++) { |
48 | if (sg->members[i].member_id == member_id) { | |
49 | return &sg->members[i]; | |
daff3353 EJ |
50 | } |
51 | } | |
52 | ||
53 | return NULL; | |
54 | } | |
55 | ||
56 | static bool | |
91fc374a | 57 | member_enabled_cb(ofp_port_t member_id, void *aux) |
daff3353 | 58 | { |
91fc374a | 59 | struct member *member; |
daff3353 | 60 | |
91fc374a BP |
61 | member = member_lookup(aux, member_id); |
62 | return member ? member->enabled : false; | |
daff3353 EJ |
63 | } |
64 | ||
f25d0cf3 | 65 | static struct ofpact_bundle * |
daff3353 EJ |
66 | parse_bundle_actions(char *actions) |
67 | { | |
f25d0cf3 BP |
68 | struct ofpact_bundle *bundle; |
69 | struct ofpbuf ofpacts; | |
70 | struct ofpact *action; | |
bdda5aca | 71 | char *error; |
daff3353 | 72 | |
f25d0cf3 | 73 | ofpbuf_init(&ofpacts, 0); |
50f96b10 | 74 | error = bundle_parse_load(actions, NULL, &ofpacts); |
bdda5aca BP |
75 | if (error) { |
76 | ovs_fatal(0, "%s", error); | |
77 | } | |
78 | ||
6fd6ed71 | 79 | action = ofpacts.data; |
f25d0cf3 BP |
80 | bundle = ofpact_get_BUNDLE(xmemdup(action, action->len)); |
81 | ofpbuf_uninit(&ofpacts); | |
daff3353 | 82 | |
91fc374a BP |
83 | if (bundle->n_members > MAX_MEMBERS) { |
84 | ovs_fatal(0, "At most %u members are supported", MAX_MEMBERS); | |
daff3353 EJ |
85 | } |
86 | ||
f25d0cf3 | 87 | return bundle; |
daff3353 EJ |
88 | } |
89 | ||
90 | static const char * | |
91 | mask_str(uint8_t mask, size_t n_bits) | |
92 | { | |
93 | static char str[9]; | |
94 | size_t i; | |
95 | ||
96 | n_bits = MIN(n_bits, 8); | |
97 | for (i = 0; i < n_bits; i++) { | |
98 | str[i] = (1 << i) & mask ? '1' : '0'; | |
99 | } | |
100 | str[i] = '\0'; | |
101 | ||
102 | return str; | |
103 | } | |
104 | ||
eadd1644 AZ |
105 | static void |
106 | test_bundle_main(int argc, char *argv[]) | |
daff3353 EJ |
107 | { |
108 | bool ok = true; | |
f25d0cf3 | 109 | struct ofpact_bundle *bundle; |
daff3353 EJ |
110 | struct flow *flows; |
111 | size_t i, n_permute, old_n_enabled; | |
91fc374a | 112 | struct member_group sg; |
04a85497 | 113 | int old_active; |
daff3353 EJ |
114 | |
115 | set_program_name(argv[0]); | |
daff3353 EJ |
116 | |
117 | if (argc != 2) { | |
118 | ovs_fatal(0, "usage: %s bundle_action", program_name); | |
119 | } | |
120 | ||
f25d0cf3 | 121 | bundle = parse_bundle_actions(argv[1]); |
daff3353 | 122 | |
91fc374a BP |
123 | /* Generate 'members' array. */ |
124 | sg.n_members = 0; | |
125 | for (i = 0; i < bundle->n_members; i++) { | |
126 | ofp_port_t member_id = bundle->members[i]; | |
daff3353 | 127 | |
91fc374a BP |
128 | if (member_lookup(&sg, member_id)) { |
129 | ovs_fatal(0, "Redundant members are not supported. "); | |
daff3353 EJ |
130 | } |
131 | ||
91fc374a BP |
132 | sg.members[sg.n_members].member_id = member_id; |
133 | sg.n_members++; | |
daff3353 EJ |
134 | } |
135 | ||
136 | /* Generate flows. */ | |
137 | flows = xmalloc(N_FLOWS * sizeof *flows); | |
138 | for (i = 0; i < N_FLOWS; i++) { | |
94639963 | 139 | flow_random_hash_fields(&flows[i]); |
4e022ec0 | 140 | flows[i].regs[0] = ofp_to_u16(OFPP_NONE); |
daff3353 EJ |
141 | } |
142 | ||
143 | /* Cycles through each possible liveness permutation for the given | |
91fc374a | 144 | * n_members. The initial state is equivalent to all members down, so we |
daff3353 EJ |
145 | * skip it by starting at i = 1. We do one extra iteration to cover |
146 | * transitioning from the final state back to the initial state. */ | |
147 | old_n_enabled = 0; | |
04a85497 | 148 | old_active = -1; |
91fc374a | 149 | n_permute = 1 << sg.n_members; |
daff3353 | 150 | for (i = 1; i <= n_permute + 1; i++) { |
91fc374a | 151 | struct member *member; |
daff3353 EJ |
152 | size_t j, n_enabled, changed; |
153 | double disruption, perfect; | |
154 | uint8_t mask; | |
04a85497 | 155 | int active; |
daff3353 EJ |
156 | |
157 | mask = i % n_permute; | |
158 | ||
91fc374a | 159 | /* Gray coding ensures that in each iteration exactly one member |
daff3353 EJ |
160 | * changes its liveness. This makes the expected disruption a bit |
161 | * easier to calculate, and is likely similar to how failures will be | |
162 | * experienced in the wild. */ | |
163 | mask = mask ^ (mask >> 1); | |
164 | ||
91fc374a | 165 | /* Initialize members. */ |
daff3353 | 166 | n_enabled = 0; |
91fc374a BP |
167 | for (j = 0; j < sg.n_members; j++) { |
168 | member = &sg.members[j]; | |
169 | member->flow_count = 0; | |
170 | member->enabled = ((1 << j) & mask) != 0; | |
daff3353 | 171 | |
91fc374a | 172 | if (member->enabled) { |
daff3353 EJ |
173 | n_enabled++; |
174 | } | |
175 | } | |
176 | ||
04a85497 | 177 | active = -1; |
91fc374a BP |
178 | for (j = 0; j < sg.n_members; j++) { |
179 | if (sg.members[j].enabled) { | |
04a85497 EJ |
180 | active = j; |
181 | break; | |
182 | } | |
183 | } | |
184 | ||
daff3353 EJ |
185 | changed = 0; |
186 | for (j = 0; j < N_FLOWS; j++) { | |
187 | struct flow *flow = &flows[j]; | |
91fc374a | 188 | ofp_port_t old_member_id, ofp_port; |
bcd2633a | 189 | struct flow_wildcards wc; |
daff3353 | 190 | |
91fc374a BP |
191 | old_member_id = u16_to_ofp(flow->regs[0]); |
192 | ofp_port = bundle_execute(bundle, flow, &wc, member_enabled_cb, | |
bcd2633a | 193 | &sg); |
4e022ec0 | 194 | flow->regs[0] = ofp_to_u16(ofp_port); |
daff3353 | 195 | |
f25d0cf3 | 196 | if (ofp_port != OFPP_NONE) { |
91fc374a | 197 | member_lookup(&sg, ofp_port)->flow_count++; |
daff3353 EJ |
198 | } |
199 | ||
91fc374a | 200 | if (old_member_id != ofp_port) { |
daff3353 EJ |
201 | changed++; |
202 | } | |
203 | } | |
204 | ||
f25d0cf3 | 205 | if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) { |
04a85497 | 206 | perfect = active == old_active ? 0.0 : 1.0; |
daff3353 | 207 | } else { |
04a85497 EJ |
208 | if (old_n_enabled || n_enabled) { |
209 | perfect = 1.0 / MAX(old_n_enabled, n_enabled); | |
210 | } else { | |
91fc374a | 211 | /* This will happen when 'sg.n_members' is 0. */ |
04a85497 EJ |
212 | perfect = 0; |
213 | } | |
daff3353 EJ |
214 | } |
215 | ||
216 | disruption = changed / (double)N_FLOWS; | |
04a85497 | 217 | printf("%s: disruption=%.2f (perfect=%.2f)", |
91fc374a | 218 | mask_str(mask, sg.n_members), disruption, perfect); |
daff3353 | 219 | |
91fc374a BP |
220 | for (j = 0 ; j < sg.n_members; j++) { |
221 | member = &sg.members[j]; | |
daff3353 EJ |
222 | double flow_percent; |
223 | ||
91fc374a | 224 | flow_percent = member->flow_count / (double)N_FLOWS; |
04a85497 | 225 | printf( " %.2f", flow_percent); |
daff3353 | 226 | |
91fc374a | 227 | if (member->enabled) { |
04a85497 EJ |
228 | double perfect_fp; |
229 | ||
f25d0cf3 | 230 | if (bundle->algorithm == NX_BD_ALG_ACTIVE_BACKUP) { |
04a85497 EJ |
231 | perfect_fp = j == active ? 1.0 : 0.0; |
232 | } else { | |
233 | perfect_fp = 1.0 / n_enabled; | |
234 | } | |
daff3353 EJ |
235 | |
236 | if (fabs(flow_percent - perfect_fp) >= .01) { | |
91fc374a | 237 | fprintf(stderr, "%s: member %d: flow_percentage=%.5f for" |
daff3353 | 238 | " differs from perfect=%.5f by more than .01\n", |
91fc374a | 239 | mask_str(mask, sg.n_members), member->member_id, |
daff3353 EJ |
240 | flow_percent, perfect_fp); |
241 | ok = false; | |
242 | } | |
91fc374a BP |
243 | } else if (member->flow_count) { |
244 | fprintf(stderr, "%s: member %d: disabled member received" | |
245 | " flows.\n", mask_str(mask, sg.n_members), | |
246 | member->member_id); | |
daff3353 EJ |
247 | ok = false; |
248 | } | |
249 | } | |
250 | printf("\n"); | |
251 | ||
252 | if (fabs(disruption - perfect) >= .01) { | |
253 | fprintf(stderr, "%s: disruption=%.5f differs from perfect=%.5f by" | |
91fc374a | 254 | " more than .01\n", mask_str(mask, sg.n_members), |
daff3353 EJ |
255 | disruption, perfect); |
256 | ok = false; | |
257 | } | |
258 | ||
04a85497 | 259 | old_active = active; |
daff3353 EJ |
260 | old_n_enabled = n_enabled; |
261 | } | |
262 | ||
f25d0cf3 | 263 | free(bundle); |
daff3353 | 264 | free(flows); |
eadd1644 | 265 | exit(ok ? 0 : 1); |
daff3353 | 266 | } |
eadd1644 AZ |
267 | |
268 | OVSTEST_REGISTER("test-bundle", test_bundle_main); |