]>
Commit | Line | Data |
---|---|---|
eadd1644 | 1 | /* Copyright (c) 2011, 2012, 2013, 2014 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 | |
28 | #define MAX_SLAVES 8 /* Maximum supported by this test framework. */ | |
29 | ||
30 | struct slave { | |
4e022ec0 | 31 | ofp_port_t slave_id; |
daff3353 EJ |
32 | |
33 | bool enabled; | |
34 | size_t flow_count; | |
35 | }; | |
36 | ||
37 | struct slave_group { | |
38 | size_t n_slaves; | |
39 | struct slave slaves[MAX_SLAVES]; | |
40 | }; | |
41 | ||
42 | static struct slave * | |
4e022ec0 | 43 | slave_lookup(struct slave_group *sg, ofp_port_t slave_id) |
daff3353 EJ |
44 | { |
45 | size_t i; | |
46 | ||
47 | for (i = 0; i < sg->n_slaves; i++) { | |
48 | if (sg->slaves[i].slave_id == slave_id) { | |
49 | return &sg->slaves[i]; | |
50 | } | |
51 | } | |
52 | ||
53 | return NULL; | |
54 | } | |
55 | ||
56 | static bool | |
4e022ec0 | 57 | slave_enabled_cb(ofp_port_t slave_id, void *aux) |
daff3353 EJ |
58 | { |
59 | struct slave *slave; | |
60 | ||
61 | slave = slave_lookup(aux, slave_id); | |
62 | return slave ? slave->enabled : false; | |
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); |
bdda5aca BP |
74 | error = bundle_parse_load(actions, &ofpacts); |
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 | |
f25d0cf3 | 83 | if (bundle->n_slaves > MAX_SLAVES) { |
daff3353 EJ |
84 | ovs_fatal(0, "At most %u slaves are supported", MAX_SLAVES); |
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; | |
112 | struct slave_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 EJ |
122 | |
123 | /* Generate 'slaves' array. */ | |
124 | sg.n_slaves = 0; | |
f25d0cf3 | 125 | for (i = 0; i < bundle->n_slaves; i++) { |
4e022ec0 | 126 | ofp_port_t slave_id = bundle->slaves[i]; |
daff3353 EJ |
127 | |
128 | if (slave_lookup(&sg, slave_id)) { | |
129 | ovs_fatal(0, "Redundant slaves are not supported. "); | |
130 | } | |
131 | ||
132 | sg.slaves[sg.n_slaves].slave_id = slave_id; | |
133 | sg.n_slaves++; | |
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 | |
144 | * n_slaves. The initial state is equivalent to all slaves down, so we | |
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; |
daff3353 EJ |
149 | n_permute = 1 << sg.n_slaves; |
150 | for (i = 1; i <= n_permute + 1; i++) { | |
151 | struct slave *slave; | |
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 | ||
159 | /* Gray coding ensures that in each iteration exactly one slave | |
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 | ||
165 | /* Initialize slaves. */ | |
166 | n_enabled = 0; | |
167 | for (j = 0; j < sg.n_slaves; j++) { | |
168 | slave = &sg.slaves[j]; | |
169 | slave->flow_count = 0; | |
170 | slave->enabled = ((1 << j) & mask) != 0; | |
171 | ||
172 | if (slave->enabled) { | |
173 | n_enabled++; | |
174 | } | |
175 | } | |
176 | ||
04a85497 EJ |
177 | active = -1; |
178 | for (j = 0; j < sg.n_slaves; j++) { | |
179 | if (sg.slaves[j].enabled) { | |
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]; | |
4e022ec0 | 188 | ofp_port_t old_slave_id, ofp_port; |
bcd2633a | 189 | struct flow_wildcards wc; |
daff3353 | 190 | |
4e022ec0 | 191 | old_slave_id = u16_to_ofp(flow->regs[0]); |
bcd2633a JP |
192 | ofp_port = bundle_execute(bundle, flow, &wc, slave_enabled_cb, |
193 | &sg); | |
4e022ec0 | 194 | flow->regs[0] = ofp_to_u16(ofp_port); |
daff3353 | 195 | |
f25d0cf3 BP |
196 | if (ofp_port != OFPP_NONE) { |
197 | slave_lookup(&sg, ofp_port)->flow_count++; | |
daff3353 EJ |
198 | } |
199 | ||
f25d0cf3 | 200 | if (old_slave_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 { | |
211 | /* This will happen when 'sg.n_slaves' is 0. */ | |
212 | perfect = 0; | |
213 | } | |
daff3353 EJ |
214 | } |
215 | ||
216 | disruption = changed / (double)N_FLOWS; | |
04a85497 | 217 | printf("%s: disruption=%.2f (perfect=%.2f)", |
daff3353 EJ |
218 | mask_str(mask, sg.n_slaves), disruption, perfect); |
219 | ||
220 | for (j = 0 ; j < sg.n_slaves; j++) { | |
221 | struct slave *slave = &sg.slaves[j]; | |
222 | double flow_percent; | |
223 | ||
224 | flow_percent = slave->flow_count / (double)N_FLOWS; | |
04a85497 | 225 | printf( " %.2f", flow_percent); |
daff3353 EJ |
226 | |
227 | if (slave->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) { | |
237 | fprintf(stderr, "%s: slave %d: flow_percentage=%.5f for" | |
238 | " differs from perfect=%.5f by more than .01\n", | |
239 | mask_str(mask, sg.n_slaves), slave->slave_id, | |
240 | flow_percent, perfect_fp); | |
241 | ok = false; | |
242 | } | |
243 | } else if (slave->flow_count) { | |
244 | fprintf(stderr, "%s: slave %d: disabled slave received" | |
245 | " flows.\n", mask_str(mask, sg.n_slaves), | |
246 | slave->slave_id); | |
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" | |
254 | " more than .01\n", mask_str(mask, sg.n_slaves), | |
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); |