From f665796f26e18afe15ef4daca4ffb8735b123447 Mon Sep 17 00:00:00 2001 From: Lon Hohberger Date: Wed, 13 Jan 2010 23:34:35 -0500 Subject: [PATCH] Enable VM Channel support in serial plugin - Add cmdline processing for channel_ip - Allow 'port' to be used in fence_virt mode - Allow configuration of vmchannel mode for fence_virtd serial plugin - Remove serial port default from fence_virt. It must now be specified or else VM Channel mode is used. Note that to use VM Channel mode, you need: qemu 0.12.1 or later libvirt 0.7.3 or later - Update man pages Signed-off-by: Lon Hohberger --- client/main.c | 2 +- client/options.c | 18 +++++++- client/serial.c | 46 +++++++++++++------- include/options.h | 1 + include/xvm.h | 1 + man/fence_virt.8 | 77 ++++++++++++++++++++++------------ man/fence_virt.conf.5 | 37 ++++++++++------ server/serial.c | 32 +++++++++++++- server/serial.h | 2 +- server/virt-serial.c | 98 +++++++++++++++++++++++++++++++++---------- 10 files changed, 232 insertions(+), 82 deletions(-) diff --git a/client/main.c b/client/main.c index 956020a..5b65c39 100644 --- a/client/main.c +++ b/client/main.c @@ -59,7 +59,7 @@ main(int argc, char **argv) my_options = "di:a:p:r:C:c:k:M:H:uo:t:?hV"; args.mode = MODE_MULTICAST; } else { - my_options = "dD:P:M:H:o:t:?hV"; + my_options = "dD:P:A:p:M:H:o:t:?hV"; args.mode = MODE_SERIAL; } diff --git a/client/options.c b/client/options.c index 6816ec2..528bc74 100644 --- a/client/options.c +++ b/client/options.c @@ -97,6 +97,15 @@ assign_address(fence_virt_args_t *args, struct arg_info *arg, char *value) } +static inline void +assign_channel_address(fence_virt_args_t *args, struct arg_info *arg, char *value) +{ + if (args->serial.address) + free(args->serial.address); + args->serial.address = strdup(value); +} + + static inline void assign_port(fence_virt_args_t *args, struct arg_info *arg, char *value) { @@ -319,8 +328,12 @@ static struct arg_info _arg_info[] = { "Multicast address (default=" IPV4_MCAST_DEFAULT " / " IPV6_MCAST_DEFAULT ")", assign_address }, + { 'A', "-A
", "channel_address", + "VM Channel IP address (default=" DEFAULT_CHANNEL_IP ")", + assign_channel_address }, + { 'p', "-p ", "port", - "IP port (default=1229)", + "Multicast or VMChannel IP port (default=1229)", assign_port }, { 'I', "-I ", "interface", @@ -443,8 +456,9 @@ args_init(fence_virt_args_t *args) args->net.port = DEFAULT_MCAST_PORT; args->net.ifindex = 0; args->net.family = PF_INET; - args->serial.device = strdup(DEFAULT_SERIAL_DEVICE); + args->serial.device = NULL; args->serial.speed = strdup(DEFAULT_SERIAL_SPEED); + args->serial.address = strdup(DEFAULT_CHANNEL_IP); args->timeout = 30; args->retr_time = 20; args->flags = 0; diff --git a/client/serial.c b/client/serial.c index 3ce872d..390f4d7 100644 --- a/client/serial.c +++ b/client/serial.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include static int @@ -212,28 +214,46 @@ wait_for(int fd, const char *pattern, size_t size, struct timeval *tout) int serial_fence_virt(fence_virt_args_t *args) { + struct in_addr ina; + struct in6_addr in6a; serial_req_t req; int fd, ret; char speed[32], *flags = NULL; struct timeval tv; serial_resp_t resp; - strncpy(speed, args->serial.speed, sizeof(speed)); + if (args->serial.device) { + strncpy(speed, args->serial.speed, sizeof(speed)); - //printf("Port: %s Speed: %s\n", args->serial.device, speed); + //printf("Port: %s Speed: %s\n", args->serial.device, speed); - if ((flags = strchr(speed, ','))) { - *flags = 0; - flags++; + if ((flags = strchr(speed, ','))) { + *flags = 0; + flags++; + } + + fd = open_port(args->serial.device, speed, flags); + if (fd == -1) { + perror("open_port"); + return -1; + } + + hangup(fd, 300000); + } else { + fd = -1; + if (inet_pton(PF_INET, args->serial.address, &ina)) { + fd = ipv4_connect(&ina, args->net.port, 3); + } else if (inet_pton(PF_INET6, args->serial.address, &in6a)) { + fd = ipv6_connect(&in6a, args->net.port, 3); + } + + if (fd < 0) { + perror("vmchannel connect"); + printf("Failed to connect to %s:%d\n", args->serial.address, + args->net.port); + } } - fd = open_port(args->serial.device, speed, flags); - if (fd == -1) { - perror("open_port"); - return -1; - } - - hangup(fd, 300000); memset(&req, 0, sizeof(req)); req.magic = SERIAL_MAGIC; @@ -241,8 +261,6 @@ serial_fence_virt(fence_virt_args_t *args) gettimeofday(&tv, NULL); req.seqno = (int)tv.tv_usec; - if (args->flags & RF_UUID) - req.flags |= RF_UUID; if (args->domain) strncpy((char *)req.domain, args->domain, sizeof(req.domain)); diff --git a/include/options.h b/include/options.h index ffacfea..5253ba9 100644 --- a/include/options.h +++ b/include/options.h @@ -60,6 +60,7 @@ typedef struct { struct serial_args { char *device; /* Serial device */ char *speed; + char *address; /* vmchannel IP */ } serial; } fence_virt_args_t; diff --git a/include/xvm.h b/include/xvm.h index 835f961..b08d0bb 100644 --- a/include/xvm.h +++ b/include/xvm.h @@ -96,6 +96,7 @@ typedef struct __attribute__ ((packed)) _host_info { #define DEFAULT_SERIAL_DEVICE "/dev/ttyS1" #define DEFAULT_SERIAL_SPEED "115200,8N1" +#define DEFAULT_CHANNEL_IP "10.0.2.179" #define SERIAL_MAGIC 0x61626261 /* endian doesn't matter */ typedef struct __attribute__((packed)) _serial_fence_req { diff --git a/man/fence_virt.8 b/man/fence_virt.8 index eecb26b..862aa9a 100644 --- a/man/fence_virt.8 +++ b/man/fence_virt.8 @@ -27,7 +27,7 @@ machines from scripts. .TP .B -d . -Specify (CCS) / increment (command line) debug level +Increment (command line) debug level .TP .B -H @@ -37,12 +37,12 @@ Virtual machine (domain name) to fence .TP .B -o . -Fencing action (null, off, on, [reboot], status, devstatus, or hostlist) (Default Value: reboot) +Fencing action (null, off, on, reboot, status, devstatus, or hostlist) (Default Value: reboot) .TP .B -t . -Fencing timeout (in seconds; default=30) (Default Value: 30) +Fencing timeout (in seconds) (Default Value: 30) .SH MULTICAST PARAMETERS These parameters are used only when using fence_virt in multicast mode @@ -51,37 +51,37 @@ These parameters are used only when using fence_virt in multicast mode .TP .B -i . -IP Family ([auto], ipv4, ipv6) (Default Value: auto) +IP Family (auto, ipv4, ipv6) (Default Value: auto) .TP .B -a . -Multicast address (default=225.0.0.12 / ff02::3:1) +Multicast address (Default Values: 225.0.0.12 / ff02::3:1) .TP .B -p . -IP port (default=1229) (Default Value: 1229) +IP port (Default Value: 1229) .TP .B -T . -Multicast time-to-live (in hops; default=2) (Default Value: 2) +Multicast time-to-live (in hops) (Default Value: 2) .TP .B -r . -Multicast retransmit time (in 1/10sec; default=20) (Default Value: 20) +Multicast retransmit time (in 1/10sec) (Default Value: 20) .TP .B -C . -Authentication (none, sha1, [sha256], sha512) (Default Value: sha256) +Authentication (none, sha1, sha256, sha512) (Default Value: sha256) .TP .B -c . -Packet hash strength (none, sha1, [sha256], sha512) (Default Value: sha256) +Packet hash strength (none, sha1, sha256, sha512) (Default Value: sha256) .TP .B -k @@ -93,21 +93,31 @@ Shared key file (Default Value: /etc/cluster/fence_xvm.key) . Treat 'domain' as UUID instead of domain name. -.SH MULTICAST PARAMETERS +.SH SERIAL/VMCHANNEL PARAMETERS These parameters are used only when using fence_virt in multicast mode (e.g. by running fence_xvm). .TP .B -D . -Serial device (fence_virt mode). Default=/dev/ttyS1. On the host, the -serial device must be mapped in each domain's configuration file. See -fence_virt.conf(5) for more information. +Serial device (fence_virt mode). On the host, the serial device must +be mapped in each domain's configuration file. See fence_virt.conf(5) +for more information. Specifying a serial device causes fence_virt +to use serial mode (as opposed to VMChannel mode). .TP .B -P . -Serial parameters. Default=115200,8N1 +Serial parameters. (Default Value: 115200,8N1) + +.TP +.B -A +VMChannel IP address (Default Value: 10.0.2.179) + +.TP +.B -p +. +VMChannel IP port (Default Value: 1229) .SH GENERAL STDIN PARAMETERS These parameters are passed to fence_virt via standard input if @@ -134,7 +144,7 @@ Fencing action to perform (See FENCING ACTIONS section) .TP .B timeout . -Fencing timeout (in seconds; default=30) (Default Value: 30) +Fencing timeout (in seconds) (Default Value: 30) .SH MULTICAST STDIN PARAMETERS .TP @@ -145,56 +155,69 @@ IP Family ([auto], ipv4, ipv6) (Default Value: auto) .TP .B multicast_address . -Multicast address (default=225.0.0.12 / ff02::3:1) +Multicast address (Defaults: 225.0.0.12 / ff02::3:1) .TP .B port . -IP port (default=1229) (Default Value: 1229) +IP port (Default Value: 1229) .TP .B multicast_ttl . -Multicast time-to-live (in hops; default=2) (Default Value: 2) +Multicast time-to-live (in hops) (Default Value: 2) .TP .B retrans . -Multicast retransmit time (in 1/10sec; default=20) (Default Value: 20) +Multicast retransmit time (in 1/10sec) (Default Value: 20) .TP .B auth . -Authentication (none, sha1, [sha256], sha512) (Default Value: sha256) +Authentication (none, sha1, sha256, sha512) (Default Value: sha256) .TP .B hash . -Packet hash strength (none, sha1, [sha256], sha512) (Default Value: sha256) +Packet hash strength (none, sha1, sha256, sha512) (Default Value: sha256) .TP .B key_file . -Shared key file (default=/etc/cluster/fence_xvm.key) (Default Value: /etc/cluster/fence_xvm.key) +Shared key file (Default Value: /etc/cluster/fence_xvm.key) .TP .B use_uuid . Treat 'domain' as UUID instead of domain name -.SH SERIAL STDIN PARAMETERS +.SH SERIAL/VMCHANNEL STDIN PARAMETERS .TP .B serial_device . -Serial device. Default=/dev/ttyS1. On the host, the serial device must -be mapped in each domain's configuration file. See fence_virt.conf(5) -for more information. +Serial device. On the host, the serial device must be mapped in +each domain's configuration file. See fence_virt.conf(5) +for more information. If specified, causes fence_virt to operate +in serial mode (not specifying causes fence_virt to operate in +VM Channel mode). .TP .B serial_params . Serial parameters. Default=115200,8N1. +.TP +.B channel_ip +. +Channel IP. Default=10.0.2.179 + +.TP +.B port +. +Channel port. Default=1229 + + .SH FENCING ACTIONS .TP diff --git a/man/fence_virt.conf.5 b/man/fence_virt.conf.5 index 6303184..1f6937d 100644 --- a/man/fence_virt.conf.5 +++ b/man/fence_virt.conf.5 @@ -73,10 +73,6 @@ There are various listeners available for fence_virtd, each one handles decoding and authentication of a given fencing request. The following configuration blocks belong in the \fBlisteners\fP section of fence_virtd.conf -.SS vmchannel -To be done. This listener utilizes serial vmchannel tied to Unix domain -sockets on the host in order to receive and route fencing requests. - .SS multicast .TP .B key_file @@ -119,23 +115,25 @@ is used as a gateway. .SS serial -The serial listener plugin utilizes libvirt's serial mapping to listen -for requests. When using the serial listener, it is necessary to add -a serial port (preferably pointing to /dev/ttyS1) in the libvirt domain -description. Note that only serial type +The serial listener plugin utilizes libvirt's serial (or VMChannel) +mapping to listen for requests. When using the serial listener, it is +necessary to add a serial port (preferably pointing to /dev/ttyS1) or +a channel (preferrably pointing to 10.0.2.179:1229) to the +libvirt domain description. Note that only type .B unix , mode .B bind -is supported. The socket path does not matter, and currently, fence_virt -will only use the -.B first specified -virtual serial port of this type. Example libvirt XML: +serial ports and channels are supported. Example libvirt XML: .in 8 - + <\fBserial\fP type='unix'> + <\fBchannel\fP type='unix'> + + + .in 0 .TP @@ -143,6 +141,19 @@ virtual serial port of this type. Example libvirt XML: . the URI to use when connecting to libvirt by the serial plugin. +.TP +.B path +. +Sockets must reside in this directory in order to be considered +valid. This can be used to prevent fence_virtd from using the wrong +sockets. + +.TP +.B mode +. +This selects the type of sockets to register. Valid values are "serial" +(default) and "vmchannel". + .SH BACKENDS diff --git a/server/serial.c b/server/serial.c index b249e63..67241f7 100644 --- a/server/serial.c +++ b/server/serial.c @@ -71,8 +71,10 @@ typedef struct _serial_info { const fence_callbacks_t *cb; void *priv; char *uri; + char *path; history_info_t *history; void *maps; + int mode; } serial_info; @@ -260,12 +262,16 @@ serial_dispatch(listener_context_t c, struct timeval *timeout) if (n == 0) return 0; + printf("max = %d\n", max); + /* find & read request */ for (x = 0; x <= max; x++) { if (FD_ISSET(x, &rfds)) { tv.tv_sec = 1; tv.tv_usec = 0; + printf("Reading from %d\n", x); + ret = _read_retry(x, &data, sizeof(data), &tv); if (ret != sizeof(data)) { @@ -314,6 +320,29 @@ serial_config(config_object_t *config, serial_info *args) args->uri = strdup(value); } + if (sc_get(config, "listeners/serial/@path", + value, sizeof(value)-1) == 0) { + dbg_printf(1, "Got %s for uri\n", value); + args->path = strdup(value); + } + + if (sc_get(config, "listeners/serial/@mode", + value, sizeof(value)-1) == 0) { + if (!strcasecmp(value, "vmchannel")) { + args->mode = 1; + } else if (!strcasecmp(value, "serial")) { + args->mode = 0; + } else { + args->mode = atoi(value); + if (args->mode < 0) + args->mode = 0; + } + + dbg_printf(1, "Got %s for mode\n", + args->mode?"VMChannel":"serial"); + + } + return errors; } @@ -347,7 +376,7 @@ serial_init(listener_context_t *c, const fence_callbacks_t *cb, info->magic = SERIAL_PLUG_MAGIC; info->history = history_init(check_history, 10, sizeof(fence_req_t)); *c = (listener_context_t)info; - start_event_listener(info->uri); + start_event_listener(info->uri, info->path, info->mode); sleep(1); return 0; @@ -366,6 +395,7 @@ serial_shutdown(listener_context_t c) stop_event_listener(); domain_sock_cleanup(); free(info->uri); + free(info->path); free(info); return 0; diff --git a/server/serial.h b/server/serial.h index 39c9540..ef11dd1 100644 --- a/server/serial.h +++ b/server/serial.h @@ -18,7 +18,7 @@ int static_map_check(void *info, const char *value1, const char *value2); int static_map_init(config_object_t *config, void **perm_info); /* virt-serial.c - event thread control functions */ -int start_event_listener(const char *uri); +int start_event_listener(const char *uri, const char *path, int mode); int stop_event_listener(void); diff --git a/server/virt-serial.c b/server/virt-serial.c index 681bba8..a135f12 100644 --- a/server/virt-serial.c +++ b/server/virt-serial.c @@ -185,13 +185,14 @@ myEventRemoveTimeoutFunc(int timer) static int -domainStarted(virDomainPtr mojaDomain) +domainStarted(virDomainPtr mojaDomain, const char *path, int mode) { char dom_uuid[42]; char *xml; xmlDocPtr doc; xmlNodePtr cur, devices, child, serial; xmlAttrPtr attr, attr_mode, attr_path; + size_t path_len = 0; if (!mojaDomain) return -1; @@ -200,6 +201,8 @@ domainStarted(virDomainPtr mojaDomain) xml = virDomainGetXMLDesc(mojaDomain, 0); // printf("%s\n", xml); + if (path) + path_len = strlen(path); // @todo: free mojaDomain @@ -229,7 +232,9 @@ domainStarted(virDomainPtr mojaDomain) for (child = devices->xmlChildrenNode; child != NULL; child = child->next) { - if (xmlStrcmp(child->name, (const xmlChar *) "serial")) { + + if ((!mode && xmlStrcmp(child->name, (const xmlChar *) "serial")) || + (mode && xmlStrcmp(child->name, (const xmlChar *) "channel"))) { continue; } @@ -252,13 +257,25 @@ domainStarted(virDomainPtr mojaDomain) attr_mode = xmlHasProp(serial, (const xmlChar *)"mode"); attr_path = xmlHasProp(serial, (const xmlChar *)"path"); - if ((attr_path != NULL) && - (attr_mode != NULL) && - (!xmlStrcmp(attr_mode->children->content, - (const xmlChar *) "bind"))) { - domain_sock_setup(dom_uuid, (const char *) - attr_path->children->content); + if (!attr_path || !attr_mode) + continue; + + if (xmlStrcmp(attr_mode->children->content, + (const xmlChar *) "bind")) + continue; + + if (path) { + if (xmlStrlen(attr_path->children->content) < + path_len) + continue; + if (strncmp(attr_path->children->content, + path, path_len)) + continue; } + + + domain_sock_setup(dom_uuid, (const char *) + attr_path->children->content); } } } @@ -268,7 +285,7 @@ domainStarted(virDomainPtr mojaDomain) } static int -registerExisting(virConnectPtr vp) +registerExisting(virConnectPtr vp, const char *path, int mode) { int *d_ids = NULL; int d_count, x; @@ -312,7 +329,7 @@ registerExisting(virConnectPtr vp) if (d_info.state != VIR_DOMAIN_SHUTOFF && d_info.state != VIR_DOMAIN_CRASHED) - domainStarted(dom); + domainStarted(dom, path, mode); virDomainFree(dom); } @@ -337,15 +354,28 @@ domainStopped(virDomainPtr mojaDomain) } +struct event_args { + char *uri; + char *path; + int mode; +}; + + static void * event_thread(void *arg) { + struct event_args *args = (struct event_args *)arg; virConnectPtr dconn = NULL; struct domain_info dinfo; - int sts; int callback1ret = -1; + int sts; - dbg_printf(3,"Event listener starting \n"); + dbg_printf(3, "Libvirt event listener starting\n"); + if (args->uri) + dbg_printf(3," * URI: %s\n", args->uri); + if (args->path) + dbg_printf(3," * Socket path: %s\n", args->path); + dbg_printf(3," * Mode: %s\n", args->mode?"VMChannel":"Serial"); virEventRegisterImpl(myEventAddHandleFunc, myEventUpdateHandleFunc, @@ -354,15 +384,15 @@ event_thread(void *arg) myEventUpdateTimeoutFunc, myEventRemoveTimeoutFunc); - dconn = virConnectOpen((char *)arg); + dconn = virConnectOpen(args->uri); if (!dconn) { dbg_printf(1, "Error connecting to libvirt\n"); - return NULL; + goto out; } DEBUG0("Registering domain event cbs"); - registerExisting(dconn); + registerExisting(dconn, args->path, args->mode); /* Add 2 callbacks to prove this works with more than just one */ memset(&dinfo, 0, sizeof (dinfo)); @@ -385,7 +415,7 @@ event_thread(void *arg) } if (dinfo.event == VIR_DOMAIN_EVENT_STARTED) { - domainStarted(dinfo.dom); + domainStarted(dinfo.dom, args->path, args->mode); virDomainFree(dinfo.dom); dinfo.dom = NULL; dinfo.event = VIR_DOMAIN_EVENT_UNDEFINED; @@ -408,7 +438,7 @@ event_thread(void *arg) if (pfd.revents & POLLHUP) { DEBUG0("Reset by peer"); - return NULL; + goto out; } if (h_cb) { @@ -428,26 +458,48 @@ event_thread(void *arg) dbg_printf(1, "error closing libvirt connection\n"); } +out: + free(args->uri); + free(args->path); + free(args); return NULL; } int -start_event_listener(const char *uri) +start_event_listener(const char *uri, const char *path, int mode) { - char *arg = NULL; + struct event_args *args = NULL; virInitialize(); + + args = malloc(sizeof(*args)); + if (!args) + return -1; + memset(args, 0, sizeof(*args)); if (uri) { - arg = strdup(uri); - if (uri == NULL) - return -1; + args->uri = strdup(uri); + if (args->uri == NULL) + goto out_fail; } + if (path) { + args->path = strdup(path); + if (args->path == NULL) + goto out_fail; + } + + args->mode = mode; run = 1; - return pthread_create(&event_tid, NULL, event_thread, arg); + return pthread_create(&event_tid, NULL, event_thread, args); + +out_fail: + free(args->uri); + free(args->path); + free(args); + return -1; }