]>
Commit | Line | Data |
---|---|---|
064af421 | 1 | /* |
6468b79c | 2 | * Copyright (c) 2008, 2009, 2010 Nicira Networks. |
064af421 | 3 | * |
a14bc59f BP |
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: | |
064af421 | 7 | * |
a14bc59f BP |
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. | |
064af421 BP |
15 | */ |
16 | ||
17 | #include <config.h> | |
18 | #include "status.h" | |
19 | #include <arpa/inet.h> | |
20 | #include <assert.h> | |
21 | #include <errno.h> | |
3b250e29 | 22 | #include <inttypes.h> |
064af421 BP |
23 | #include <stdlib.h> |
24 | #include <unistd.h> | |
25 | #include "dynamic-string.h" | |
26 | #include "list.h" | |
27 | #include "ofpbuf.h" | |
28 | #include "ofproto.h" | |
29 | #include "openflow/nicira-ext.h" | |
ae8871ec | 30 | #include "packets.h" |
064af421 BP |
31 | #include "rconn.h" |
32 | #include "svec.h" | |
33 | #include "timeval.h" | |
34 | #include "vconn.h" | |
35 | ||
36 | #define THIS_MODULE VLM_status | |
37 | #include "vlog.h" | |
38 | ||
39 | struct status_category { | |
40 | struct list node; | |
41 | char *name; | |
42 | void (*cb)(struct status_reply *, void *aux); | |
43 | void *aux; | |
44 | }; | |
45 | ||
46 | struct switch_status { | |
47 | time_t booted; | |
48 | struct status_category *config_cat; | |
49 | struct status_category *switch_cat; | |
50 | struct list categories; | |
51 | }; | |
52 | ||
53 | struct status_reply { | |
54 | struct status_category *category; | |
55 | struct ds request; | |
56 | struct ds output; | |
57 | }; | |
58 | ||
59 | int | |
60 | switch_status_handle_request(struct switch_status *ss, struct rconn *rconn, | |
61 | struct nicira_header *request) | |
62 | { | |
63 | struct status_category *c; | |
64 | struct nicira_header *reply; | |
65 | struct status_reply sr; | |
66 | struct ofpbuf *b; | |
67 | int retval; | |
68 | ||
69 | sr.request.string = (void *) (request + 1); | |
70 | sr.request.length = ntohs(request->header.length) - sizeof *request; | |
71 | ds_init(&sr.output); | |
72 | LIST_FOR_EACH (c, struct status_category, node, &ss->categories) { | |
73 | if (!memcmp(c->name, sr.request.string, | |
74 | MIN(strlen(c->name), sr.request.length))) { | |
75 | sr.category = c; | |
76 | c->cb(&sr, c->aux); | |
77 | } | |
78 | } | |
79 | reply = make_openflow_xid(sizeof *reply + sr.output.length, | |
80 | OFPT_VENDOR, request->header.xid, &b); | |
81 | reply->vendor = htonl(NX_VENDOR_ID); | |
82 | reply->subtype = htonl(NXT_STATUS_REPLY); | |
83 | memcpy(reply + 1, sr.output.string, sr.output.length); | |
84 | retval = rconn_send(rconn, b, NULL); | |
85 | if (retval && retval != EAGAIN) { | |
86 | VLOG_WARN("send failed (%s)", strerror(retval)); | |
87 | } | |
88 | ds_destroy(&sr.output); | |
89 | return 0; | |
90 | } | |
91 | ||
92 | void | |
93 | rconn_status_cb(struct status_reply *sr, void *rconn_) | |
94 | { | |
95 | struct rconn *rconn = rconn_; | |
96 | time_t now = time_now(); | |
ae8871ec JP |
97 | uint32_t remote_ip = rconn_get_remote_ip(rconn); |
98 | uint32_t local_ip = rconn_get_local_ip(rconn); | |
064af421 BP |
99 | |
100 | status_reply_put(sr, "name=%s", rconn_get_name(rconn)); | |
ae8871ec JP |
101 | if (remote_ip) { |
102 | status_reply_put(sr, "remote-ip="IP_FMT, IP_ARGS(&remote_ip)); | |
103 | status_reply_put(sr, "remote-port=%d", | |
104 | ntohs(rconn_get_remote_port(rconn))); | |
105 | status_reply_put(sr, "local-ip="IP_FMT, IP_ARGS(&local_ip)); | |
106 | status_reply_put(sr, "local-port=%d", | |
107 | ntohs(rconn_get_local_port(rconn))); | |
108 | } | |
064af421 BP |
109 | status_reply_put(sr, "state=%s", rconn_get_state(rconn)); |
110 | status_reply_put(sr, "backoff=%d", rconn_get_backoff(rconn)); | |
952efc48 | 111 | status_reply_put(sr, "probe-interval=%d", rconn_get_probe_interval(rconn)); |
064af421 BP |
112 | status_reply_put(sr, "is-connected=%s", |
113 | rconn_is_connected(rconn) ? "true" : "false"); | |
114 | status_reply_put(sr, "sent-msgs=%u", rconn_packets_sent(rconn)); | |
115 | status_reply_put(sr, "received-msgs=%u", rconn_packets_received(rconn)); | |
116 | status_reply_put(sr, "attempted-connections=%u", | |
117 | rconn_get_attempted_connections(rconn)); | |
118 | status_reply_put(sr, "successful-connections=%u", | |
119 | rconn_get_successful_connections(rconn)); | |
120 | status_reply_put(sr, "last-connection=%ld", | |
121 | (long int) (now - rconn_get_last_connection(rconn))); | |
7df824b7 BP |
122 | status_reply_put(sr, "last-received=%ld", |
123 | (long int) (now - rconn_get_last_received(rconn))); | |
064af421 BP |
124 | status_reply_put(sr, "time-connected=%lu", |
125 | rconn_get_total_time_connected(rconn)); | |
126 | status_reply_put(sr, "state-elapsed=%u", rconn_get_state_elapsed(rconn)); | |
127 | } | |
128 | ||
129 | static void | |
130 | config_status_cb(struct status_reply *sr, void *ofproto_) | |
131 | { | |
132 | const struct ofproto *ofproto = ofproto_; | |
6468b79c | 133 | uint64_t datapath_id; |
064af421 BP |
134 | struct svec listeners; |
135 | int probe_interval, max_backoff; | |
136 | size_t i; | |
137 | ||
3b250e29 JP |
138 | datapath_id = ofproto_get_datapath_id(ofproto); |
139 | if (datapath_id) { | |
b123cc3c | 140 | status_reply_put(sr, "datapath-id=%016"PRIx64, datapath_id); |
3b250e29 JP |
141 | } |
142 | ||
064af421 BP |
143 | svec_init(&listeners); |
144 | ofproto_get_listeners(ofproto, &listeners); | |
145 | for (i = 0; i < listeners.n; i++) { | |
146 | status_reply_put(sr, "management%zu=%s", i, listeners.names[i]); | |
147 | } | |
148 | svec_destroy(&listeners); | |
149 | ||
150 | probe_interval = ofproto_get_probe_interval(ofproto); | |
151 | if (probe_interval) { | |
152 | status_reply_put(sr, "probe-interval=%d", probe_interval); | |
153 | } | |
154 | ||
155 | max_backoff = ofproto_get_max_backoff(ofproto); | |
156 | if (max_backoff) { | |
157 | status_reply_put(sr, "max-backoff=%d", max_backoff); | |
158 | } | |
159 | } | |
160 | ||
161 | static void | |
162 | switch_status_cb(struct status_reply *sr, void *ss_) | |
163 | { | |
164 | struct switch_status *ss = ss_; | |
165 | time_t now = time_now(); | |
166 | ||
167 | status_reply_put(sr, "now=%ld", (long int) now); | |
168 | status_reply_put(sr, "uptime=%ld", (long int) (now - ss->booted)); | |
169 | status_reply_put(sr, "pid=%ld", (long int) getpid()); | |
170 | } | |
171 | ||
172 | struct switch_status * | |
173 | switch_status_create(const struct ofproto *ofproto) | |
174 | { | |
ec6fde61 | 175 | struct switch_status *ss = xzalloc(sizeof *ss); |
064af421 BP |
176 | ss->booted = time_now(); |
177 | list_init(&ss->categories); | |
178 | ss->config_cat = switch_status_register(ss, "config", config_status_cb, | |
179 | (void *) ofproto); | |
180 | ss->switch_cat = switch_status_register(ss, "switch", switch_status_cb, | |
181 | ss); | |
182 | return ss; | |
183 | } | |
184 | ||
185 | void | |
186 | switch_status_destroy(struct switch_status *ss) | |
187 | { | |
188 | if (ss) { | |
189 | /* Orphan any remaining categories, so that unregistering them later | |
190 | * won't write to bad memory. */ | |
191 | struct status_category *c, *next; | |
192 | LIST_FOR_EACH_SAFE (c, next, | |
193 | struct status_category, node, &ss->categories) { | |
194 | list_init(&c->node); | |
195 | } | |
196 | switch_status_unregister(ss->config_cat); | |
197 | switch_status_unregister(ss->switch_cat); | |
198 | free(ss); | |
199 | } | |
200 | } | |
201 | ||
202 | struct status_category * | |
203 | switch_status_register(struct switch_status *ss, | |
204 | const char *category, | |
205 | status_cb_func *cb, void *aux) | |
206 | { | |
207 | struct status_category *c = xmalloc(sizeof *c); | |
208 | c->cb = cb; | |
209 | c->aux = aux; | |
210 | c->name = xstrdup(category); | |
211 | list_push_back(&ss->categories, &c->node); | |
212 | return c; | |
213 | } | |
214 | ||
215 | void | |
216 | switch_status_unregister(struct status_category *c) | |
217 | { | |
218 | if (c) { | |
219 | if (!list_is_empty(&c->node)) { | |
220 | list_remove(&c->node); | |
221 | } | |
222 | free(c->name); | |
223 | free(c); | |
224 | } | |
225 | } | |
226 | ||
227 | void | |
228 | status_reply_put(struct status_reply *sr, const char *content, ...) | |
229 | { | |
230 | size_t old_length = sr->output.length; | |
231 | size_t added; | |
232 | va_list args; | |
233 | ||
234 | /* Append the status reply to the output. */ | |
235 | ds_put_format(&sr->output, "%s.", sr->category->name); | |
236 | va_start(args, content); | |
237 | ds_put_format_valist(&sr->output, content, args); | |
238 | va_end(args); | |
239 | if (ds_last(&sr->output) != '\n') { | |
240 | ds_put_char(&sr->output, '\n'); | |
241 | } | |
242 | ||
243 | /* Drop what we just added if it doesn't match the request. */ | |
244 | added = sr->output.length - old_length; | |
245 | if (added < sr->request.length | |
246 | || memcmp(&sr->output.string[old_length], | |
247 | sr->request.string, sr->request.length)) { | |
248 | ds_truncate(&sr->output, old_length); | |
249 | } | |
250 | } |