view usr/src/cmd/cmd-inet/usr.sbin/ilbadm/ilbadm_hc.c @ 10946:324bab2b3370

PSARC 2008/575 ILB: Integrated L3/L4 Load balancer 6882718 in-kernel simple L3/L4 load balancing service should be provided in Solaris 6884202 ipobs_hook() in ip_input() invalidates DB_REF assumption
author Sangeeta Misra <Sangeeta.Misra@Sun.COM>
date Tue, 03 Nov 2009 23:15:19 -0800
parents
children
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/list.h>
#include <netinet/in.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <errno.h>
#include <ofmt.h>
#include <libilb.h>
#include "ilbadm.h"

extern int	optind, optopt, opterr;
extern char	*optarg;

typedef struct hc_export_arg {
	FILE	*fp;
} hc_export_arg_t;

/* Maximum columns for printing hc output. */
#define	SHOW_HC_COLS	80

/* OFMT call back to print out a hc server result field. */
static boolean_t print_hc_result(ofmt_arg_t *, char *, uint_t);

/* ID to indicate which field to be printed. */
enum hc_print_id {
	hc_of_rname, hc_of_hname, hc_of_sname, hc_of_status, hc_of_fail_cnt,
	hc_of_lasttime, hc_of_nexttime, hc_of_rtt,
	hc_of_name, hc_of_timeout, hc_of_count, hc_of_interval, hc_of_def_ping,
	hc_of_test
};

/*
 * Fields of a hc server result.  The sum of all fields' width is SHOW_HC_COLS.
 */
static ofmt_field_t hc_results[] = {
	{"RULENAME",	14,	hc_of_rname,	print_hc_result},
	{"HCNAME",	14,	hc_of_hname,	print_hc_result},
	{"SERVERID",	14,	hc_of_sname,	print_hc_result},
	{"STATUS",	9,	hc_of_status,	print_hc_result},
	{"FAIL",	5,	hc_of_fail_cnt,	print_hc_result},
	{"LAST",	9,	hc_of_lasttime,	print_hc_result},
	{"NEXT",	9,	hc_of_nexttime,	print_hc_result},
	{"RTT",		6,	hc_of_rtt,	print_hc_result},
	{NULL,		0,	0,		NULL}
};

/* OFMT call back to print out a hc info field. */
static boolean_t print_hc(ofmt_arg_t *, char *, uint_t);

/*
 * Fields of a hc info.  The sume of all fields' width is SHOW_HC_COLS.
 */
static ofmt_field_t hc_fields[] = {
	{"HCNAME",	14,	hc_of_name,	print_hc},
	{"TIMEOUT",	8,	hc_of_timeout,	print_hc},
	{"COUNT",	8,	hc_of_count,	print_hc},
	{"INTERVAL",	9,	hc_of_interval,	print_hc},
	{"DEF_PING",	9,	hc_of_def_ping,	print_hc},
	{"TEST",	32,	hc_of_test,	print_hc},
	{NULL,		0,	0,		NULL}
};

