a55c57c3ff7409fd68ff015bd651a959e7a67215
[cascardo/ovs.git] / tests / test-reconnect.c
1 /*
2  * Copyright (c) 2009, 2010, 2011, 2012, 2014 Nicira, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #undef NDEBUG
19 #include "reconnect.h"
20 #include <errno.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "command-line.h"
25 #include "compiler.h"
26 #include "ovstest.h"
27 #include "svec.h"
28 #include "util.h"
29 #include "openvswitch/vlog.h"
30
31 static struct reconnect *reconnect;
32 static int now;
33
34 static void diff_stats(const struct reconnect_stats *old,
35                        const struct reconnect_stats *new,
36                        int delta);
37 static const struct ovs_cmdl_command *get_all_commands(void);
38
39 static void
40 test_reconnect_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
41 {
42     extern struct vlog_module VLM_reconnect;
43     struct reconnect_stats prev;
44     unsigned int old_max_tries;
45     int old_time;
46     char line[128];
47
48     vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_OFF);
49
50     now = 1000;
51     reconnect = reconnect_create(now);
52     reconnect_set_name(reconnect, "remote");
53     reconnect_get_stats(reconnect, now, &prev);
54     printf("### t=%d ###\n", now);
55     old_time = now;
56     old_max_tries = reconnect_get_max_tries(reconnect);
57     while (fgets(line, sizeof line, stdin)) {
58         struct reconnect_stats cur;
59         struct svec args;
60
61         fputs(line, stdout);
62         if (line[0] == '#') {
63             continue;
64         }
65
66         svec_init(&args);
67         svec_parse_words(&args, line);
68         svec_terminate(&args);
69         if (!svec_is_empty(&args)) {
70             struct ovs_cmdl_context ctx = {
71                 .argc = args.n,
72                 .argv = args.names,
73             };
74             ovs_cmdl_run_command(&ctx, get_all_commands());
75         }
76         svec_destroy(&args);
77
78         if (old_time != now) {
79             printf("\n### t=%d ###\n", now);
80         }
81
82         reconnect_get_stats(reconnect, now, &cur);
83         diff_stats(&prev, &cur, now - old_time);
84         prev = cur;
85         if (reconnect_get_max_tries(reconnect) != old_max_tries) {
86             old_max_tries = reconnect_get_max_tries(reconnect);
87             printf("  %u tries left\n", old_max_tries);
88         }
89
90         old_time = now;
91     }
92 }
93
94 static void
95 do_enable(struct ovs_cmdl_context *ctx OVS_UNUSED)
96 {
97     reconnect_enable(reconnect, now);
98 }
99
100 static void
101 do_disable(struct ovs_cmdl_context *ctx OVS_UNUSED)
102 {
103     reconnect_disable(reconnect, now);
104 }
105
106 static void
107 do_force_reconnect(struct ovs_cmdl_context *ctx OVS_UNUSED)
108 {
109     reconnect_force_reconnect(reconnect, now);
110 }
111
112 static int
113 error_from_string(const char *s)
114 {
115     if (!s) {
116         return 0;
117     } else if (!strcmp(s, "ECONNREFUSED")) {
118         return ECONNREFUSED;
119     } else if (!strcmp(s, "EOF")) {
120         return EOF;
121     } else {
122         ovs_fatal(0, "unknown error '%s'", s);
123     }
124 }
125
126 static void
127 do_disconnected(struct ovs_cmdl_context *ctx)
128 {
129     reconnect_disconnected(reconnect, now, error_from_string(ctx->argv[1]));
130 }
131
132 static void
133 do_connecting(struct ovs_cmdl_context *ctx OVS_UNUSED)
134 {
135     reconnect_connecting(reconnect, now);
136 }
137
138 static void
139 do_connect_failed(struct ovs_cmdl_context *ctx)
140 {
141     reconnect_connect_failed(reconnect, now, error_from_string(ctx->argv[1]));
142 }
143
144 static void
145 do_connected(struct ovs_cmdl_context *ctx OVS_UNUSED)
146 {
147     reconnect_connected(reconnect, now);
148 }
149
150 static void
151 do_activity(struct ovs_cmdl_context *ctx OVS_UNUSED)
152 {
153     reconnect_activity(reconnect, now);
154 }
155
156 static void
157 do_run(struct ovs_cmdl_context *ctx)
158 {
159     enum reconnect_action action;
160
161     if (ctx->argc > 1) {
162         now += atoi(ctx->argv[1]);
163     }
164
165     action = reconnect_run(reconnect, now);
166     switch (action) {
167     default:
168         if (action != 0) {
169             OVS_NOT_REACHED();
170         }
171         break;
172
173     case RECONNECT_CONNECT:
174         printf("  should connect\n");
175         break;
176
177     case RECONNECT_DISCONNECT:
178         printf("  should disconnect\n");
179         break;
180
181     case RECONNECT_PROBE:
182         printf("  should send probe\n");
183         break;
184     }
185 }
186
187 static void
188 do_advance(struct ovs_cmdl_context *ctx)
189 {
190     now += atoi(ctx->argv[1]);
191 }
192
193 static void
194 do_timeout(struct ovs_cmdl_context *ctx OVS_UNUSED)
195 {
196     int timeout = reconnect_timeout(reconnect, now);
197     if (timeout >= 0) {
198         printf("  advance %d ms\n", timeout);
199         now += timeout;
200     } else {
201         printf("  no timeout\n");
202     }
203 }
204
205 static void
206 do_set_max_tries(struct ovs_cmdl_context *ctx)
207 {
208     reconnect_set_max_tries(reconnect, atoi(ctx->argv[1]));
209 }
210
211 static void
212 diff_stats(const struct reconnect_stats *old,
213            const struct reconnect_stats *new,
214            int delta)
215 {
216     if (old->state != new->state
217         || old->state_elapsed != new->state_elapsed
218         || old->backoff != new->backoff) {
219         printf("  in %s for %u ms (%d ms backoff)\n",
220                new->state, new->state_elapsed, new->backoff);
221     }
222     if (old->creation_time != new->creation_time
223         || old->last_activity != new->last_activity
224         || old->last_connected != new->last_connected) {
225         printf("  created %lld, last activity %lld, last connected %lld\n",
226                new->creation_time, new->last_activity, new->last_connected);
227     }
228     if (old->n_successful_connections != new->n_successful_connections
229         || old->n_attempted_connections != new->n_attempted_connections
230         || old->seqno != new->seqno) {
231         printf("  %u successful connections out of %u attempts, seqno %u\n",
232                new->n_successful_connections, new->n_attempted_connections,
233                new->seqno);
234     }
235     if (old->is_connected != new->is_connected) {
236         printf("  %sconnected\n", new->is_connected ? "" : "dis");
237     }
238     if (old->last_connected != new->last_connected
239         || (old->msec_since_connect != new->msec_since_connect - delta
240             && !(old->msec_since_connect == UINT_MAX
241                  && new->msec_since_connect == UINT_MAX))
242         || (old->total_connected_duration != new->total_connected_duration - delta
243             && !(old->total_connected_duration == 0
244                  && new->total_connected_duration == 0))) {
245         printf("  last connected %u ms ago, connected %u ms total\n",
246                new->msec_since_connect, new->total_connected_duration);
247     }
248     if (old->last_disconnected != new->last_disconnected
249         || (old->msec_since_disconnect != new->msec_since_disconnect - delta
250             && !(old->msec_since_disconnect == UINT_MAX
251                  && new->msec_since_disconnect == UINT_MAX))) {
252         printf("  disconnected at %llu ms (%u ms ago)\n",
253                new->last_disconnected, new->msec_since_disconnect);
254     }
255 }
256
257 static void
258 do_set_passive(struct ovs_cmdl_context *ctx OVS_UNUSED)
259 {
260     reconnect_set_passive(reconnect, true, now);
261 }
262
263 static void
264 do_listening(struct ovs_cmdl_context *ctx OVS_UNUSED)
265 {
266     reconnect_listening(reconnect, now);
267 }
268
269 static void
270 do_listen_error(struct ovs_cmdl_context *ctx)
271 {
272     reconnect_listen_error(reconnect, now, atoi(ctx->argv[1]));
273 }
274
275 static const struct ovs_cmdl_command all_commands[] = {
276     { "enable", NULL, 0, 0, do_enable },
277     { "disable", NULL, 0, 0, do_disable },
278     { "force-reconnect", NULL, 0, 0, do_force_reconnect },
279     { "disconnected", NULL, 0, 1, do_disconnected },
280     { "connecting", NULL, 0, 0, do_connecting },
281     { "connect-failed", NULL, 0, 1, do_connect_failed },
282     { "connected", NULL, 0, 0, do_connected },
283     { "activity", NULL, 0, 0, do_activity },
284     { "run", NULL, 0, 1, do_run },
285     { "advance", NULL, 1, 1, do_advance },
286     { "timeout", NULL, 0, 0, do_timeout },
287     { "set-max-tries", NULL, 1, 1, do_set_max_tries },
288     { "passive", NULL, 0, 0, do_set_passive },
289     { "listening", NULL, 0, 0, do_listening },
290     { "listen-error", NULL, 1, 1, do_listen_error },
291     { NULL, NULL, 0, 0, NULL },
292 };
293
294 static const struct ovs_cmdl_command *
295 get_all_commands(void)
296 {
297     return all_commands;
298 }
299
300 OVSTEST_REGISTER("test-reconnect", test_reconnect_main);