hast_snmp.c revision 260412
1/*-
2 * Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/10/usr.sbin/bsnmpd/modules/snmp_hast/hast_snmp.c 260412 2014-01-07 21:23:41Z trociny $");
29
30#include <sys/param.h>
31#include <sys/queue.h>
32
33#include <bsnmp/snmpmod.h>
34
35#include <string.h>
36
37#include "hast.h"
38#include "hast_oid.h"
39#include "hast_proto.h"
40#include "hast_tree.h"
41#include "nv.h"
42#include "pjdlog.h"
43#include "proto.h"
44
45#define UPDATE_INTERVAL	500	/* update interval in ticks */
46
47static struct lmodule *module;
48
49static const struct asn_oid oid_hast = OIDX_begemotHast;
50
51/* the Object Resource registration index */
52static u_int hast_index = 0;
53
54/*
55 * Structure that describes single resource.
56 */
57struct hast_snmp_resource {
58	TAILQ_ENTRY(hast_snmp_resource) link;
59	int32_t		index;
60	char		name[NAME_MAX];
61	int		error;
62	int		role;
63	char		provname[NAME_MAX];
64	char		localpath[PATH_MAX];
65	int32_t		extentsize;
66	int32_t		keepdirty;
67	char		remoteaddr[HAST_ADDRSIZE];
68	char		sourceaddr[HAST_ADDRSIZE];
69	int		replication;
70	int		status;
71	uint64_t	dirty;
72	uint64_t	reads;
73	uint64_t	writes;
74	uint64_t	deletes;
75	uint64_t	flushes;
76	uint64_t	activemap_updates;
77	uint64_t	read_errors;
78	uint64_t	write_errors;
79	uint64_t	delete_errors;
80	uint64_t	flush_errors;
81	pid_t		workerpid;
82	uint32_t	local_queue;
83	uint32_t	send_queue;
84	uint32_t	recv_queue;
85	uint32_t	done_queue;
86	uint32_t	idle_queue;
87};
88
89static TAILQ_HEAD(, hast_snmp_resource) resources =
90    TAILQ_HEAD_INITIALIZER(resources);
91
92/* Path to configuration file. */
93static u_char *cfgpath;
94/* Ticks of the last hast resources update. */
95static uint64_t last_resources_update;
96
97static void free_resources(void);
98static int hastctl(struct nv *nvin, struct nv **nvout);
99static int hast_fini(void);
100static int hast_init(struct lmodule *mod, int argc, char *argv[]);
101static void hast_start(void);
102static int set_role(const char *resource, int role);
103static int str2role(const char *str);
104static int str2replication(const char *str);
105static int str2status(const char *str);
106static int update_resources(void);
107
108const struct snmp_module config = {
109    .comment   = "This module implements the BEGEMOT MIB for HAST.",
110    .init      = hast_init,
111    .start     = hast_start,
112    .fini      = hast_fini,
113    .tree      = hast_ctree,
114    .tree_size = hast_CTREE_SIZE,
115};
116
117static int
118hast_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
119{
120
121	module = mod;
122
123	pjdlog_init(PJDLOG_MODE_SYSLOG);
124	pjdlog_debug_set(0);
125
126	cfgpath = malloc(sizeof(HAST_CONFIG));
127	if (cfgpath == NULL) {
128		pjdlog_error("Unable to allocate %zu bytes for cfgpath",
129		    sizeof(HAST_CONFIG));
130		return (-1);
131	}
132	strcpy(cfgpath, HAST_CONFIG);
133	return(0);
134}
135
136static void
137hast_start(void)
138{
139	hast_index = or_register(&oid_hast,
140	    "The MIB module for BEGEMOT-HAST-MIB.", module);
141}
142
143static int
144hast_fini(void)
145{
146
147	or_unregister(hast_index);
148	free_resources();
149	free(cfgpath);
150	return (0);
151}
152
153static void
154free_resources(void)
155{
156	struct hast_snmp_resource *res;
157
158	while ((res = TAILQ_FIRST(&resources)) != NULL) {
159		TAILQ_REMOVE(&resources, res, link);
160		free(res);
161	}
162}
163
164static int
165str2role(const char *str)
166{
167
168	if (strcmp(str, "init") == 0)
169		return (HAST_ROLE_INIT);
170	if (strcmp(str, "primary") == 0)
171		return (HAST_ROLE_PRIMARY);
172	if (strcmp(str, "secondary") == 0)
173		return (HAST_ROLE_SECONDARY);
174	return (HAST_ROLE_UNDEF);
175}
176
177static int
178str2replication(const char *str)
179{
180
181	if (strcmp(str, "fullsync") == 0)
182		return (HAST_REPLICATION_FULLSYNC);
183	if (strcmp(str, "memsync") == 0)
184		return (HAST_REPLICATION_MEMSYNC);
185	if (strcmp(str, "async") == 0)
186		return (HAST_REPLICATION_ASYNC);
187	return (-1);
188}
189
190static int
191str2status(const char *str)
192{
193
194	if (strcmp(str, "complete") == 0)
195		return (0);
196	if (strcmp(str, "degraded") == 0)
197		return (1);
198	return (-1);
199}
200
201static int
202hastctl(struct nv *nvin, struct nv **nvout)
203{
204	struct hastd_config *cfg;
205	struct proto_conn *conn;
206	struct nv *nv;
207	int error;
208
209	cfg = yy_config_parse(cfgpath, true);
210	if (cfg == NULL)
211		return (-1);
212
213	/* Setup control connection... */
214	if (proto_client(NULL, cfg->hc_controladdr, &conn) == -1) {
215		pjdlog_error("Unable to setup control connection to %s",
216		    cfg->hc_controladdr);
217		return (-1);
218	}
219	/* ...and connect to hastd. */
220	if (proto_connect(conn, HAST_TIMEOUT) == -1) {
221		pjdlog_error("Unable to connect to hastd via %s",
222		    cfg->hc_controladdr);
223		proto_close(conn);
224		return (-1);
225	}
226	/* Send the command to the server... */
227	if (hast_proto_send(NULL, conn, nvin, NULL, 0) == -1) {
228		pjdlog_error("Unable to send command to hastd via %s",
229		    cfg->hc_controladdr);
230		proto_close(conn);
231		return (-1);
232	}
233	/* ...and receive reply. */
234	if (hast_proto_recv_hdr(conn, &nv) == -1) {
235		pjdlog_error("cannot receive reply from hastd via %s",
236		    cfg->hc_controladdr);
237		proto_close(conn);
238		return (-1);
239	}
240	proto_close(conn);
241	error = nv_get_int16(nv, "error");
242	if (error != 0) {
243		pjdlog_error("Error %d received from hastd.", error);
244		nv_free(nv);
245		return (-1);
246	}
247	nv_set_error(nv, 0);
248	*nvout = nv;
249	return (0);
250}
251
252static int
253set_role(const char *resource, int role)
254{
255	struct nv *nvin, *nvout;
256	int error;
257
258	nvin = nv_alloc();
259	nv_add_string(nvin, resource, "resource%d", 0);
260	nv_add_uint8(nvin, HASTCTL_CMD_SETROLE, "cmd");
261	nv_add_uint8(nvin, role, "role");
262	error = hastctl(nvin, &nvout);
263	nv_free(nvin);
264	if (error != 0)
265		return (-1);
266	nv_free(nvout);
267	return (SNMP_ERR_NOERROR);
268}
269
270static int
271update_resources(void)
272{
273	struct hast_snmp_resource *res;
274	struct nv *nvin, *nvout;
275	static uint64_t now;
276	unsigned int i;
277	const char *str;
278	int error;
279
280	now = get_ticks();
281	if (now - last_resources_update < UPDATE_INTERVAL)
282		return (0);
283
284	last_resources_update = now;
285
286	free_resources();
287
288	nvin = nv_alloc();
289	nv_add_uint8(nvin, HASTCTL_CMD_STATUS, "cmd");
290	nv_add_string(nvin, "all", "resource%d", 0);
291	error = hastctl(nvin, &nvout);
292	nv_free(nvin);
293	if (error != 0)
294		return (-1);
295
296	for (i = 0; ; i++) {
297		str = nv_get_string(nvout, "resource%u", i);
298		if (str == NULL)
299			break;
300		res = calloc(1, sizeof(*res));
301		if (res == NULL) {
302			pjdlog_error("Unable to allocate %zu bytes for "
303			    "resource", sizeof(*res));
304			return (-1);
305		}
306		res->index = i + 1;
307		strncpy(res->name, str, sizeof(res->name) - 1);
308		error = nv_get_int16(nvout, "error%u", i);
309		if (error != 0)
310			continue;
311		str = nv_get_string(nvout, "role%u", i);
312		res->role = str != NULL ? str2role(str) : HAST_ROLE_UNDEF;
313		str = nv_get_string(nvout, "provname%u", i);
314		if (str != NULL)
315			strncpy(res->provname, str, sizeof(res->provname) - 1);
316		str = nv_get_string(nvout, "localpath%u", i);
317		if (str != NULL) {
318			strncpy(res->localpath, str,
319			    sizeof(res->localpath) - 1);
320		}
321		res->extentsize = nv_get_uint32(nvout, "extentsize%u", i);
322		res->keepdirty = nv_get_uint32(nvout, "keepdirty%u", i);
323		str = nv_get_string(nvout, "remoteaddr%u", i);
324		if (str != NULL) {
325			strncpy(res->remoteaddr, str,
326			    sizeof(res->remoteaddr) - 1);
327		}
328		str = nv_get_string(nvout, "sourceaddr%u", i);
329		if (str != NULL) {
330			strncpy(res->sourceaddr, str,
331			    sizeof(res->sourceaddr) - 1);
332		}
333		str = nv_get_string(nvout, "replication%u", i);
334		res->replication = str != NULL ? str2replication(str) : -1;
335		str = nv_get_string(nvout, "status%u", i);
336		res->status = str != NULL ? str2status(str) : -1;
337		res->dirty = nv_get_uint64(nvout, "dirty%u", i);
338		res->reads = nv_get_uint64(nvout, "stat_read%u", i);
339		res->writes = nv_get_uint64(nvout, "stat_write%u", i);
340		res->deletes = nv_get_uint64(nvout, "stat_delete%u", i);
341		res->flushes = nv_get_uint64(nvout, "stat_flush%u", i);
342		res->activemap_updates =
343		    nv_get_uint64(nvout, "stat_activemap_update%u", i);
344		res->read_errors =
345		    nv_get_uint64(nvout, "stat_read_error%u", i);
346		res->write_errors =
347		    nv_get_uint64(nvout, "stat_write_error%u", i);
348		res->delete_errors =
349		    nv_get_uint64(nvout, "stat_delete_error%u", i);
350		res->flush_errors =
351		    nv_get_uint64(nvout, "stat_flush_error%u", i);
352		res->workerpid = nv_get_int32(nvout, "workerpid%u", i);
353		res->local_queue =
354		    nv_get_uint64(nvout, "local_queue_size%u", i);
355		res->send_queue =
356		    nv_get_uint64(nvout, "send_queue_size%u", i);
357		res->recv_queue =
358		    nv_get_uint64(nvout, "recv_queue_size%u", i);
359		res->done_queue =
360		    nv_get_uint64(nvout, "done_queue_size%u", i);
361		res->idle_queue =
362		    nv_get_uint64(nvout, "idle_queue_size%u", i);
363		TAILQ_INSERT_TAIL(&resources, res, link);
364	}
365	nv_free(nvout);
366	return (0);
367}
368
369int
370op_hastConfig(struct snmp_context *context, struct snmp_value *value,
371    u_int sub, u_int iidx __unused, enum snmp_op op)
372{
373	asn_subid_t which;
374
375	which = value->var.subs[sub - 1];
376
377	switch (op) {
378	case SNMP_OP_GET:
379		switch (which) {
380		case LEAF_hastConfigFile:
381			return (string_get(value, cfgpath, -1));
382		default:
383			return (SNMP_ERR_RES_UNAVAIL);
384		}
385	case SNMP_OP_SET:
386		switch (which) {
387		case LEAF_hastConfigFile:
388			return (string_save(value, context, -1,
389			    (u_char **)&cfgpath));
390		default:
391			return (SNMP_ERR_RES_UNAVAIL);
392		}
393	case SNMP_OP_GETNEXT:
394	case SNMP_OP_ROLLBACK:
395	case SNMP_OP_COMMIT:
396		return (SNMP_ERR_NOERROR);
397	default:
398		return (SNMP_ERR_RES_UNAVAIL);
399	}
400}
401
402int
403op_hastResourceTable(struct snmp_context *context __unused,
404    struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op)
405{
406	struct hast_snmp_resource *res;
407	asn_subid_t which;
408	int ret;
409
410	if (update_resources() == -1)
411		return (SNMP_ERR_RES_UNAVAIL);
412
413	which = value->var.subs[sub - 1];
414
415	switch (op) {
416	case SNMP_OP_GETNEXT:
417		res = NEXT_OBJECT_INT(&resources, &value->var, sub);
418		if (res == NULL)
419			return (SNMP_ERR_NOSUCHNAME);
420		value->var.len = sub + 1;
421		value->var.subs[sub] = res->index;
422		break;
423	case SNMP_OP_GET:
424		if (value->var.len - sub != 1)
425			return (SNMP_ERR_NOSUCHNAME);
426		res = FIND_OBJECT_INT(&resources, &value->var, sub);
427		if (res == NULL)
428			return (SNMP_ERR_NOSUCHNAME);
429		break;
430	case SNMP_OP_SET:
431		res = FIND_OBJECT_INT(&resources, &value->var, sub);
432		if (res == NULL)
433			return (SNMP_ERR_NOSUCHNAME);
434		switch (which) {
435		case LEAF_hastResourceRole:
436			ret = set_role(res->name, value->v.integer);
437			/* force update on next run */
438			last_resources_update = 0;
439			break;
440		default:
441			ret = SNMP_ERR_NOT_WRITEABLE;
442			break;
443		}
444		return ret;
445	case SNMP_OP_ROLLBACK:
446	case SNMP_OP_COMMIT:
447		return (SNMP_ERR_NOERROR);
448	default:
449		return (SNMP_ERR_RES_UNAVAIL);
450	}
451
452	ret = SNMP_ERR_NOERROR;
453
454	switch (which) {
455	case LEAF_hastResourceIndex:
456		value->v.integer = res->index;
457		break;
458	case LEAF_hastResourceName:
459		ret = string_get(value, res->name, -1);
460		break;
461	case LEAF_hastResourceRole:
462		value->v.integer = res->role;
463		break;
464	case LEAF_hastResourceProvName:
465		ret = string_get(value, res->provname, -1);
466		break;
467	case LEAF_hastResourceLocalPath:
468		ret = string_get(value, res->localpath, -1);
469		break;
470	case LEAF_hastResourceExtentSize:
471		value->v.integer = res->extentsize;
472		break;
473	case LEAF_hastResourceKeepDirty:
474		value->v.integer = res->keepdirty;
475		break;
476	case LEAF_hastResourceRemoteAddr:
477		ret = string_get(value, res->remoteaddr, -1);
478		break;
479	case LEAF_hastResourceSourceAddr:
480		ret = string_get(value, res->sourceaddr, -1);
481		break;
482	case LEAF_hastResourceReplication:
483		value->v.integer = res->replication;
484		break;
485	case LEAF_hastResourceStatus:
486		value->v.integer = res->status;
487		break;
488	case LEAF_hastResourceDirty:
489		value->v.counter64 = res->dirty;
490		break;
491	case LEAF_hastResourceReads:
492		value->v.counter64 = res->reads;
493		break;
494	case LEAF_hastResourceWrites:
495		value->v.counter64 = res->writes;
496		break;
497	case LEAF_hastResourceDeletes:
498		value->v.counter64 = res->deletes;
499		break;
500	case LEAF_hastResourceFlushes:
501		value->v.counter64 = res->flushes;
502		break;
503	case LEAF_hastResourceActivemapUpdates:
504		value->v.counter64 = res->activemap_updates;
505		break;
506	case LEAF_hastResourceReadErrors:
507		value->v.counter64 = res->read_errors;
508		break;
509	case LEAF_hastResourceWriteErrors:
510		value->v.counter64 = res->write_errors;
511		break;
512	case LEAF_hastResourceDeleteErrors:
513		value->v.counter64 = res->delete_errors;
514		break;
515	case LEAF_hastResourceFlushErrors:
516		value->v.counter64 = res->flush_errors;
517		break;
518	case LEAF_hastResourceWorkerPid:
519		value->v.integer = res->workerpid;
520		break;
521	case LEAF_hastResourceLocalQueue:
522		value->v.uint32 = res->local_queue;
523		break;
524	case LEAF_hastResourceSendQueue:
525		value->v.uint32 = res->send_queue;
526		break;
527	case LEAF_hastResourceRecvQueue:
528		value->v.uint32 = res->recv_queue;
529		break;
530	case LEAF_hastResourceDoneQueue:
531		value->v.uint32 = res->done_queue;
532		break;
533	case LEAF_hastResourceIdleQueue:
534		value->v.uint32 = res->idle_queue;
535		break;
536	default:
537		ret = SNMP_ERR_RES_UNAVAIL;
538		break;
539	}
540	return (ret);
541}
542