static boolean_t
print_hc(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
{
	enum hc_print_id id = of_arg->ofmt_id;
	ilb_hc_info_t *info = (ilb_hc_info_t *)of_arg->ofmt_cbarg;

	switch (id) {
	case hc_of_name:
		(void) strlcpy(buf, info->hci_name, bufsize);
		break;
	case hc_of_timeout:
		(void) snprintf(buf, bufsize, "%d", info->hci_timeout);
		break;
	case hc_of_count:
		(void) snprintf(buf, bufsize, "%d", info->hci_count);
		break;
	case hc_of_interval:
		(void) snprintf(buf, bufsize, "%d", info->hci_interval);
		break;
	case hc_of_def_ping:
		(void) snprintf(buf, bufsize, "%c",
		    info->hci_def_ping ? 'Y' : 'N');
		break;
	case hc_of_test:
		(void) snprintf(buf, bufsize, "%s", info->hci_test);
		break;
	}
	return (B_TRUE);
}

/* Call back to ilb_walk_hc(). */
/* ARGSUSED */
static ilb_status_t
ilbadm_print_hc(ilb_handle_t h, ilb_hc_info_t *hc_info, void *arg)
{
	ofmt_handle_t	ofmt_h = arg;

	ofmt_print(ofmt_h, hc_info);
	return (ILB_STATUS_OK);
}

/*
 * Print out health check objects given their name.
 * Or print out all health check objects if no name given.
 */
/* ARGSUSED */
ilbadm_status_t
ilbadm_show_hc(int argc, char *argv[])
{
	ilb_handle_t	h = ILB_INVALID_HANDLE;
	ilb_status_t	rclib;
	ofmt_handle_t	ofmt_h;
	ofmt_status_t	ofmt_ret;

	if ((ofmt_ret = ofmt_open("all", hc_fields, 0, SHOW_HC_COLS,
	    &ofmt_h)) != OFMT_SUCCESS) {
		char err_buf[SHOW_HC_COLS];

		ilbadm_err(gettext("ofmt_open failed: %s"),
		    ofmt_strerror(ofmt_h, ofmt_ret, err_buf, SHOW_HC_COLS));
		return (ILBADM_LIBERR);
	}
	rclib = ilb_open(&h);
	if (rclib != ILB_STATUS_OK)
		goto out;

	if (argc == 1) {
		rclib = ilb_walk_hc(h, ilbadm_print_hc, ofmt_h);
	} else {
		ilb_hc_info_t hc_info;
		int i;

		for (i = 1; i < argc; i++) {
			rclib = ilb_get_hc_info(h, argv[i], &hc_info);
			if (rclib == ILB_STATUS_OK)
				ofmt_print(ofmt_h, &hc_info);
			else
				break;
		}
	}
out:
	ofmt_close(ofmt_h);

	if (h != ILB_INVALID_HANDLE)
		(void) ilb_close(h);

	if (rclib != ILB_STATUS_OK) {
		ilbadm_err(ilb_errstr(rclib));
		return (ILBADM_LIBERR);
	}

	return (ILBADM_OK);
}

static boolean_t
print_hc_result(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
{
	enum hc_print_id id = of_arg->ofmt_id;
	ilb_hc_srv_t *srv = (ilb_hc_srv_t *)of_arg->ofmt_cbarg;
	struct tm tv;

	switch (id) {
	case hc_of_rname:
		(void) strlcpy(buf, srv->hcs_rule_name, bufsize);
		break;
	case hc_of_hname:
		(void) strlcpy(buf, srv->hcs_hc_name, bufsize);
		break;
	case hc_of_sname:
		(void) strlcpy(buf, srv->hcs_ID, bufsize);
		break;
	case hc_of_status:
		switch (srv->hcs_status) {
		case ILB_HCS_UNINIT:
			(void) strlcpy(buf, "un-init", bufsize);
			break;
		case ILB_HCS_UNREACH:
			(void) strlcpy(buf, "unreach", bufsize);
			break;
		case ILB_HCS_ALIVE:
			(void) strlcpy(buf, "alive", bufsize);
			break;
		case ILB_HCS_DEAD:
			(void) strlcpy(buf, "dead", bufsize);
			break;
		case ILB_HCS_DISABLED:
			(void) strlcpy(buf, "disabled", bufsize);
			break;
		}
		break;
	case hc_of_fail_cnt:
		(void) snprintf(buf, bufsize, "%u", srv->hcs_fail_cnt);
		break;
	case hc_of_lasttime:
		if (localtime_r(&srv->hcs_lasttime, &tv) == NULL)
			return (B_FALSE);
		(void) snprintf(buf, bufsize, "%02d:%02d:%02d", tv.tm_hour,
		    tv.tm_min, tv.tm_sec);
		break;
	case hc_of_nexttime:
		if (srv->hcs_status == ILB_HCS_DISABLED)
			break;
		if (localtime_r(&srv->hcs_nexttime, &tv) == NULL)
			return (B_FALSE);
		(void) snprintf(buf, bufsize, "%02d:%02d:%02d", tv.tm_hour,
		    tv.tm_min, tv.tm_sec);
		break;
	case hc_of_rtt:
		(void) snprintf(buf, bufsize, "%u", srv->hcs_rtt);
		break;
	}
	return (B_TRUE);
}

/* Call back to ilbd_walk_hc_srvs(). */
/* ARGSUSED */
static ilb_status_t
ilbadm_print_hc_result(ilb_handle_t h, ilb_hc_srv_t *srv, void *arg)
{
	ofmt_handle_t	ofmt_h = arg;

	ofmt_print(ofmt_h, srv);
	return (ILB_STATUS_OK);
}

/*
 * Output hc result of a specified rule or all rules.
 */
ilbadm_status_t
ilbadm_show_hc_result(int argc, char *argv[])
{
	ilb_handle_t	h = ILB_INVALID_HANDLE;
	ilb_status_t 	rclib = ILB_STATUS_OK;
	int		i;
	ofmt_handle_t	ofmt_h;
	ofmt_status_t	ofmt_ret;

	/* ilbadm show-hc-result [rule-name] */
	if (argc < 1) {
		ilbadm_err(gettext("usage: ilbadm show-hc-result"
		    " [rule-name]"));
		return (ILBADM_LIBERR);
	}

	if ((ofmt_ret = ofmt_open("all", hc_results, 0, SHOW_HC_COLS,
	    &ofmt_h)) != OFMT_SUCCESS) {
		char err_buf[SHOW_HC_COLS];

		ilbadm_err(gettext("ofmt_open failed: %s"),
		    ofmt_strerror(ofmt_h, ofmt_ret, err_buf, SHOW_HC_COLS));
		return (ILBADM_LIBERR);
	}

	rclib = ilb_open(&h);
	if (rclib != ILB_STATUS_OK)
		goto out;

	/* If no rule name is given, show results for all rules. */
	if (argc == 1) {
		rclib = ilb_walk_hc_srvs(h, ilbadm_print_hc_result, NULL,
		    ofmt_h);
	} else {
		for (i = 1; i < argc; i++) {
			rclib = ilb_walk_hc_srvs(h, ilbadm_print_hc_result,
			    argv[i], ofmt_h);
			if (rclib != ILB_STATUS_OK)
				break;
		}
	}
out:
	ofmt_close(ofmt_h);

	if (h != ILB_INVALID_HANDLE)
		(void) ilb_close(h);

	if (rclib != ILB_STATUS_OK) {
		ilbadm_err(ilb_errstr(rclib));
		return (ILBADM_LIBERR);
	}
	return (ILBADM_OK);
}

#define	ILBADM_DEF_HC_COUNT	3
#define	ILBADM_DEF_HC_INTERVAL	30	/* in sec */
#define	ILBADM_DEF_HC_TIMEOUT	5	/* in sec */

static ilbadm_key_name_t hc_parse_keys[] = {
	{ILB_KEY_HC_TEST, "hc-test", "hc-test"},
	{ILB_KEY_HC_COUNT, "hc-count", "hc-count"},
	{ILB_KEY_HC_TIMEOUT, "hc-timeout", "hc-tout"},
	{ILB_KEY_HC_INTERVAL, "hc-interval", "hc-intl"},
	{ILB_KEY_BAD, "", ""}
};

static ilbadm_status_t
ilbadm_hc_parse_arg(char *arg, ilb_hc_info_t *hc)
{
	ilbadm_status_t ret;

	/* set default value for count, interval, timeout */
	hc->hci_count = ILBADM_DEF_HC_COUNT;
	hc->hci_interval = ILBADM_DEF_HC_INTERVAL;
	hc->hci_timeout = ILBADM_DEF_HC_TIMEOUT;
	hc->hci_test[0] = '\0';

	ret = i_parse_optstring(arg, hc, hc_parse_keys, 0, NULL);
	if (ret != ILBADM_OK && ret != ILBADM_LIBERR) {
		ilbadm_err(ilbadm_errstr(ret));
		return (ILBADM_LIBERR);
	}
	if (hc->hci_test[0] == '\0' && ret != ILBADM_LIBERR) {
		ilbadm_err("hc-test: missing");
		return (ILBADM_LIBERR);
	}
	return (ret);
}

/* ARGSUSED */
ilbadm_status_t
ilbadm_create_hc(int argc, char *argv[])
{
	ilb_handle_t	h = ILB_INVALID_HANDLE;
	ilb_hc_info_t	hc_info;
	ilbadm_status_t	ret = ILBADM_OK;
	ilb_status_t	rclib;
	char		c;


	hc_info.hci_def_ping = B_TRUE;
	while ((c = getopt(argc, argv, ":h:n")) != -1) {
		if (c == 'h') {
			ret = ilbadm_hc_parse_arg(optarg, &hc_info);
			if (ret != ILBADM_OK)
				return (ret);
		} else if (c == 'n') {
			hc_info.hci_def_ping = B_FALSE;
		} else {
			ilbadm_err(gettext("bad argument %c"), c);
			return (ILBADM_LIBERR);
		}
	}

	if (optind >= argc) {
		ilbadm_err(gettext("usage: ilbadm"
		    " create-healthcheck [-n] -h"
		    " hc-test=val[,hc-timeout=val][,hc-count=va]"
		    "[,hc-interval=val]  hc-name"));
		return (ILBADM_FAIL);
	}

	if (strlen(argv[optind]) > ILBD_NAMESZ - 1) {
		ilbadm_err(gettext("health check object name %s is too long - "
		    "must not exceed %d chars"), argv[optind],
		    ILBD_NAMESZ - 1);
		return (ILBADM_FAIL);
	}

	if (((strcasecmp(hc_info.hci_test, ILB_HC_STR_UDP) == 0) ||
	    (strcasecmp(hc_info.hci_test, ILB_HC_STR_PING) == 0)) &&
	    !(hc_info.hci_def_ping)) {
		ilbadm_err(gettext("cannot disable default PING"
		    " for this test"));
		return (ILBADM_LIBERR);
	}

	rclib = ilb_open(&h);
	if (rclib != ILB_STATUS_OK)
		goto out;

	(void) strlcpy(hc_info.hci_name, argv[optind],
	    sizeof (hc_info.hci_name));
	rclib = ilb_create_hc(h, &hc_info);
out:
	if (h != ILB_INVALID_HANDLE)
		(void) ilb_close(h);

	if (rclib != ILB_STATUS_OK) {
		ilbadm_err(ilb_errstr(rclib));
		ret = ILBADM_LIBERR;
	}
	return (ret);
}

ilbadm_status_t
ilbadm_destroy_hc(int argc, char *argv[])
{
	ilb_handle_t	h = ILB_INVALID_HANDLE;
	ilb_status_t	rclib;
	ilbadm_status_t ret = ILBADM_OK;
	int		i;

	if (argc < 2) {
		ilbadm_err(gettext("usage: ilbadm"
		    " delete-healthcheck hc-name ..."));
		return (ILBADM_LIBERR);
	}

	rclib = ilb_open(&h);
	if (rclib != ILB_STATUS_OK)
		goto out;

	for (i = 1; i < argc; i++) {
		rclib = ilb_destroy_hc(h, argv[i]);
		if (rclib != ILB_STATUS_OK)
			break;
	}
out:
	if (h != ILB_INVALID_HANDLE)
		(void) ilb_close(h);

	if (rclib != ILB_STATUS_OK) {
		ilbadm_err(ilb_errstr(rclib));
		ret = ILBADM_LIBERR;
	}
	return (ret);
}

/*
 * Since this function is used by libilb function, it
 * must return libilb errors
 */
/* ARGSUSED */
ilb_status_t
ilbadm_export_hcinfo(ilb_handle_t h, ilb_hc_info_t *hc_info, void *arg)
{
	FILE 		*fp = ((hc_export_arg_t *)arg)->fp;
	int		count = 0;
	int		ret;

	/*
	 * a test name "PING" implies "no default ping", so we only
	 * print -n if the test is NOT "PING"
	 */
	if (hc_info->hci_def_ping == B_FALSE &&
	    strncasecmp(hc_info->hci_test, "PING", 5) != 0)
		(void) fprintf(fp, "create-healthcheck -n -h ");
	else
		(void) fprintf(fp, "create-healthcheck -h ");

	if (*hc_info->hci_test != '\0') {
		(void) fprintf(fp, "hc-test=%s", hc_info->hci_test);
		count++;
	}
	if (hc_info->hci_timeout != 0) {
		if (count++ > 0)
			(void) fprintf(fp, ",");
		(void) fprintf(fp, "hc-timeout=%d", hc_info->hci_timeout);
	}
	if (hc_info->hci_count != 0) {
		if (count++ > 0)
			(void) fprintf(fp, ",");
		(void) fprintf(fp, "hc-count=%d", hc_info->hci_count);
	}
	if (hc_info->hci_interval != 0) {
		if (count > 0)
			(void) fprintf(fp, ",");
		(void) fprintf(fp, "hc-interval=%d", hc_info->hci_interval);
	}

	/*
	 * if any of the above writes fails, then, we assume, so will
	 * this one; so it's sufficient to test once
	 */
	ret = fprintf(fp, " %s\n", hc_info->hci_name);
	if (ret < 0)
		goto out_fail;
	ret = fflush(fp);

out_fail:
	if (ret < 0)
		return (ILB_STATUS_WRITE);
	return (ILB_STATUS_OK);
}

ilbadm_status_t
ilbadm_export_hc(ilb_handle_t h, FILE *fp)
{
	ilb_status_t	rclib;
	ilbadm_status_t	ret = ILBADM_OK;
	hc_export_arg_t	arg;

	arg.fp = fp;
	rclib = ilb_walk_hc(h, ilbadm_export_hcinfo, (void *)&arg);
	if (rclib != ILB_STATUS_OK) {
		ilbadm_err(ilb_errstr(rclib));
		ret = ILBADM_LIBERR;
	}
	return (ret);
}