bcfa7a8677
MFC after: 1 week
542 lines
14 KiB
C
542 lines
14 KiB
C
/*-
|
|
* Copyright (c) 2013 Mikolaj Golub <trociny@FreeBSD.org>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <bsnmp/snmpmod.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "hast.h"
|
|
#include "hast_oid.h"
|
|
#include "hast_proto.h"
|
|
#include "hast_tree.h"
|
|
#include "nv.h"
|
|
#include "pjdlog.h"
|
|
#include "proto.h"
|
|
|
|
#define UPDATE_INTERVAL 500 /* update interval in ticks */
|
|
|
|
static struct lmodule *module;
|
|
|
|
static const struct asn_oid oid_hast = OIDX_begemotHast;
|
|
|
|
/* the Object Resource registration index */
|
|
static u_int hast_index = 0;
|
|
|
|
/*
|
|
* Structure that describes single resource.
|
|
*/
|
|
struct hast_snmp_resource {
|
|
TAILQ_ENTRY(hast_snmp_resource) link;
|
|
int32_t index;
|
|
char name[NAME_MAX];
|
|
int error;
|
|
int role;
|
|
char provname[NAME_MAX];
|
|
char localpath[PATH_MAX];
|
|
int32_t extentsize;
|
|
int32_t keepdirty;
|
|
char remoteaddr[HAST_ADDRSIZE];
|
|
char sourceaddr[HAST_ADDRSIZE];
|
|
int replication;
|
|
int status;
|
|
uint64_t dirty;
|
|
uint64_t reads;
|
|
uint64_t writes;
|
|
uint64_t deletes;
|
|
uint64_t flushes;
|
|
uint64_t activemap_updates;
|
|
uint64_t read_errors;
|
|
uint64_t write_errors;
|
|
uint64_t delete_errors;
|
|
uint64_t flush_errors;
|
|
pid_t workerpid;
|
|
uint32_t local_queue;
|
|
uint32_t send_queue;
|
|
uint32_t recv_queue;
|
|
uint32_t done_queue;
|
|
uint32_t idle_queue;
|
|
};
|
|
|
|
static TAILQ_HEAD(, hast_snmp_resource) resources =
|
|
TAILQ_HEAD_INITIALIZER(resources);
|
|
|
|
/* Path to configuration file. */
|
|
static u_char *cfgpath;
|
|
/* Ticks of the last hast resources update. */
|
|
static uint64_t last_resources_update;
|
|
|
|
static void free_resources(void);
|
|
static int hastctl(struct nv *nvin, struct nv **nvout);
|
|
static int hast_fini(void);
|
|
static int hast_init(struct lmodule *mod, int argc, char *argv[]);
|
|
static void hast_start(void);
|
|
static int set_role(const char *resource, int role);
|
|
static int str2role(const char *str);
|
|
static int str2replication(const char *str);
|
|
static int str2status(const char *str);
|
|
static int update_resources(void);
|
|
|
|
const struct snmp_module config = {
|
|
.comment = "This module implements the BEGEMOT MIB for HAST.",
|
|
.init = hast_init,
|
|
.start = hast_start,
|
|
.fini = hast_fini,
|
|
.tree = hast_ctree,
|
|
.tree_size = hast_CTREE_SIZE,
|
|
};
|
|
|
|
static int
|
|
hast_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
|
|
{
|
|
|
|
module = mod;
|
|
|
|
pjdlog_init(PJDLOG_MODE_SYSLOG);
|
|
pjdlog_debug_set(0);
|
|
|
|
cfgpath = malloc(sizeof(HAST_CONFIG));
|
|
if (cfgpath == NULL) {
|
|
pjdlog_error("Unable to allocate %zu bytes for cfgpath",
|
|
sizeof(HAST_CONFIG));
|
|
return (-1);
|
|
}
|
|
strcpy(cfgpath, HAST_CONFIG);
|
|
return(0);
|
|
}
|
|
|
|
static void
|
|
hast_start(void)
|
|
{
|
|
hast_index = or_register(&oid_hast,
|
|
"The MIB module for BEGEMOT-HAST-MIB.", module);
|
|
}
|
|
|
|
static int
|
|
hast_fini(void)
|
|
{
|
|
|
|
or_unregister(hast_index);
|
|
free_resources();
|
|
free(cfgpath);
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
free_resources(void)
|
|
{
|
|
struct hast_snmp_resource *res;
|
|
|
|
while ((res = TAILQ_FIRST(&resources)) != NULL) {
|
|
TAILQ_REMOVE(&resources, res, link);
|
|
free(res);
|
|
}
|
|
}
|
|
|
|
static int
|
|
str2role(const char *str)
|
|
{
|
|
|
|
if (strcmp(str, "init") == 0)
|
|
return (HAST_ROLE_INIT);
|
|
if (strcmp(str, "primary") == 0)
|
|
return (HAST_ROLE_PRIMARY);
|
|
if (strcmp(str, "secondary") == 0)
|
|
return (HAST_ROLE_SECONDARY);
|
|
return (HAST_ROLE_UNDEF);
|
|
}
|
|
|
|
static int
|
|
str2replication(const char *str)
|
|
{
|
|
|
|
if (strcmp(str, "fullsync") == 0)
|
|
return (HAST_REPLICATION_FULLSYNC);
|
|
if (strcmp(str, "memsync") == 0)
|
|
return (HAST_REPLICATION_MEMSYNC);
|
|
if (strcmp(str, "async") == 0)
|
|
return (HAST_REPLICATION_ASYNC);
|
|
return (-1);
|
|
}
|
|
|
|
static int
|
|
str2status(const char *str)
|
|
{
|
|
|
|
if (strcmp(str, "complete") == 0)
|
|
return (0);
|
|
if (strcmp(str, "degraded") == 0)
|
|
return (1);
|
|
return (-1);
|
|
}
|
|
|
|
static int
|
|
hastctl(struct nv *nvin, struct nv **nvout)
|
|
{
|
|
struct hastd_config *cfg;
|
|
struct proto_conn *conn;
|
|
struct nv *nv;
|
|
int error;
|
|
|
|
cfg = yy_config_parse(cfgpath, true);
|
|
if (cfg == NULL)
|
|
return (-1);
|
|
|
|
/* Setup control connection... */
|
|
if (proto_client(NULL, cfg->hc_controladdr, &conn) == -1) {
|
|
pjdlog_error("Unable to setup control connection to %s",
|
|
cfg->hc_controladdr);
|
|
return (-1);
|
|
}
|
|
/* ...and connect to hastd. */
|
|
if (proto_connect(conn, HAST_TIMEOUT) == -1) {
|
|
pjdlog_error("Unable to connect to hastd via %s",
|
|
cfg->hc_controladdr);
|
|
proto_close(conn);
|
|
return (-1);
|
|
}
|
|
/* Send the command to the server... */
|
|
if (hast_proto_send(NULL, conn, nvin, NULL, 0) == -1) {
|
|
pjdlog_error("Unable to send command to hastd via %s",
|
|
cfg->hc_controladdr);
|
|
proto_close(conn);
|
|
return (-1);
|
|
}
|
|
/* ...and receive reply. */
|
|
if (hast_proto_recv_hdr(conn, &nv) == -1) {
|
|
pjdlog_error("cannot receive reply from hastd via %s",
|
|
cfg->hc_controladdr);
|
|
proto_close(conn);
|
|
return (-1);
|
|
}
|
|
proto_close(conn);
|
|
error = nv_get_int16(nv, "error");
|
|
if (error != 0) {
|
|
pjdlog_error("Error %d received from hastd.", error);
|
|
nv_free(nv);
|
|
return (-1);
|
|
}
|
|
nv_set_error(nv, 0);
|
|
*nvout = nv;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
set_role(const char *resource, int role)
|
|
{
|
|
struct nv *nvin, *nvout;
|
|
int error;
|
|
|
|
nvin = nv_alloc();
|
|
nv_add_string(nvin, resource, "resource%d", 0);
|
|
nv_add_uint8(nvin, HASTCTL_CMD_SETROLE, "cmd");
|
|
nv_add_uint8(nvin, role, "role");
|
|
error = hastctl(nvin, &nvout);
|
|
nv_free(nvin);
|
|
if (error != 0)
|
|
return (-1);
|
|
nv_free(nvout);
|
|
return (SNMP_ERR_NOERROR);
|
|
}
|
|
|
|
static int
|
|
update_resources(void)
|
|
{
|
|
struct hast_snmp_resource *res;
|
|
struct nv *nvin, *nvout;
|
|
static uint64_t now;
|
|
unsigned int i;
|
|
const char *str;
|
|
int error;
|
|
|
|
now = get_ticks();
|
|
if (now - last_resources_update < UPDATE_INTERVAL)
|
|
return (0);
|
|
|
|
last_resources_update = now;
|
|
|
|
free_resources();
|
|
|
|
nvin = nv_alloc();
|
|
nv_add_uint8(nvin, HASTCTL_CMD_STATUS, "cmd");
|
|
nv_add_string(nvin, "all", "resource%d", 0);
|
|
error = hastctl(nvin, &nvout);
|
|
nv_free(nvin);
|
|
if (error != 0)
|
|
return (-1);
|
|
|
|
for (i = 0; ; i++) {
|
|
str = nv_get_string(nvout, "resource%u", i);
|
|
if (str == NULL)
|
|
break;
|
|
res = calloc(1, sizeof(*res));
|
|
if (res == NULL) {
|
|
pjdlog_error("Unable to allocate %zu bytes for "
|
|
"resource", sizeof(*res));
|
|
return (-1);
|
|
}
|
|
res->index = i + 1;
|
|
strncpy(res->name, str, sizeof(res->name) - 1);
|
|
error = nv_get_int16(nvout, "error%u", i);
|
|
if (error != 0)
|
|
continue;
|
|
str = nv_get_string(nvout, "role%u", i);
|
|
res->role = str != NULL ? str2role(str) : HAST_ROLE_UNDEF;
|
|
str = nv_get_string(nvout, "provname%u", i);
|
|
if (str != NULL)
|
|
strncpy(res->provname, str, sizeof(res->provname) - 1);
|
|
str = nv_get_string(nvout, "localpath%u", i);
|
|
if (str != NULL) {
|
|
strncpy(res->localpath, str,
|
|
sizeof(res->localpath) - 1);
|
|
}
|
|
res->extentsize = nv_get_uint32(nvout, "extentsize%u", i);
|
|
res->keepdirty = nv_get_uint32(nvout, "keepdirty%u", i);
|
|
str = nv_get_string(nvout, "remoteaddr%u", i);
|
|
if (str != NULL) {
|
|
strncpy(res->remoteaddr, str,
|
|
sizeof(res->remoteaddr) - 1);
|
|
}
|
|
str = nv_get_string(nvout, "sourceaddr%u", i);
|
|
if (str != NULL) {
|
|
strncpy(res->sourceaddr, str,
|
|
sizeof(res->sourceaddr) - 1);
|
|
}
|
|
str = nv_get_string(nvout, "replication%u", i);
|
|
res->replication = str != NULL ? str2replication(str) : -1;
|
|
str = nv_get_string(nvout, "status%u", i);
|
|
res->status = str != NULL ? str2status(str) : -1;
|
|
res->dirty = nv_get_uint64(nvout, "dirty%u", i);
|
|
res->reads = nv_get_uint64(nvout, "stat_read%u", i);
|
|
res->writes = nv_get_uint64(nvout, "stat_write%u", i);
|
|
res->deletes = nv_get_uint64(nvout, "stat_delete%u", i);
|
|
res->flushes = nv_get_uint64(nvout, "stat_flush%u", i);
|
|
res->activemap_updates =
|
|
nv_get_uint64(nvout, "stat_activemap_update%u", i);
|
|
res->read_errors =
|
|
nv_get_uint64(nvout, "stat_read_error%u", i);
|
|
res->write_errors =
|
|
nv_get_uint64(nvout, "stat_write_error%u", i);
|
|
res->delete_errors =
|
|
nv_get_uint64(nvout, "stat_delete_error%u", i);
|
|
res->flush_errors =
|
|
nv_get_uint64(nvout, "stat_flush_error%u", i);
|
|
res->workerpid = nv_get_int32(nvout, "workerpid%u", i);
|
|
res->local_queue =
|
|
nv_get_uint64(nvout, "local_queue_size%u", i);
|
|
res->send_queue =
|
|
nv_get_uint64(nvout, "send_queue_size%u", i);
|
|
res->recv_queue =
|
|
nv_get_uint64(nvout, "recv_queue_size%u", i);
|
|
res->done_queue =
|
|
nv_get_uint64(nvout, "done_queue_size%u", i);
|
|
res->idle_queue =
|
|
nv_get_uint64(nvout, "idle_queue_size%u", i);
|
|
TAILQ_INSERT_TAIL(&resources, res, link);
|
|
}
|
|
nv_free(nvout);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
op_hastConfig(struct snmp_context *context, struct snmp_value *value,
|
|
u_int sub, u_int iidx __unused, enum snmp_op op)
|
|
{
|
|
asn_subid_t which;
|
|
|
|
which = value->var.subs[sub - 1];
|
|
|
|
switch (op) {
|
|
case SNMP_OP_GET:
|
|
switch (which) {
|
|
case LEAF_hastConfigFile:
|
|
return (string_get(value, cfgpath, -1));
|
|
default:
|
|
return (SNMP_ERR_RES_UNAVAIL);
|
|
}
|
|
case SNMP_OP_SET:
|
|
switch (which) {
|
|
case LEAF_hastConfigFile:
|
|
return (string_save(value, context, -1,
|
|
(u_char **)&cfgpath));
|
|
default:
|
|
return (SNMP_ERR_RES_UNAVAIL);
|
|
}
|
|
case SNMP_OP_GETNEXT:
|
|
case SNMP_OP_ROLLBACK:
|
|
case SNMP_OP_COMMIT:
|
|
return (SNMP_ERR_NOERROR);
|
|
default:
|
|
return (SNMP_ERR_RES_UNAVAIL);
|
|
}
|
|
}
|
|
|
|
int
|
|
op_hastResourceTable(struct snmp_context *context __unused,
|
|
struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op)
|
|
{
|
|
struct hast_snmp_resource *res;
|
|
asn_subid_t which;
|
|
int ret;
|
|
|
|
if (update_resources() == -1)
|
|
return (SNMP_ERR_RES_UNAVAIL);
|
|
|
|
which = value->var.subs[sub - 1];
|
|
|
|
switch (op) {
|
|
case SNMP_OP_GETNEXT:
|
|
res = NEXT_OBJECT_INT(&resources, &value->var, sub);
|
|
if (res == NULL)
|
|
return (SNMP_ERR_NOSUCHNAME);
|
|
value->var.len = sub + 1;
|
|
value->var.subs[sub] = res->index;
|
|
break;
|
|
case SNMP_OP_GET:
|
|
if (value->var.len - sub != 1)
|
|
return (SNMP_ERR_NOSUCHNAME);
|
|
res = FIND_OBJECT_INT(&resources, &value->var, sub);
|
|
if (res == NULL)
|
|
return (SNMP_ERR_NOSUCHNAME);
|
|
break;
|
|
case SNMP_OP_SET:
|
|
res = FIND_OBJECT_INT(&resources, &value->var, sub);
|
|
if (res == NULL)
|
|
return (SNMP_ERR_NOSUCHNAME);
|
|
switch (which) {
|
|
case LEAF_hastResourceRole:
|
|
ret = set_role(res->name, value->v.integer);
|
|
/* force update on next run */
|
|
last_resources_update = 0;
|
|
break;
|
|
default:
|
|
ret = SNMP_ERR_NOT_WRITEABLE;
|
|
break;
|
|
}
|
|
return ret;
|
|
case SNMP_OP_ROLLBACK:
|
|
case SNMP_OP_COMMIT:
|
|
return (SNMP_ERR_NOERROR);
|
|
default:
|
|
return (SNMP_ERR_RES_UNAVAIL);
|
|
}
|
|
|
|
ret = SNMP_ERR_NOERROR;
|
|
|
|
switch (which) {
|
|
case LEAF_hastResourceIndex:
|
|
value->v.integer = res->index;
|
|
break;
|
|
case LEAF_hastResourceName:
|
|
ret = string_get(value, res->name, -1);
|
|
break;
|
|
case LEAF_hastResourceRole:
|
|
value->v.integer = res->role;
|
|
break;
|
|
case LEAF_hastResourceProvName:
|
|
ret = string_get(value, res->provname, -1);
|
|
break;
|
|
case LEAF_hastResourceLocalPath:
|
|
ret = string_get(value, res->localpath, -1);
|
|
break;
|
|
case LEAF_hastResourceExtentSize:
|
|
value->v.integer = res->extentsize;
|
|
break;
|
|
case LEAF_hastResourceKeepDirty:
|
|
value->v.integer = res->keepdirty;
|
|
break;
|
|
case LEAF_hastResourceRemoteAddr:
|
|
ret = string_get(value, res->remoteaddr, -1);
|
|
break;
|
|
case LEAF_hastResourceSourceAddr:
|
|
ret = string_get(value, res->sourceaddr, -1);
|
|
break;
|
|
case LEAF_hastResourceReplication:
|
|
value->v.integer = res->replication;
|
|
break;
|
|
case LEAF_hastResourceStatus:
|
|
value->v.integer = res->status;
|
|
break;
|
|
case LEAF_hastResourceDirty:
|
|
value->v.counter64 = res->dirty;
|
|
break;
|
|
case LEAF_hastResourceReads:
|
|
value->v.counter64 = res->reads;
|
|
break;
|
|
case LEAF_hastResourceWrites:
|
|
value->v.counter64 = res->writes;
|
|
break;
|
|
case LEAF_hastResourceDeletes:
|
|
value->v.counter64 = res->deletes;
|
|
break;
|
|
case LEAF_hastResourceFlushes:
|
|
value->v.counter64 = res->flushes;
|
|
break;
|
|
case LEAF_hastResourceActivemapUpdates:
|
|
value->v.counter64 = res->activemap_updates;
|
|
break;
|
|
case LEAF_hastResourceReadErrors:
|
|
value->v.counter64 = res->read_errors;
|
|
break;
|
|
case LEAF_hastResourceWriteErrors:
|
|
value->v.counter64 = res->write_errors;
|
|
break;
|
|
case LEAF_hastResourceDeleteErrors:
|
|
value->v.counter64 = res->delete_errors;
|
|
break;
|
|
case LEAF_hastResourceFlushErrors:
|
|
value->v.counter64 = res->flush_errors;
|
|
break;
|
|
case LEAF_hastResourceWorkerPid:
|
|
value->v.integer = res->workerpid;
|
|
break;
|
|
case LEAF_hastResourceLocalQueue:
|
|
value->v.uint32 = res->local_queue;
|
|
break;
|
|
case LEAF_hastResourceSendQueue:
|
|
value->v.uint32 = res->send_queue;
|
|
break;
|
|
case LEAF_hastResourceRecvQueue:
|
|
value->v.uint32 = res->recv_queue;
|
|
break;
|
|
case LEAF_hastResourceDoneQueue:
|
|
value->v.uint32 = res->done_queue;
|
|
break;
|
|
case LEAF_hastResourceIdleQueue:
|
|
value->v.uint32 = res->idle_queue;
|
|
break;
|
|
default:
|
|
ret = SNMP_ERR_RES_UNAVAIL;
|
|
break;
|
|
}
|
|
return (ret);
|
|
}
|