• R/O
  • SSH

pm_kvm_tools: Commit

※ リポジトリは、https://github.com/linux-ha-japan/pm_kvm_tools-1.0 へ移行しました。
仮想環境監視連携機能とSTONITH機能。KVM版。


Commit MetaInfo

Revision441a96ed873fa1f33e8bae467a4fcbc81d50fbc5 (tree)
Time2011-09-15 15:27:04
AuthorKazunori INOUE <inouekazu@inte...>
CommiterKazunori INOUE

Log Message

Initial commit for pm_kvm_tools - tools of pacemaker for the KVM virtual environment

Change Summary

Incremental Difference

diff -r 000000000000 -r 441a96ed873f .hgignore
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgignore Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,96 @@
1+syntax: glob
2+
3+
4+# Autofoo entries
5+*.o
6+*.la
7+*.lo
8+*.loT
9+*.pyc
10+.libs
11+.deps
12+*.cache
13+*.upgrade.xml
14+.cvsignore
15+compile
16+configure
17+configure.status
18+configure.lineno
19+depcomp
20+aclocal.m4
21+libtool
22+ltmain.sh
23+ltconfig
24+libltdl
25+mkinstalldirs
26+install-sh
27+missing
28+py-compile
29+autom4te*
30+libtool.m4
31+ltdl.m4
32+libltdl.tar
33+autoconf
34+autoheader
35+automake
36+include/ha_version.h
37+ylwrap
38+
39+# BEAM Entries
40+*.beam
41+parser-messages
42+MISC_ERRORS
43+cscope.files
44+cscope.out
45+patches
46+updates
47+logs
48+
49+# OS and Editor Artifacts
50+.DS_Store
51+.bomb
52+*.rej
53+*.bz2
54+*.sed
55+*.diff
56+*.patch
57+*.gres
58+*~
59+
60+# Entries generated by configure
61+include/stamp-h1
62+include/stamp-h2
63+include/config.h
64+include/config.h.in
65+pm_kvm_tools.spec
66+
67+# Project build targets
68+tools/vm-connect
69+tools/vm-connectd
70+tools/vm-stonithd
71+tools/vm-managerd
72+
73+# Misc
74+HTML
75+TAGS
76+GPATH
77+GRTAGS
78+GSYMS
79+GTAGS
80+.gres.*
81+*.orig
82+.gdb_history
83+*~
84+\#*
85+.changes
86+mock
87+*.src.rpm
88+
89+# Entries better done as regexp's to avoid matching too broadly
90+syntax: regexp
91+^config\.*
92+^doc/.*/tmp
93+^doc/.*/publish
94+README$
95+Makefile$
96+Makefile.in$
diff -r 000000000000 -r 441a96ed873f Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile.am Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,29 @@
1+#
2+# Makefile.am for pm_kvm_tools
3+#
4+
5+MAINTAINERCLEANFILES = Makefile.in aclocal.m4 configure libtool.m4 ltdl.m4 libltdl.tar
6+
7+SUBDIRS = include lib tools plugins resources conf
8+
9+SPEC = $(PACKAGE_NAME).spec
10+TARFILE = $(PACKAGE_NAME)-$(VERSION).tar.gz
11+EXTRA_DIST = $(SPEC) autogen.sh include/vm_connect.h \
12+ conf/vm-manager.conf conf/vm-connectd.conf.sample
13+
14+$(TARFILE):
15+ $(MAKE) dist
16+
17+RPM_ROOT = $(shell pwd)
18+RPMBUILDOPTS = --define "_sourcedir $(RPM_ROOT)" \
19+ --define "_specdir $(RPM_ROOT)"
20+
21+srpm: clean
22+ rm -f $(TARFILE)
23+ $(MAKE) $(SPEC) $(TARFILE)
24+ rpmbuild $(RPMBUILDOPTS) --nodeps -bs --rmsource $(SPEC)
25+
26+rpm: clean
27+ rm -f $(TARFILE)
28+ $(MAKE) $(SPEC) $(TARFILE)
29+ rpmbuild $(RPMBUILDOPTS) -ba --rmsource $(SPEC)
diff -r 000000000000 -r 441a96ed873f autogen.sh
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/autogen.sh Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,5 @@
1+#!/bin/sh
2+# Run this to generate all the initial makefiles, etc.
3+
4+echo Building configuration system...
5+autoreconf -i && echo Now run ./configure and make
diff -r 000000000000 -r 441a96ed873f conf/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/conf/Makefile.am Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,7 @@
1+MAINTAINERCLEANFILES = Makefile.in
2+
3+confdir = ${sysconfdir}
4+conf_DATA = vm-manager.conf
5+
6+initconfdir = ${sysconfdir}/init
7+initconf_DATA = vm-connectd.conf.sample
diff -r 000000000000 -r 441a96ed873f conf/vm-connectd.conf.sample
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/conf/vm-connectd.conf.sample Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,11 @@
1+# vm-connectd
2+#
3+# Starts vm-connectd included in pm_kvm_tools package,
4+# it's for GUEST environment.
5+
6+start on runlevel [2345]
7+
8+env HA_logfacility=local1
9+
10+respawn
11+exec /usr/sbin/vm-connectd -t guest
diff -r 000000000000 -r 441a96ed873f conf/vm-manager.conf
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/conf/vm-manager.conf Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,16 @@
1+[default_ping_set]
2+red = eq 0
3+yellow = lt 100
4+green = eq 100
5+
6+[diskcheck_status]
7+red = eq ERROR
8+green = eq normal
9+
10+[diskcheck_status_internal]
11+red = eq ERROR
12+green = eq normal
13+
14+[operator_check_status]
15+green = eq normal
16+red = eq ERROR
diff -r 000000000000 -r 441a96ed873f include/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/Makefile.am Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,1 @@
1+MAINTAINERCLEANFILES = Makefile.in config.h.in
diff -r 000000000000 -r 441a96ed873f include/vm_connect.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/vm_connect.h Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,62 @@
1+/*
2+ * vm_connect : Communication routines for the pm_kvm_tools.
3+ *
4+ * Copyright (C) 2010 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
5+ *
6+ * This program is free software; you can redistribute it and/or
7+ * modify it under the terms of the GNU General Public
8+ * License as published by the Free Software Foundation; either
9+ * version 2.1 of the License, or (at your option) any later version.
10+ *
11+ * This software is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+ * General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public
17+ * License along with this library; if not, write to the Free Software
18+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+ */
20+
21+#ifndef VM_CONNECT_H
22+#define VM_CONNECT_H
23+
24+#include <config.h>
25+#include <glib.h>
26+
27+#define SOCK_PATH "/var/run/vmconnectd.sock"
28+#define GUEST_SOCKDIR "/var/lib/libvirt/qemu"
29+#define SCD_PATH "/dev/virtio-ports"
30+#define SCD_NAME "vmconnectd"
31+
32+typedef enum msgtype_s { /* sequence of elements has to start from 0 */
33+ T_MOD_MONITOR = 0, /* status monitor (server) module */
34+ T_MOD_STONITH, /* STONITH function (server) module */
35+ T_MIGRATION_OCCURRED,
36+ T_CLIENT_NOT_CONNECT
37+} msgtype;
38+
39+typedef struct msginfo_s
40+{
41+ msgtype type;
42+ char id[64]; /* message ID */
43+ int sock_client; /* endpoint with client process */
44+ int sock_guest; /* endpoint with guest */
45+ guint datalen;
46+} msginfo;
47+
48+typedef struct vm_message_s
49+{
50+ msginfo info;
51+ char *data;
52+} vm_message;
53+
54+int listen_to(const char *sock_path);
55+int connect_to(const char *sock_path, msgtype type);
56+int send_message(int sockfd, msgtype type, const char *msgid, const char *data);
57+int send_msg(int sockfd, const vm_message *msg);
58+int receive_msg(int sockfd, vm_message *msg);
59+void on_migration_occurred(void);
60+gboolean on_msg_arrived(GIOChannel *channel, GIOCondition condition, gpointer unused);
61+
62+#endif /* VM_CONNECT_H */
diff -r 000000000000 -r 441a96ed873f lib/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/Makefile.am Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,13 @@
1+MAINTAINERCLEANFILES = Makefile.in
2+
3+INCLUDES = -I/usr/include/pacemaker \
4+ -I/usr/include/glib-2.0 \
5+ -I$(libdir)/glib-2.0/include \
6+ -I/usr/include/libxml2
7+
8+lib_LTLIBRARIES = libvmconnect.la
9+libvmconnect_la_SOURCES = vm_connect.c
10+libvmconnect_la_LDFLAGS = -version-info 1:0:0
11+libvmconnect_la_LDFLAGS += -lplumb -lcrmcommon
12+
13+AM_CFLAGS = -Wall -Werror -fPIC
diff -r 000000000000 -r 441a96ed873f lib/vm_connect.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/vm_connect.c Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,413 @@
1+/*
2+ * vm_connect : Communication routines for the pm_kvm_tools.
3+ *
4+ * Copyright (C) 2010 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
5+ *
6+ * This program is free software; you can redistribute it and/or
7+ * modify it under the terms of the GNU General Public
8+ * License as published by the Free Software Foundation; either
9+ * version 2.1 of the License, or (at your option) any later version.
10+ *
11+ * This software is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+ * General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public
17+ * License along with this library; if not, write to the Free Software
18+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+ */
20+
21+#include <stdio.h>
22+#include <unistd.h>
23+#include <sys/un.h>
24+#include <errno.h>
25+#include <crm/crm.h>
26+#include <vm_connect.h>
27+
28+GHashTable *io_watch; /* GHashTable indexed by sockfd */
29+int sock_server[2]; /* endpoints with server process */
30+int sock_host; /* endpoint with host */
31+gboolean on_host = TRUE;
32+
33+static void
34+vm_connect_free(gpointer free_obj)
35+{
36+ g_free(free_obj);
37+ free_obj = NULL;
38+
39+ return;
40+}
41+
42+static int
43+deliver_msg(int sockfd, const vm_message *msg)
44+{
45+ ssize_t size;
46+ gpointer tmpmsg;
47+
48+ crm_debug_2("called..");
49+ crm_malloc0(tmpmsg, sizeof(msginfo)+msg->info.datalen+1);
50+ memcpy(tmpmsg, msg, sizeof(msginfo));
51+ memcpy(tmpmsg+sizeof(msginfo), msg->data, msg->info.datalen);
52+ size = write(sockfd, tmpmsg, sizeof(msginfo)+msg->info.datalen);
53+ crm_free(tmpmsg);
54+ if (size < 0) {
55+ cl_perror("write(2) call failed");
56+ return -1;
57+ }
58+ return 0;
59+}
60+
61+gboolean
62+on_msg_arrived(GIOChannel *channel, GIOCondition condition, gpointer unused)
63+{
64+ int sockfd;
65+ int rc;
66+ crm_debug_2("called..");
67+ crm_debug_3("condition is [%d]", condition);
68+ sockfd = g_io_channel_unix_get_fd(channel);
69+ crm_debug_3("on message socket [%d]", sockfd);
70+
71+ if (condition & G_IO_IN) {
72+ vm_message msg;
73+
74+ rc = receive_msg(sockfd, &msg);
75+ if (rc < 0 || rc == 1) {
76+ g_io_channel_unref(channel);
77+ return FALSE;
78+ }
79+
80+ rc = 0;
81+ if (on_host) {
82+ if (msg.info.sock_guest) {
83+ /* send to guest */
84+ crm_debug("deliver msg socket [%d] => socket [%d]",
85+ sockfd, msg.info.sock_guest);
86+ rc = deliver_msg(msg.info.sock_guest, &msg);
87+ if(rc < 0) {
88+ /* ゲストへの配送に失敗 */
89+ crm_err("failed to deliver message to guest socket [%d]",
90+ msg.info.sock_guest);
91+ }
92+ }
93+ else {
94+ if (msg.info.sock_client) {
95+ /* send to client on host */
96+ msg.info.sock_guest = sockfd;
97+ /*
98+ * 対象のクライアントが接続されていない場合再送を要求
99+ * ゲストとの接続は保持
100+ */
101+ if(sock_server[msg.info.type] == 0) {
102+ crm_err("client is not connected");
103+ crm_free(msg.data);
104+ msg.info.datalen = 0;
105+ msg.info.type = T_CLIENT_NOT_CONNECT;
106+ rc = deliver_msg(msg.info.sock_guest, &msg);
107+ return TRUE;
108+ }
109+ crm_debug("deliver msg socket [%d] => socket [%d]",
110+ sockfd, sock_server[msg.info.type]);
111+ rc = deliver_msg(sock_server[msg.info.type], &msg);
112+ if(rc < 0) {
113+ /* ホスト上のクライアントへの配送に失敗 */
114+ crm_err("failed to deliver message to client [%d]"
115+ " on the host", sock_server[msg.info.type]);
116+ }
117+ }
118+ else {
119+ /* クライアントが接続してきたとき */
120+ sock_server[msg.info.type] = sockfd;
121+ switch(msg.info.type) {
122+ case T_MOD_MONITOR:
123+ crm_info("vm-managerd is connected.");
124+ break;
125+ case T_MOD_STONITH:
126+ crm_info("vm-stonithd is connected.");
127+ break;
128+ default:
129+ break;
130+ }
131+ }
132+ }
133+ }
134+ else {
135+ /*
136+ * on guest
137+ */
138+ if (msg.info.type == T_MIGRATION_OCCURRED) {
139+ on_migration_occurred();
140+ return TRUE;
141+ }
142+ if (msg.info.sock_client) {
143+ /* send to client on guest */
144+ crm_debug("deliver msg socket [%d] => socket [%d]",
145+ sockfd, msg.info.sock_client);
146+ rc = deliver_msg(msg.info.sock_client, &msg);
147+ if(rc < 0) {
148+ /*
149+ * ゲスト上のクライアントへの配送に失敗
150+ */
151+ crm_debug("failed to deliver message to client [%d]"
152+ " on the guest", msg.info.sock_client);
153+ }
154+ }
155+ else {
156+ msg.info.sock_client = sockfd;
157+ /* send to hypervisor */
158+ crm_debug("deliver msg socket [%d] => socket [%d]",
159+ sockfd, sock_host);
160+ rc = deliver_msg(sock_host, &msg);
161+ if(rc < 0) {
162+ /* ホストへの配送に失敗 */
163+ crm_err("failed to deliver message to host socket [%d]",
164+ sock_host);
165+ }
166+ }
167+ }
168+ crm_free(msg.data);
169+
170+ }
171+ else if (condition & G_IO_ERR) {
172+ crm_debug_3("G_IO_ERR");
173+ }
174+ else if (condition & G_IO_HUP) {
175+ crm_debug_3("G_IO_HUP");
176+ if(!on_host) {
177+ on_migration_occurred();
178+ }
179+ crm_info("close connection with the socket [%d].", sockfd);
180+ close(sockfd);
181+ g_io_channel_unref(channel);
182+ return FALSE;
183+ }
184+
185+ return TRUE;
186+}
187+
188+/*
189+ * called when there is message to receive.
190+ */
191+static gboolean
192+on_listen(GIOChannel *channel, GIOCondition condition, gpointer unused)
193+{
194+ crm_debug_2("called...");
195+
196+ if (condition & G_IO_IN) {
197+ int *sockfd = g_new(int, 1);
198+ struct sockaddr_un addr;
199+ socklen_t addrlen = sizeof(addr);
200+ guint *sourceid = g_new(guint, 1);
201+
202+ *sockfd = accept(g_io_channel_unix_get_fd(channel),
203+ (struct sockaddr*)&addr, &addrlen);
204+ if (*sockfd < 0) {
205+ cl_perror("accept(2) call failed");
206+ return TRUE;
207+ }
208+ crm_debug_3("accept a client connection, socket [%d] on %s",
209+ *sockfd, on_host ? "host" : "guest");
210+ *sourceid = g_io_add_watch_full(g_io_channel_unix_new(*sockfd),
211+ G_PRIORITY_DEFAULT, G_IO_IN|G_IO_ERR|G_IO_HUP,
212+ on_msg_arrived, NULL, NULL);
213+ crm_debug_4("insert io watch source id [%d]", *sourceid);
214+ g_hash_table_insert(io_watch, sockfd, sourceid);
215+ }
216+ else if (condition & G_IO_HUP) {
217+ crm_debug_3("G_IO_HUP");
218+ }
219+ return TRUE;
220+}
221+
222+/*
223+ * create socket and listen for waiting message.
224+ */
225+int
226+listen_to(const char *sock_path)
227+{
228+ int sockfd;
229+ int rc;
230+ struct sockaddr_un addr;
231+
232+ crm_debug_2("called..");
233+
234+ sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
235+ if (sockfd < 0) {
236+ cl_perror("socket(2) call failed");
237+ return -1;
238+ }
239+ memset(&addr, 0, sizeof(struct sockaddr_un));
240+ addr.sun_family = AF_UNIX;
241+ g_strlcpy(addr.sun_path, sock_path, sizeof(addr.sun_path)-1);
242+
243+ unlink(sock_path);
244+ rc = bind(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un));
245+ if (rc < 0) {
246+ cl_perror("bind(2) call failed");
247+ goto cleanup_close;
248+ }
249+ rc = listen(sockfd, SOMAXCONN);
250+ if (rc < 0) {
251+ cl_perror("listen(2) call failed");
252+ goto cleanup_close;
253+ }
254+
255+ /* create source for socket and add to the mainloop */
256+ g_io_add_watch_full(g_io_channel_unix_new(sockfd),
257+ G_PRIORITY_DEFAULT, G_IO_IN|G_IO_HUP, on_listen, NULL, NULL);
258+
259+ io_watch = g_hash_table_new_full(g_int_hash, g_int_equal, vm_connect_free, vm_connect_free);
260+ return sockfd;
261+
262+cleanup_close:
263+ close(sockfd);
264+ return -1;
265+}
266+
267+int
268+connect_to(const char *sock_path, msgtype type)
269+{
270+ int sockfd;
271+ int rc;
272+ struct sockaddr_un addr;
273+
274+ crm_debug_2("called..");
275+
276+ sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
277+ if (sockfd < 0) {
278+ cl_perror("socket(2) call failed");
279+ return -1;
280+ }
281+ memset(&addr, 0, sizeof(struct sockaddr_un));
282+ addr.sun_family = AF_UNIX;
283+ g_strlcpy(addr.sun_path, sock_path, sizeof(addr.sun_path)-1);
284+
285+ rc = connect(sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un));
286+ if (rc < 0) {
287+ cl_perror("connect(2) call failed");
288+ close(sockfd);
289+ return -1;
290+ }
291+
292+ /* デーモンからの接続時ソケットをタイプ別にsock_serverに保存 */
293+ if (on_host == TRUE) {
294+ rc = send_message(sockfd, type, NULL, NULL);
295+ if (rc < 0) {
296+ close(sockfd);
297+ return -1;
298+ }
299+ }
300+ return sockfd;
301+}
302+
303+int
304+send_message(int sockfd, msgtype type, const char *msgid, const char *data)
305+{
306+ vm_message msg;
307+
308+ crm_debug_2("called..");
309+
310+ memset(&msg, 0, sizeof(vm_message));
311+ msg.info.type = type;
312+ if (msgid) {
313+ g_strlcpy(msg.info.id, msgid, sizeof(msg.info.id)-1);
314+ }
315+ if (data) {
316+ msg.info.datalen = strlen(data);
317+ msg.data = (char*)data;
318+ }
319+ return deliver_msg(sockfd, &msg);
320+}
321+
322+int
323+send_msg(int sockfd, const vm_message *msg)
324+{
325+ crm_debug_2("called..");
326+ return deliver_msg(sockfd, msg);
327+}
328+
329+/*
330+ * Returns array of pointer [msg->data], call free() after use.
331+ * return:
332+ * 0: success - message was received
333+ * 1: if socket was closed
334+ * 2: if message (LiveMigration occurred) was received
335+ * -1: if system call error occurred
336+ */
337+int
338+receive_msg(int sockfd, vm_message *msg)
339+{
340+ int rc, i;
341+
342+ crm_debug_2("called..");
343+
344+ memset(msg, 0, sizeof(vm_message));
345+
346+ rc = read(sockfd, msg, sizeof(msginfo));
347+ if (rc < 0) {
348+ cl_perror("read(2) call failed");
349+ return -1;
350+ }
351+ else if (rc == 0) {
352+ /* EOF -> closed socket */
353+ for(i=0; i<2; i++) {
354+ /* remove client socket info */
355+ if(sock_server[i] == sockfd) {
356+ sock_server[i] = 0;
357+ }
358+ }
359+
360+ /* close client socket */
361+ close(sockfd);
362+ crm_debug("closed socket [%d]", sockfd);
363+
364+ if (io_watch != NULL) {
365+ gpointer sourceid = g_hash_table_lookup(io_watch, (gpointer*)&sockfd);
366+ if (sourceid != NULL) {
367+ crm_debug_4("remove io watch source id [%d]", *(guint*)sourceid);
368+ g_source_remove(*(guint*)sourceid);
369+ g_hash_table_remove(io_watch, (gpointer*)&sockfd);
370+ }
371+ }
372+ return 1;
373+ }
374+ crm_debug_3("read(%d, hdr): [%d:%s:%u]",
375+ sockfd, msg->info.type, msg->info.id, msg->info.datalen);
376+ if (msg->info.type == T_MIGRATION_OCCURRED || msg->info.type == T_CLIENT_NOT_CONNECT)
377+ return 2;
378+ if (msg->info.datalen > 0) {
379+ crm_malloc0(msg->data, msg->info.datalen+1);
380+ rc = read(sockfd, msg->data, msg->info.datalen);
381+ if (rc < 0) {
382+ cl_perror("read(2) call failed");
383+ g_free(msg->data);
384+ return -1;
385+ }
386+ crm_debug_3("read(%d, data): [%s]", sockfd, msg->data);
387+ }
388+ return 0;
389+}
390+
391+static void
392+send_migration_occurred(gpointer key, gpointer value, gpointer unused)
393+{
394+ crm_debug_2("called..");
395+
396+ send_message(*(int*)key, T_MIGRATION_OCCURRED, NULL, NULL);
397+ return;
398+}
399+
400+/*
401+ * this function which should be called when connection with host is lost
402+ * (SIGHUP) on guest.
403+ */
404+void
405+on_migration_occurred(void)
406+{
407+ crm_debug_2("called..");
408+
409+ crm_debug_3("io_watch's size: %d", g_hash_table_size(io_watch));
410+ g_hash_table_foreach(io_watch, send_migration_occurred, NULL);
411+ return;
412+}
413+
diff -r 000000000000 -r 441a96ed873f plugins/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/Makefile.am Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,6 @@
1+MAINTAINERCLEANFILES = Makefile.in
2+
3+EXTRA_DIST = $(ext_SCRIPTS)
4+
5+extdir = $(stonith_ext_plugindir)
6+ext_SCRIPTS = vm-stonith
diff -r 000000000000 -r 441a96ed873f plugins/vm-stonith
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/vm-stonith Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,160 @@
1+#!/bin/sh
2+#
3+# External STONITH module for vm-stonith.
4+#
5+# Copyright (c) 2010 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
6+#
7+# This program is free software; you can redistribute it and/or modify
8+# it under the terms of version 2 of the GNU General Public License as
9+# published by the Free Software Foundation.
10+#
11+# This program is distributed in the hope that it would be useful, but
12+# WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14+#
15+# Further, this software is distributed without any warranty that it is
16+# free of the rightful claim of any third person regarding infringement
17+# or the like. Any license provided herein, whether implied or
18+# otherwise, applies only to this software file. Patent licenses, if
19+# any, provided herein do not apply to combinations of this program with
20+# other software, or any other product whatsoever.
21+#
22+# You should have received a copy of the GNU General Public License
23+# along with this program; if not, write the Free Software Foundation,
24+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
25+#
26+CONNCMD="/usr/sbin/vm-connect -t stonith -i `uuidgen`"
27+OPTIMEOUT="timeout"
28+
29+check_delimeter() {
30+ delimeter_default=':'
31+ : ${delimeter=$delimeter_default}
32+ if [ "$delimeter" = " " ]; then
33+ ha_log.sh err "Invalid delimeter [$delimeter]."
34+ exit 6 #ERR_CONFIGURED
35+ fi
36+}
37+
38+trap_handler() {
39+ ha_log.sh info "Request: target=$host, op=$OPTIMEOUT"
40+ ha_log.sh debug "$CONNCMD -R \"$OPTIMEOUT\""
41+ $CONNCMD -R "$OPTIMEOUT"
42+ exit 1
43+}
44+trap trap_handler TERM
45+
46+ha_log.sh debug "\$*: [$*]"
47+case $1 in
48+gethosts)
49+ check_delimeter
50+ for h in $hostlist; do
51+ echo $h | awk -F $delimeter '{print $1}'
52+ done
53+ exit 0
54+ ;;
55+on|off|reset|status)
56+ if [ "x$hostlist" = "x" ]; then
57+ ha_log.sh err "hostlist isn't set."
58+ exit 6 #ERR_CONFIGURED
59+ fi
60+ check_delimeter
61+
62+ target=""
63+ if [ "x$2" != "x" ]; then
64+ target=`echo $2 | tr A-Z a-z`
65+ fi
66+
67+ for h in $hostlist; do
68+ host=`echo $h | awk -F $delimeter '{print $1}' | tr A-Z a-z`
69+ rsc=`echo $h | awk -F $delimeter '{print $2}'`
70+
71+ if [ "$1" != "status" -a "$target" != "$host" ]; then
72+ continue
73+ fi
74+
75+ while true; do
76+ ha_log.sh info "Request: target=$host, op=$1"
77+ ha_log.sh debug "$CONNCMD -r \"$1 $rsc\""
78+ res=`$CONNCMD -r "$1 $rsc" 2>/dev/null`
79+ rc=$?
80+ ha_log.sh debug "rc [$rc]"
81+ if [ $rc -eq 2 ]; then
82+ ha_log.sh notice "request failed."
83+ sleep 1
84+ continue
85+ elif [ $rc -ne 0 ]; then
86+ ha_log.sh err "request failed."
87+ exit 1
88+ fi
89+
90+ ha_log.sh info "Result: $res"
91+ if [ "$res" = "OK" ]; then
92+ if [ "$1" = "status" ]; then
93+ break
94+ else
95+ exit 0
96+ fi
97+ else
98+ exit 1
99+ fi
100+ done
101+ done
102+ if [ "$1" = "status" ]; then
103+ exit 0
104+ else
105+ exit 1
106+ fi
107+ ;;
108+getconfignames)
109+ echo "hostlist delimeter"
110+ exit 0
111+ ;;
112+getinfo-devid)
113+ echo "vm-stonith STONITH device"
114+ exit 0
115+ ;;
116+getinfo-devname)
117+ echo "vm-stonith STONITH external device"
118+ exit 0
119+ ;;
120+getinfo-devdescr)
121+ echo "Allows STONITH to control guests managed by a CRM/Pacemaker host."
122+ echo "Requires VM + CRM/Pacemaker at both layers."
123+ exit 0
124+ ;;
125+getinfo-devurl)
126+ echo "Virtio-Serial -> http://fedoraproject.org/wiki/Features/VirtioSerial"
127+ exit 0
128+ ;;
129+getinfo-xml)
130+ cat <<VMSTONITHXML
131+<parameters>
132+<parameter name="hostlist" unique="1" required="1">
133+<content type="string" />
134+<shortdesc lang="en">
135+Host Map
136+</shortdesc>
137+<longdesc lang="en">
138+A mapping of hostname and resource ID supported by this device.
139+For example: "guest-a1:encrypted-rscid guest-a2:encrypted-rscid"
140+ * encrypted-rscid : encrypted resource ID of the virtual machine managed by the cluster of host.
141+</longdesc>
142+</parameter>
143+<parameter name="delimeter" unique="0" required="0">
144+<content type="string" />
145+<shortdesc lang="en">
146+Delimeter of hostname and resource ID
147+</shortdesc>
148+<longdesc lang="en">
149+The delimiter of the hostname and resource ID in hostlist parameter.
150+(The space character cannot be specified.)
151+</longdesc>
152+</parameter>
153+</parameters>
154+VMSTONITHXML
155+ exit 0
156+ ;;
157+*)
158+ exit 1
159+ ;;
160+esac
diff -r 000000000000 -r 441a96ed873f pm_kvm_tools.spec.in
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pm_kvm_tools.spec.in Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,100 @@
1+########################################
2+# Derived definitions
3+########################################
4+%define __check_files %{nil}
5+%define name pm_kvm_tools
6+%define version @VERSION@
7+%define release 1.el6
8+%define prefix /usr
9+%define ORGARCH pm_kvm_tools-%{version}
10+#
11+#
12+Summary: Applications of pacemaker for the KVM virtual environment.
13+Name: %{name}
14+Version: %{version}
15+Release: %{release}
16+Group: Applications
17+Source: %{ORGARCH}.tar.gz
18+License: GPL/LGPL
19+Vendor: NIPPON TELEGRAPH AND TELEPHONE CORPORATION
20+BuildRoot: %{_tmppath}/%{name}-%{version}
21+BuildRequires: autoconf, automake libtool pacemaker-libs-devel
22+Requires: pacemaker >= 1.0.9
23+
24+########################################
25+%description
26+########################################
27+This package contains the following applications of pacemaker for the KVM virtual environment.
28+ vm-manager : Status monitor for virtual environment.
29+ vm-stonith : STONITH function for virtual environment.
30+
31+########################################
32+%prep
33+########################################
34+rm -rf $RPM_BUILD_ROOT
35+%setup -q -n %{ORGARCH}
36+pushd $RPM_BUILD_DIR/%{ORGARCH}
37+./autogen.sh
38+./configure
39+popd
40+
41+########################################
42+%build
43+########################################
44+pushd $RPM_BUILD_DIR/%{ORGARCH}
45+make DESTDIR=$RPM_BUILD_ROOT
46+popd
47+
48+########################################
49+%install
50+########################################
51+pushd $RPM_BUILD_DIR/%{ORGARCH}
52+make DESTDIR=$RPM_BUILD_ROOT install
53+popd
54+
55+########################################
56+%clean
57+########################################
58+if
59+ [ -n "${RPM_BUILD_ROOT}" -a "${RPM_BUILD_ROOT}" != "/" ]
60+then
61+ rm -rf $RPM_BUILD_ROOT
62+fi
63+rm -rf $RPM_BUILD_DIR/%{ORGARCH}
64+
65+########################################
66+%pre
67+########################################
68+true
69+
70+########################################
71+%post
72+########################################
73+true
74+
75+########################################
76+%preun
77+########################################
78+true
79+
80+########################################
81+%postun
82+########################################
83+true
84+
85+########################################
86+%files
87+########################################
88+%defattr(-,root,root)
89+%{_libdir}/libvmconnect.so*
90+%{_sbindir}/vm-connectd
91+%{_sbindir}/vm-connect
92+%{_sbindir}/vm-managerd
93+%{_sbindir}/vm-stonithd
94+@stonith_ext_plugindir@/vm-stonith
95+%dir @OCF_RA_DIR@/extra
96+@OCF_RA_DIR@/extra/VirtualDomain
97+@OCF_RA_DIR@/extra/vm-anything
98+@OCF_RA_DIR@/extra/vm-client
99+%config %{_sysconfdir}/vm-manager.conf
100+%config %{_sysconfdir}/init/vm-connectd.conf.sample
diff -r 000000000000 -r 441a96ed873f resources/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/Makefile.am Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,6 @@
1+MAINTAINERCLEANFILES = Makefile.in
2+
3+EXTRA_DIST = $(ocf_SCRIPTS)
4+
5+ocfdir = $(OCF_RA_DIR)/extra
6+ocf_SCRIPTS = VirtualDomain vm-anything vm-client
diff -r 000000000000 -r 441a96ed873f resources/VirtualDomain
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/VirtualDomain Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,477 @@
1+#!/bin/sh
2+#
3+# Support: linux-ha@lists.linux-ha.org
4+# License: GNU General Public License (GPL)
5+#
6+# Resource Agent for domains managed by the libvirt API.
7+# Requires a running libvirt daemon (libvirtd).
8+#
9+# (c) 2008-2010 Florian Haas, Dejan Muhamedagic,
10+# and Linux-HA contributors
11+#
12+# usage: $0 {start|stop|status|monitor|migrate_to|migrate_from|meta-data|validate-all}
13+#
14+#######################################################################
15+# Initialization:
16+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
17+. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
18+
19+# Defaults
20+OCF_RESKEY_force_stop_default=0
21+OCF_RESKEY_hypervisor_default="$(virsh --quiet uri)"
22+
23+: ${OCF_RESKEY_force_stop=${OCF_RESKEY_force_stop_default}}
24+: ${OCF_RESKEY_hypervisor=${OCF_RESKEY_hypervisor_default}}
25+#######################################################################
26+
27+## I'd very much suggest to make this RA use bash,
28+## and then use magic $SECONDS.
29+## But for now:
30+NOW=$(date +%s)
31+
32+usage() {
33+ echo "usage: $0 {start|stop|status|monitor|migrate_to|migrate_from|meta-data|validate-all}"
34+}
35+
36+meta_data() {
37+ cat <<EOF
38+<?xml version="1.0"?>
39+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
40+<resource-agent name="VirtualDomain">
41+<version>1.0</version>
42+
43+<longdesc lang="en">
44+Resource agent for a virtual domain (a.k.a. domU, virtual machine,
45+virtual environment etc., depending on context) managed by libvirtd.
46+</longdesc>
47+<shortdesc lang="en">Manages virtual domains through the libvirt virtualization framework</shortdesc>
48+
49+<parameters>
50+
51+<parameter name="config" unique="1" required="1">
52+<longdesc lang="en">
53+Absolute path to the libvirt configuration file,
54+for this virtual domain.
55+</longdesc>
56+<shortdesc lang="en">Virtual domain configuration file</shortdesc>
57+<content type="string" default="" />
58+</parameter>
59+
60+<parameter name="hypervisor" unique="0" required="0">
61+<longdesc lang="en">
62+Hypervisor URI to connect to. See the libvirt documentation for
63+details on supported URI formats. The default is system dependent.
64+</longdesc>
65+<shortdesc lang="en">Hypervisor URI</shortdesc>
66+<content type="string" default="${OCF_RESKEY_hypervisor_default}"/>
67+</parameter>
68+
69+<parameter name="force_stop" unique="0" required="0">
70+<longdesc lang="en">
71+Always forcefully shut down ("destroy") the domain on stop. The default
72+behavior is to resort to a forceful shutdown only after a graceful
73+shutdown attempt has failed. You should only set this to true if
74+your virtual domain (or your virtualization backend) does not support
75+graceful shutdown.
76+</longdesc>
77+<shortdesc lang="en">Always force shutdown on stop</shortdesc>
78+<content type="boolean" default="${OCF_RESKEY_force_stop_default}" />
79+</parameter>
80+
81+<parameter name="migration_transport" unique="0" required="0">
82+<longdesc lang="en">
83+Transport used to connect to the remote hypervisor while
84+migrating. Please refer to the libvirt documentation for details on
85+transports available. If this parameter is omitted, the resource will
86+use libvirt's default transport to connect to the remote hypervisor.
87+</longdesc>
88+<shortdesc lang="en">Remote hypervisor transport</shortdesc>
89+<content type="string" default="" />
90+</parameter>
91+
92+<parameter name="monitor_scripts" unique="0" required="0">
93+<longdesc lang="en">
94+To additionally monitor services within the virtual domain, add this
95+parameter with a list of scripts to monitor.
96+
97+Note: when monitor scripts are used, the start and migrate_from operations
98+will complete only when all monitor scripts have completed successfully.
99+Be sure to set the timeout of these operations to accommodate this delay.
100+</longdesc>
101+<shortdesc lang="en">space-separated list of monitor scripts</shortdesc>
102+<content type="string" default="" />
103+</parameter>
104+
105+</parameters>
106+
107+<actions>
108+<action name="start" timeout="90" />
109+<action name="stop" timeout="90" />
110+<action name="status" depth="0" timeout="30" interval="10" />
111+<action name="monitor" depth="0" timeout="30" interval="10" />
112+<action name="migrate_from" timeout="60" />
113+<action name="migrate_to" timeout="120" />
114+<action name="meta-data" timeout="5" />
115+<action name="validate-all" timeout="5" />
116+</actions>
117+</resource-agent>
118+EOF
119+}
120+
121+# Set options to be passed to virsh:
122+VIRSH_OPTIONS="--connect=${OCF_RESKEY_hypervisor} --quiet"
123+
124+# A state file where we record the domain name:
125+STATEFILE="${HA_RSCTMP}/VirtualDomain-${OCF_RESOURCE_INSTANCE}.state"
126+
127+VirtualDomain_Define() {
128+ local virsh_output
129+ local domain_name
130+ # Note: passing in the domain name from outside the script is
131+ # intended for testing and debugging purposes only. Don't do this
132+ # in production, instead let the script figure out the domain name
133+ # from the config file. You have been warned.
134+ if [ -z "$DOMAIN_NAME" ]; then
135+ # Spin until we have a domain name
136+ while true; do
137+ virsh_output=`virsh ${VIRSH_OPTIONS} define ${OCF_RESKEY_config}`
138+ domain_name=`echo "$virsh_output" | sed -e 's/Domain \(.*\) defined from .*$/\1/'`
139+ if [ -n $domain_name ]; then
140+ break;
141+ fi
142+ ocf_log debug "Domain not defined yet, probably unable to connect to hypervisor. Retrying."
143+ sleep 1
144+ done
145+ echo "$domain_name" > $STATEFILE
146+ ocf_log info "Domain name \"$domain_name\" saved to $STATEFILE."
147+ else
148+ ocf_log warn "Domain name ${DOMAIN_NAME} already defined, overriding configuration file ${OCF_RESKEY_config}. You should do this for testing only."
149+ fi
150+}
151+
152+VirtualDomain_Cleanup_Statefile() {
153+ rm -f $STATEFILE || ocf_log warn "Failed to remove $STATEFILE during $__OCF_ACTION."
154+}
155+
156+VirtualDomain_Status() {
157+ local try=0
158+ rc=$OCF_ERR_GENERIC
159+ status="no state"
160+ while [ "$status" = "no state" ]; do
161+ try=$(($try + 1 ))
162+ status="`virsh $VIRSH_OPTIONS domstate $DOMAIN_NAME`"
163+ case "$status" in
164+ "shut off")
165+ # shut off: domain is defined, but not started
166+ ocf_log debug "Virtual domain $DOMAIN_NAME is currently $status."
167+ rc=$OCF_NOT_RUNNING
168+ ;;
169+ running|paused|idle|blocked)
170+ # running: domain is currently actively consuming cycles
171+ # paused: domain is paused (suspended)
172+ # idle: domain is running but idle
173+ # blocked: synonym for idle used by legacy Xen versions
174+ ocf_log debug "Virtual domain $DOMAIN_NAME is currently $status."
175+ rc=$OCF_SUCCESS
176+ ;;
177+ ""|"no state")
178+ # Empty string may be returned when virsh does not
179+ # receive a reply from libvirtd.
180+ # "no state" may occur when the domain is currently
181+ # being migrated (on the migration target only), or
182+ # whenever virsh can't reliably obtain the domain
183+ # state.
184+ status="no state"
185+ if [ "$__OCF_ACTION" = "stop" ] && [ $try -ge 3 ]; then
186+ # During the stop operation, we want to bail out
187+ # quickly, so as to be able to force-stop (destroy)
188+ # the domain if necessary.
189+ ocf_log error "Virtual domain $DOMAIN_NAME has no state during stop operation, bailing out."
190+ return $OCF_ERR_GENERIC;
191+ else
192+ # During all other actions, we just wait and try
193+ # again, relying on the CRM/LRM to time us out if
194+ # this takes too long.
195+ ocf_log info "Virtual domain $DOMAIN_NAME currently has no state, retrying."
196+ sleep 1
197+ fi
198+ ;;
199+ *)
200+ # any other output is unexpected.
201+ ocf_log error "Virtual domain $DOMAIN_NAME has unknown status \"$status\"!"
202+ ;;
203+ esac
204+ done
205+ return $rc
206+}
207+
208+VirtualDomain_Start() {
209+ if VirtualDomain_Status; then
210+ ocf_log info "Virtual domain $DOMAIN_NAME already running."
211+ return $OCF_SUCCESS
212+ fi
213+
214+ virsh $VIRSH_OPTIONS start ${DOMAIN_NAME}
215+ rc=$?
216+ if [ $rc -ne 0 ]; then
217+ ocf_log error "Failed to start virtual domain ${DOMAIN_NAME}."
218+ return $OCF_ERR_GENERIC
219+ fi
220+
221+ while ! VirtualDomain_Monitor; do
222+ sleep 1
223+ done
224+ return $OCF_SUCCESS
225+}
226+
227+VirtualDomain_Stop() {
228+ local i
229+ local status
230+ local shutdown_timeout
231+ local out ex
232+
233+ VirtualDomain_Status
234+ status=$?
235+
236+ # Check the forced shutdown (destroy) FLAG requested by the vm-stonith function.
237+ if ! ocf_is_true $OCF_RESKEY_force_stop; then
238+ local xpath="//cib/status/node_state[@uname='`hostname`']/transient_attributes/instance_attributes/nvpair[@name='force_stop-${OCF_RESOURCE_INSTANCE}'][@value='true']"
239+ $HA_SBIN_DIR/cibadmin -Q -A"$xpath" 1>/dev/null 2>&1
240+ if [ $? -eq 0 ]; then
241+ ocf_log info "set variable OCF_RESKEY_force_stop=1"
242+ OCF_RESKEY_force_stop=1
243+ fi
244+ fi
245+
246+ case $status in
247+ $OCF_SUCCESS)
248+ if ! ocf_is_true $OCF_RESKEY_force_stop; then
249+ # Issue a graceful shutdown request
250+ ocf_log info "Issuing graceful shutdown request for domain ${DOMAIN_NAME}."
251+ virsh $VIRSH_OPTIONS shutdown ${DOMAIN_NAME}
252+ # The "shutdown_timeout" we use here is the operation
253+ # timeout specified in the CIB, minus 5 seconds
254+ shutdown_timeout=$(( $NOW + ($OCF_RESKEY_CRM_meta_timeout/1000) -5 ))
255+ # Loop on status until we reach $shutdown_timeout
256+ while [ $NOW -lt $shutdown_timeout ]; do
257+ VirtualDomain_Status
258+ status=$?
259+ case $status in
260+ $OCF_NOT_RUNNING)
261+ # This was a graceful shutdown. Clean
262+ # up and return.
263+ VirtualDomain_Cleanup_Statefile
264+ return $OCF_SUCCESS
265+ ;;
266+ $OCF_SUCCESS)
267+ # Domain is still running, keep
268+ # waiting (until shutdown_timeout
269+ # expires)
270+ sleep 1
271+ ;;
272+ *)
273+ # Something went wrong. Bail out and
274+ # resort to forced stop (destroy).
275+ break;
276+ esac
277+ NOW=$(date +%s)
278+ done
279+ fi
280+ ;;
281+ $OCF_NOT_RUNNING)
282+ ocf_log info "Domain $DOMAIN_NAME already stopped."
283+ return $OCF_SUCCESS
284+ esac
285+ # OK. Now if the above graceful shutdown hasn't worked, kill
286+ # off the domain with destroy. If that too does not work,
287+ # have the LRM time us out.
288+ ocf_log info "Issuing forced shutdown (destroy) request for domain ${DOMAIN_NAME}."
289+ out=$(virsh $VIRSH_OPTIONS destroy ${DOMAIN_NAME} 2>&1)
290+ ex=$?
291+ echo >&2 "$out"
292+ # unconditionally clean up.
293+ VirtualDomain_Cleanup_Statefile
294+ case $ex$out in
295+ *"error: Requested operation is not valid: domain is not running"*)
296+ : ;; # unexpected path to the intended outcome, all is well
297+ [!0]*)
298+ return $OCF_ERR_GENERIC ;;
299+ 0*)
300+ while [ $status != $OCF_NOT_RUNNING ]; do
301+ VirtualDomain_Status
302+ status=$?
303+ done ;;
304+ esac
305+ return $OCF_SUCCESS
306+}
307+
308+VirtualDomain_Migrate_To() {
309+ local target_node
310+ local remoteuri
311+ local transport_suffix
312+
313+ target_node="$OCF_RESKEY_CRM_meta_migrate_target"
314+
315+ if VirtualDomain_Status; then
316+ # Find out the remote hypervisor to connect to. That is, turn
317+ # something like "qemu://foo:9999/system" into
318+ # "qemu+tcp://bar:9999/system"
319+ if [ -n "${OCF_RESKEY_migration_transport}" ]; then
320+ transport_suffix="+${OCF_RESKEY_migration_transport}"
321+ fi
322+ # Scared of that sed expression? So am I. :-)
323+ remoteuri=$(echo ${OCF_RESKEY_hypervisor} | sed -e "s,\(.*\)://[^/:]*\(:\?[0-9]*\)/\(.*\),\1${transport_suffix}://${target_node}\2/\3,")
324+
325+ # OK, we know where to connect to. Now do the actual migration.
326+ ocf_log info "$DOMAIN_NAME: Starting live migration to ${target_node} (using remote hypervisor URI ${remoteuri})."
327+ virsh ${VIRSH_OPTIONS} migrate --live $DOMAIN_NAME ${remoteuri}
328+ rc=$?
329+ if [ $rc -ne 0 ]; then
330+ ocf_log err "$DOMAIN_NAME: live migration to ${remoteuri} failed: $rc"
331+ return $OCF_ERR_GENERIC
332+ else
333+ ocf_log info "$DOMAIN_NAME: live migration to ${target_node} succeeded."
334+ VirtualDomain_Cleanup_Statefile
335+ return $OCF_SUCCESS
336+ fi
337+ else
338+ ocf_log err "$DOMAIN_NAME: migrate_to: Not active locally!"
339+ return $OCF_ERR_GENERIC
340+ fi
341+}
342+
343+VirtualDomain_Migrate_From() {
344+ while ! VirtualDomain_Monitor; do
345+ sleep 1
346+ done
347+ ocf_log info "$DOMAIN_NAME: live migration from ${OCF_RESKEY_CRM_meta_migrate_source} succeeded."
348+ return $OCF_SUCCESS
349+}
350+
351+VirtualDomain_Monitor() {
352+ # First, check the domain status. If that returns anything other
353+ # than $OCF_SUCCESS, something is definitely wrong.
354+ VirtualDomain_Status
355+ rc=$?
356+ if [ ${rc} -eq ${OCF_SUCCESS} ]; then
357+ # OK, the generic status check turned out fine. Now, if we
358+ # have monitor scripts defined, run them one after another.
359+ for script in ${OCF_RESKEY_monitor_scripts}; do
360+ script_output="$($script 2>&1)"
361+ script_rc=$?
362+ if [ ${script_rc} -ne ${OCF_SUCCESS} ]; then
363+ # A monitor script returned a non-success exit
364+ # code. Stop iterating over the list of scripts, log a
365+ # warning message, and propagate $OCF_ERR_GENERIC.
366+ ocf_log warn "Monitor command \"${script}\" for domain ${DOMAIN_NAME} returned ${script_rc} with output: ${script_output}"
367+ rc=$OCF_ERR_GENERIC
368+ break
369+ else
370+ ocf_log debug "Monitor command \"${script}\" for domain ${DOMAIN_NAME} completed successfully with output: ${script_output}"
371+ fi
372+ done
373+ fi
374+ return ${rc}
375+}
376+
377+VirtualDomain_Validate_All() {
378+ # Required binaries:
379+ for binary in virsh sed; do
380+ check_binary $binary
381+ done
382+
383+ if [ -z $OCF_RESKEY_config ]; then
384+ ocf_log error "Missing configuration parameter \"config\"."
385+ return $OCF_ERR_CONFIGURED
386+ fi
387+
388+ # check if we can read the config file (otherwise we're unable to
389+ # deduce $DOMAIN_NAME from it, see below)
390+ if [ ! -r $OCF_RESKEY_config ]; then
391+ if ocf_is_probe; then
392+ ocf_log info "Configuration file $OCF_RESKEY_config not readable during probe."
393+ else
394+ ocf_log error "Configuration file $OCF_RESKEY_config does not exist or is not readable."
395+ return $OCF_ERR_INSTALLED
396+ fi
397+ fi
398+}
399+
400+if [ $# -ne 1 ]; then
401+ usage
402+ exit $OCF_ERR_ARGS
403+fi
404+
405+case $1 in
406+ meta-data) meta_data
407+ exit $OCF_SUCCESS
408+ ;;
409+ usage) usage
410+ exit $OCF_SUCCESS
411+ ;;
412+esac
413+
414+# Everything except usage and meta-data must pass the validate test
415+VirtualDomain_Validate_All || exit $?
416+
417+# Delete the forced shutdown (destroy) FLAG created by the vm-stonith function.
418+if ocf_is_probe || [ "$__OCF_ACTION" = "start" ]; then
419+ xpath="//cib/status/node_state/transient_attributes/instance_attributes/nvpair[@name='force_stop-${OCF_RESOURCE_INSTANCE}'][@value='true']"
420+ $HA_SBIN_DIR/cibadmin -d -A"$xpath" --force 1>/dev/null 2>&1
421+fi
422+
423+# During a probe, it is permissible for the config file to not be
424+# readable (it might be on shared storage not available during the
425+# probe). In that case, VirtualDomain_Define can't work and we're
426+# unable to get the domain name. Thus, we also can't check whether the
427+# domain is running. The only thing we can do here is to assume that
428+# it is not running.
429+if ocf_is_probe && [ ! -r $OCF_RESKEY_config ]; then
430+ exit $OCF_NOT_RUNNING
431+fi
432+
433+# Define the domain on startup, and re-define whenever someone deleted
434+# the state file, or touched the config.
435+if [ ! -e $STATEFILE ] || [ $OCF_RESKEY_config -nt $STATEFILE ]; then
436+ VirtualDomain_Define
437+fi
438+# By now, we should definitely be able to read from the state file.
439+# If not, something went wrong.
440+if [ ! -r $STATEFILE ]; then
441+ ocf_log err "$STATEFILE not found or unreadable. This is unexpected. Cannot determine domain name."
442+ exit $OCF_ERR_GENERIC
443+fi
444+# Finally, retrieve the domain name from the state file.
445+DOMAIN_NAME=`cat $STATEFILE 2>/dev/null`
446+if [ -z $DOMAIN_NAME ]; then
447+ ocf_log err "$STATEFILE is empty. This is unexpected. Cannot determine domain name."
448+ exit $OCF_ERR_GENERIC
449+fi
450+
451+case $1 in
452+ start)
453+ VirtualDomain_Start
454+ ;;
455+ stop)
456+ VirtualDomain_Stop
457+ ;;
458+ migrate_to)
459+ VirtualDomain_Migrate_To
460+ ;;
461+ migrate_from)
462+ VirtualDomain_Migrate_From
463+ ;;
464+ status)
465+ VirtualDomain_Status
466+ ;;
467+ monitor)
468+ VirtualDomain_Monitor
469+ ;;
470+ validate-all)
471+ ;;
472+ *)
473+ usage
474+ exit $OCF_ERR_UNIMPLEMENTED
475+ ;;
476+esac
477+exit $?
diff -r 000000000000 -r 441a96ed873f resources/vm-anything
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/vm-anything Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,323 @@
1+#!/bin/sh
2+#
3+# OCF Resource Agent compliant resource script.
4+#
5+# Copyright (c) 2009 IN-telegence GmbH & Co. KG, Dominik Klein
6+# All Rights Reserved.
7+#
8+# This program is free software; you can redistribute it and/or modify
9+# it under the terms of version 2 of the GNU General Public License as
10+# published by the Free Software Foundation.
11+#
12+# This program is distributed in the hope that it would be useful, but
13+# WITHOUT ANY WARRANTY; without even the implied warranty of
14+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15+#
16+# Further, this software is distributed without any warranty that it is
17+# free of the rightful claim of any third person regarding infringement
18+# or the like. Any license provided herein, whether implied or
19+# otherwise, applies only to this software file. Patent licenses, if
20+# any, provided herein do not apply to combinations of this program with
21+# other software, or any other product whatsoever.
22+#
23+# You should have received a copy of the GNU General Public License
24+# along with this program; if not, write the Free Software Foundation,
25+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
26+
27+# OCF instance parameters
28+# OCF_RESKEY_binfile
29+# OCF_RESKEY_cmdline_options
30+# OCF_RESKEY_pidfile
31+# OCF_RESKEY_logfile
32+# OCF_RESKEY_errlogfile
33+# OCF_RESKEY_user
34+# OCF_RESKEY_monitor_hook
35+# OCF_RESKEY_stop_timeout
36+#
37+# This RA starts $binfile with $cmdline_options as $user and writes a $pidfile from that.
38+# If you want it to, it logs:
39+# - stdout to $logfile, stderr to $errlogfile or
40+# - stdout and stderr to $logfile
41+# - or to will be captured by lrmd if these options are omitted.
42+# Monitoring is done through $pidfile or your custom $monitor_hook script.
43+# The RA expects the program to keep running "daemon-like" and
44+# not just quit and exit. So this is NOT (yet - feel free to
45+# enhance) a way to just run a single one-shot command which just
46+# does something and then exits.
47+
48+# Initialization:
49+: ${OCF_RESKEY_login_shell:="true"}
50+: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
51+. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs
52+
53+getpid() {
54+ grep -o '[0-9]*' $1
55+}
56+
57+anything_status() {
58+ if test -f "$pidfile"
59+ then
60+ if pid=`getpid $pidfile` && [ "$pid" ] && kill -s 0 $pid
61+ then
62+ return $OCF_RUNNING
63+ else
64+ # pidfile w/o process means the process died
65+ return $OCF_ERR_GENERIC
66+ fi
67+ else
68+ return $OCF_NOT_RUNNING
69+ fi
70+}
71+
72+anything_start() {
73+ if ! anything_status
74+ then
75+ if [ -n "$logfile" -a -n "$errlogfile" ]
76+ then
77+ # We have logfile and errlogfile, so redirect STDOUT und STDERR to different files
78+ cmd="su $login_shell $user -c \"nohup $binfile $cmdline_options >> $logfile 2>> $errlogfile & \"'echo \$!' "
79+ else if [ -n "$logfile" ]
80+ then
81+ # We only have logfile so redirect STDOUT and STDERR to the same file
82+ cmd="su $login_shell $user -c \"nohup $binfile $cmdline_options >> $logfile 2>&1 & \"'echo \$!' "
83+ else
84+ # We have neither logfile nor errlogfile, so we're not going to redirect anything
85+ cmd="su $login_shell $user -c \"nohup $binfile $cmdline_options & \"'echo \$!'"
86+ fi
87+ fi
88+ ocf_log debug "Starting $process: $cmd"
89+ # Execute the command as created above
90+ eval $cmd > $pidfile
91+ if anything_status
92+ then
93+ ocf_log debug "$process: $cmd started successfully"
94+ return $OCF_SUCCESS
95+ else
96+ ocf_log err "$process: $cmd could not be started"
97+ return $OCF_ERR_GENERIC
98+ fi
99+ else
100+ # If already running, consider start successful
101+ ocf_log debug "$process: $cmd is already running"
102+ return $OCF_SUCCESS
103+ fi
104+}
105+
106+anything_stop() {
107+ if [ -n "$OCF_RESKEY_stop_timeout" ]
108+ then
109+ stop_timeout=$OCF_RESKEY_stop_timeout
110+ elif [ -n "$OCF_RESKEY_CRM_meta_timeout" ]; then
111+ # Allow 2/3 of the action timeout for the orderly shutdown
112+ # (The origin unit is ms, hence the conversion)
113+ stop_timeout=$((OCF_RESKEY_CRM_meta_timeout/1500))
114+ else
115+ stop_timeout=10
116+ fi
117+ if anything_status
118+ then
119+ pid=`getpid $pidfile`
120+ kill $pid
121+ i=0
122+ while [ $i -lt $stop_timeout ]
123+ do
124+ if ! anything_status
125+ then
126+ rm -f $pidfile
127+ return $OCF_SUCCESS
128+ fi
129+ sleep 1
130+ i=$((i+1))
131+ done
132+ ocf_log warn "Stop with SIGTERM failed/timed out, now sending SIGKILL."
133+ kill -s 9 $pid
134+ rm -f $pidfile
135+ if ! anything_status
136+ then
137+ ocf_log warn "SIGKILL did the job."
138+ return $OCF_SUCCESS
139+ else
140+ ocf_log err "Failed to stop - even with SIGKILL."
141+ return $OCF_ERR_GENERIC
142+ fi
143+ else
144+ # was not running, so stop can be considered successful
145+ rm -f $pidfile
146+ return $OCF_SUCCESS
147+ fi
148+}
149+
150+anything_monitor() {
151+ anything_status
152+ ret=$?
153+ if [ $ret -eq $OCF_SUCCESS ]
154+ then
155+ if [ -n "$OCF_RESKEY_monitor_hook" ]; then
156+ eval "$OCF_RESKEY_monitor_hook"
157+ if [ $? -ne $OCF_SUCCESS ]; then
158+ return ${OCF_ERR_GENERIC}
159+ fi
160+ return $OCF_SUCCESS
161+ else
162+ true
163+ fi
164+ else
165+ return $ret
166+ fi
167+}
168+
169+: ${OCF_RESKEY_CRM_meta_globally_unique:="true"}
170+
171+if [ ${OCF_RESKEY_CRM_meta_globally_unique} = "false" ]; then
172+ # Strip off the trailing clone marker
173+ process=`echo ${OCF_RESOURCE_INSTANCE} | sed s/:[0-9][0-9]//`
174+else
175+ process="$OCF_RESOURCE_INSTANCE"
176+fi
177+binfile="$OCF_RESKEY_binfile"
178+cmdline_options='$OCF_RESKEY_cmdline_options'
179+pidfile="$OCF_RESKEY_pidfile"
180+[ -z "$pidfile" ] && pidfile=${HA_VARRUN}/anything_${process}.pid
181+logfile="$OCF_RESKEY_logfile"
182+errlogfile="$OCF_RESKEY_errlogfile"
183+user="$OCF_RESKEY_user"
184+if ocf_is_true "$OCF_RESKEY_login_shell"; then
185+ login_shell="-"
186+else
187+ login_shell=""
188+fi
189+[ -z "$user" ] && user=root
190+
191+anything_validate() {
192+ if ! su - $user -c "test -x $binfile"
193+ then
194+ ocf_log err "binfile $binfile does not exist or is not executable by $user."
195+ exit $OCF_ERR_INSTALLED
196+ fi
197+ if ! getent passwd $user >/dev/null 2>&1
198+ then
199+ ocf_log err "user $user does not exist."
200+ exit $OCF_ERR_INSTALLED
201+ fi
202+ for logfilename in "$logfile" "$errlogfile"
203+ do
204+ if [ -n "$logfilename" ]; then
205+ mkdir -p `dirname $logfilename` || {
206+ ocf_log err "cannot create $(dirname $logfilename)"
207+ exit $OCF_ERR_INSTALLED
208+ }
209+ fi
210+ done
211+ return $OCF_SUCCESS
212+}
213+
214+anything_meta() {
215+cat <<END
216+<?xml version="1.0"?>
217+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
218+<resource-agent name="anything">
219+<version>1.0</version>
220+<longdesc lang="en">
221+This is a generic OCF RA to manage almost anything.
222+</longdesc>
223+<shortdesc lang="en">Manages an arbitrary service</shortdesc>
224+
225+<parameters>
226+<parameter name="binfile" required="1" unique="1">
227+<longdesc lang="en">
228+The full name of the binary to be executed. This is expected to keep running with the same pid and not just do something and exit.
229+</longdesc>
230+<shortdesc lang="en">Full path name of the binary to be executed</shortdesc>
231+<content type="string" default=""/>
232+</parameter>
233+<parameter name="cmdline_options" required="0">
234+<longdesc lang="en">
235+Command line options to pass to the binary
236+</longdesc>
237+<shortdesc lang="en">Command line options</shortdesc>
238+<content type="string" />
239+</parameter>
240+<parameter name="pidfile">
241+<longdesc lang="en">
242+File to read/write the PID from/to.
243+</longdesc>
244+<shortdesc lang="en">File to write STDOUT to</shortdesc>
245+<content type="string" default="${HA_VARRUN}/anything_${process}.pid"/>
246+</parameter>
247+<parameter name="logfile" required="0">
248+<longdesc lang="en">
249+File to write STDOUT to
250+</longdesc>
251+<shortdesc lang="en">File to write STDOUT to</shortdesc>
252+<content type="string" />
253+</parameter>
254+<parameter name="errlogfile" required="0">
255+<longdesc lang="en">
256+File to write STDERR to
257+</longdesc>
258+<shortdesc lang="en">File to write STDERR to</shortdesc>
259+<content type="string" />
260+</parameter>
261+<parameter name="user" required="0">
262+<longdesc lang="en">
263+User to run the command as
264+</longdesc>
265+<shortdesc lang="en">User to run the command as</shortdesc>
266+<content type="string" default="root"/>
267+</parameter>
268+<parameter name="monitor_hook">
269+<longdesc lang="en">
270+Command to run in monitor operation
271+</longdesc>
272+<shortdesc lang="en">Command to run in monitor operation</shortdesc>
273+<content type="string"/>
274+</parameter>
275+<parameter name="stop_timeout">
276+<longdesc lang="en">
277+In the stop operation: Seconds to wait for kill -TERM to succeed
278+before sending kill -SIGKILL. Defaults to 2/3 of the stop operation timeout.
279+</longdesc>
280+<shortdesc lang="en">Seconds to wait after having sent SIGTERM before sending SIGKILL in stop operation</shortdesc>
281+<content type="string" default=""/>
282+</parameter>
283+<parameter name="login_shell">
284+<longdesc lang="en">
285+It is setting to decide whether you use a login shell in a user carrying out a command.
286+</longdesc>
287+<shortdesc lang="en">Setting whether or not I use a login shell</shortdesc>
288+<content type="string" default="true"/>
289+</parameter>
290+</parameters>
291+<actions>
292+<action name="start" timeout="20s" />
293+<action name="stop" timeout="20s" />
294+<action name="monitor" depth="0" timeout="20s" interval="10" />
295+<action name="meta-data" timeout="5" />
296+<action name="validate-all" timeout="5" />
297+</actions>
298+</resource-agent>
299+END
300+exit 0
301+}
302+
303+case "$1" in
304+ meta-data|metadata|meta_data)
305+ anything_meta
306+ ;;
307+ start)
308+ anything_start
309+ ;;
310+ stop)
311+ anything_stop
312+ ;;
313+ monitor)
314+ anything_monitor
315+ ;;
316+ validate-all)
317+ anything_validate
318+ ;;
319+ *)
320+ ocf_log err "$0 was called with unsupported arguments: $*"
321+ exit $OCF_ERR_UNIMPLEMENTED
322+ ;;
323+esac
diff -r 000000000000 -r 441a96ed873f resources/vm-client
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/vm-client Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,253 @@
1+#!/bin/sh
2+#
3+#
4+# vm-client OCF RA. refer for arbitrary attribute information for
5+# vm-managerd.
6+#
7+# Copyright (c) 2010 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
8+#
9+# This program is free software; you can redistribute it and/or modify
10+# it under the terms of version 2 of the GNU General Public License as
11+# published by the Free Software Foundation.
12+#
13+# This program is distributed in the hope that it would be useful, but
14+# WITHOUT ANY WARRANTY; without even the implied warranty of
15+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16+#
17+# Further, this software is distributed without any warranty that it is
18+# free of the rightful claim of any third person regarding infringement
19+# or the like. Any license provided herein, whether implied or
20+# otherwise, applies only to this software file. Patent licenses, if
21+# any, provided herein do not apply to combinations of this program with
22+# other software, or any other product whatsoever.
23+#
24+# You should have received a copy of the GNU General Public License
25+# along with this program; if not, write the Free Software Foundation,
26+# Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
27+#
28+
29+#######################################################################
30+# Initialization:
31+
32+. ${OCF_ROOT}/resource.d/heartbeat/.ocf-shellfuncs
33+
34+#######################################################################
35+
36+meta_data() {
37+ cat <<END
38+<?xml version="1.0"?>
39+<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
40+<resource-agent name="vm-client" version="1.0">
41+<version>1.0</version>
42+
43+<longdesc lang="en">
44+This Resource Agent is vm-managerd and a thing to communicate in virtual environment.
45+</longdesc>
46+<shortdesc lang="en">vm-client resource agent</shortdesc>
47+
48+<parameters>
49+
50+<parameter name="attribute_list" unique="1" require="1">
51+<longdesc lang="en">
52+The list of the attribute to refer to host.
53+</longdesc>
54+<shortdesc lang="en">attribute list</shortdesc>
55+<content type="string" default="" />
56+</parameter>
57+
58+<parameter name="state" unique="1">
59+<longdesc lang="en">
60+Location to store the resource state in.
61+</longdesc>
62+<shortdesc lang="en">State file</shortdesc>
63+<content type="string" default="${HA_VARRUN}/vm-client-${OCF_RESOURCE_INSTANCE}.state" />
64+</parameter>
65+
66+<parameter name="debug" unique="0">
67+<longdesc lang="en">
68+Enables to use default ${ATTRD_UPDATER} and ${CONNECT_CMD} verbose logging on every call.
69+</longdesc>
70+<shortdesc lang="en">Verbose logging</shortdesc>
71+<content type="string" default="false"/>
72+</parameter>
73+
74+</parameters>
75+
76+<actions>
77+<action name="start" timeout="90" />
78+<action name="stop" timeout="100" />
79+<action name="monitor" timeout="20" interval="10" depth="0" start-delay="0" />
80+<action name="reload" timeout="90" />
81+<action name="meta-data" timeout="5" />
82+<action name="validate-all" timeout="30" />
83+</actions>
84+</resource-agent>
85+END
86+}
87+
88+#######################################################################
89+
90+vmclient_usage() {
91+ cat <<END
92+usage: $0 {start|stop|monitor|validate-all|meta-data}
93+
94+Expects to have a fully populated OCF RA-compliant environment set.
95+END
96+}
97+
98+attrd_send_update() {
99+ echo ${1} | grep "=" 2>&1 >/dev/null
100+ if [ $? -ne 0 ]; then
101+ return 0
102+ fi
103+
104+ attr_name=`echo ${1} | sed 's/=.*//g'`
105+ attr_value=`echo ${1} | sed 's/.*=//g'`
106+
107+ if [ ${attr_value}x = "x" ]; then
108+ ${ATTRD_UPDATER} -D -n ${attr_name} ${logging_options}
109+ return 0
110+ fi
111+
112+ ${ATTRD_UPDATER} -n ${attr_name} -U ${attr_value} ${logging_options}
113+ if [ $? -ne 0 ]; then
114+ ocf_log err "attrd failed to update. (attr_name=$attr_name)"
115+ exit $OCF_ERR_GENERIC
116+ fi
117+}
118+
119+communicate_vm_manager() {
120+ while true; do
121+ sleep 1
122+ reqid=`uuidgen`
123+ status_list=`${CONNECT_CMD} -t "monitor" -r "${attr_list}" -i ${reqid} ${logging_options}`
124+ rc=$?
125+ if [ $rc -eq $OCF_SUCCESS ]; then
126+ IFS=","
127+ for state in ${status_list}; do
128+ attrd_send_update "${state}"
129+ done
130+
131+ break
132+ elif [ $rc -eq 2 ]; then
133+ # is live_migration
134+ ocf_log info "Probably perform an inquiry again because migration was performed."
135+ continue
136+ fi
137+
138+ return $OCF_ERR_GENERIC
139+ done
140+
141+ return $OCF_SUCCESS
142+}
143+
144+vmclient_start() {
145+ vmclient_monitor
146+ if [ $? -eq $OCF_SUCCESS ]; then
147+ return $OCF_SUCCESS
148+ fi
149+
150+ touch ${OCF_RESKEY_state}
151+ vmclient_monitor
152+}
153+
154+vmclient_stop() {
155+
156+ rm -f ${OCF_RESKEY_state}
157+
158+ for attr_name in ${attr_list}; do
159+ ${ATTRD_UPDATER} -D -n ${attr_name} ${logging_options}
160+ if [ $? -ne 0 ]; then
161+ ocf_log err "attrd failed to delete. (attr_name=$attr_name)"
162+ return $OCF_ERR_GENERIC
163+ fi
164+ done
165+
166+ return $OCF_SUCCESS
167+}
168+
169+vmclient_monitor() {
170+ if [ -f ${OCF_RESKEY_state} ]; then
171+ communicate_vm_manager
172+ if [ $? -ne $OCF_SUCCESS ]; then
173+ ocf_log err "failed in the reception from vm-managerd."
174+ return $OCF_ERR_GENERIC
175+ fi
176+
177+ return $OCF_SUCCESS
178+ fi
179+
180+ return $OCF_NOT_RUNNING
181+}
182+
183+vmclient_validate() {
184+ # Is the state directory writable?
185+ state_dir=`dirname "$OCF_RESKEY_state"`
186+ touch "$state_dir/$$"
187+ if [ $? != 0 ]; then
188+ ocf_log err "Invalid location for 'state': $state_dir is not writable"
189+ return $OCF_ERR_ARGS
190+ fi
191+ rm "$state_dir/$$"
192+
193+ # Pidfile better be an absolute path
194+ case $OCF_RESKEY_state in
195+ /*) ;;
196+ *) ocf_log warn "You should use an absolute path for state file not: $OCF_RESKEY_state" ;;
197+ esac
198+
199+ # Check the attribute list
200+ if [ "x" = "x$attr_list" ]; then
201+ ocf_log err "Empty attribute_list."
202+ exit $OCF_ERR_CONFIGURED
203+ fi
204+
205+ check_binary ${CONNECT_CMD}
206+ check_binary ${ATTRD_UPDATER}
207+
208+ return $OCF_SUCCESS
209+}
210+
211+: ${OCF_RESKEY_CRM_meta_interval=0}
212+: ${OCF_RESKEY_CRM_meta_globally_unique:="true"}
213+: ${OCF_RESKEY_debug:="false"}
214+
215+attr_list=`echo ${OCF_RESKEY_attribute_list} | tr -s " "`
216+
217+if [ "x$OCF_RESKEY_state" = "x" ]; then
218+ if [ ${OCF_RESKEY_CRM_meta_globally_unique} = "false" ]; then
219+ state="${HA_VARRUN}/vm-client-${OCF_RESOURCE_INSTANCE}.state"
220+
221+ # Strip off the trailing clone marker
222+ OCF_RESKEY_state=`echo $state | sed s/:[0-9][0-9]*\.state/.state/`
223+ else
224+ OCF_RESKEY_state="${HA_VARRUN}/vm-client-${OCF_RESOURCE_INSTANCE}.state"
225+ fi
226+fi
227+
228+logging_options='-q'
229+if ocf_is_true ${OCF_RESKEY_debug} ; then
230+ logging_options=''
231+fi
232+
233+CONNECT_CMD="vm-connect"
234+ATTRD_UPDATER="attrd_updater"
235+
236+case $__OCF_ACTION in
237+meta-data) meta_data
238+ exit $OCF_SUCCESS
239+ ;;
240+start) vmclient_start;;
241+stop) vmclient_stop;;
242+monitor) vmclient_monitor;;
243+reload) vmclient_start;;
244+validate-all) vmclient_validate;;
245+usage|help) vmclient_usage
246+ exit $OCF_SUCCESS
247+ ;;
248+*) vmclient_usage
249+ exit $OCF_ERR_UNIMPLEMENTED
250+ ;;
251+esac
252+exit $?
253+
diff -r 000000000000 -r 441a96ed873f tools/Makefile.am
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/Makefile.am Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,32 @@
1+MAINTAINERCLEANFILES = Makefile.in
2+
3+INCLUDES = -I/usr/include/pacemaker \
4+ -I/usr/include/glib-2.0 \
5+ -I$(libdir)/glib-2.0/include \
6+ -I/usr/include/libxml2
7+
8+sbin_PROGRAMS = vm-connectd vm-connect vm-managerd vm-stonithd
9+
10+vm_connectd_SOURCES = vm-connectd.c
11+vm_connectd_LDADD = $(top_builddir)/lib/libvmconnect.la \
12+ -lgio-2.0 \
13+ -lcrmcommon \
14+ -lcib
15+
16+vm_connect_SOURCES = vm-connect.c
17+vm_connect_LDADD = $(top_builddir)/lib/libvmconnect.la
18+
19+vm_managerd_SOURCES = vm-managerd.c
20+vm_managerd_LDADD = $(top_builddir)/lib/libvmconnect.la \
21+ -lcrmcommon \
22+ -lcrmcluster \
23+ -lcib \
24+ -lxml2
25+
26+vm_stonithd_SOURCES = vm-stonithd.c
27+vm_stonithd_LDADD = $(top_builddir)/lib/libvmconnect.la \
28+ -lcib \
29+ -lpe_status \
30+ -lncurses
31+
32+AM_CFLAGS = -Wall -Werror
diff -r 000000000000 -r 441a96ed873f tools/vm-connect.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/vm-connect.c Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,178 @@
1+/*
2+ * vm_connect : Simple program which communicates between vm_connectd.
3+ *
4+ * Copyright (C) 2010 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
5+ *
6+ * This program is free software; you can redistribute it and/or
7+ * modify it under the terms of the GNU General Public
8+ * License as published by the Free Software Foundation; either
9+ * version 2.1 of the License, or (at your option) any later version.
10+ *
11+ * This software is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+ * General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public
17+ * License along with this library; if not, write to the Free Software
18+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+ */
20+
21+#include <stdio.h>
22+#include <unistd.h>
23+#include <libgen.h>
24+#include <crm/crm.h>
25+#include <vm_connect.h>
26+
27+#ifdef HAVE_GETOPT_H
28+# include <getopt.h>
29+#endif
30+
31+extern gboolean on_host; /* lib/vm_connect.c */
32+
33+#define OPTARGS "t:i:R:r:Vq?"
34+
35+static void
36+usage(const char *cmd, int exitstatus)
37+{
38+ fprintf(stderr, "\nusage: %s -t type command\n", cmd);
39+ fprintf(stderr, "\n Request data is sent to host(server process) via vm-connectd process\n");
40+
41+ fprintf(stderr, "\nnecessary options:\n");
42+ fprintf(stderr, " -%c, --%s=value\t\tSpecify 'monitor' OR 'stonith'\n", 't', "type");
43+
44+ fprintf(stderr, "\ncommands:\n");
45+ fprintf(stderr, " -%c, --%s=value\t\tRequest to server process, and receive result\n",
46+ 'r', "request");
47+ fprintf(stderr, " -%c, --%s=value\tRequest to server process\n",
48+ 'R', "request-only");
49+
50+ fprintf(stderr, "\noptions:\n");
51+ fprintf(stderr, " -%c, --%s=value\t\tSpecify request ID\n", 'i', "reqid");
52+ fprintf(stderr, " -%c, --%s\t\t\tIncrease the debug output\n", 'V', "verbose");
53+ fprintf(stderr, " -%c, --%s\t\t\tControl the output of log\n", 'q', "quiet");
54+ fprintf(stderr, " -%c, --%s\t\t\tThis text\n", '?', "help");
55+
56+ fprintf(stderr, "\n");
57+ exit(exitstatus);
58+}
59+
60+int
61+main(int argc, char **argv)
62+{
63+ msgtype type = -1;
64+ char *request = NULL;
65+ gboolean request_only = FALSE;
66+ char *reqid = NULL;
67+ int loglevel = LOG_INFO;
68+ gboolean verbose = FALSE;
69+ gboolean quiet = FALSE;
70+ int argerr = 0, flag;
71+#ifdef HAVE_GETOPT_H
72+ int opt_idx = 0;
73+ static struct option long_opts[] = {
74+ {"type", 1, 0, 't'},
75+ {"request", 1, 0, 'r'},
76+ {"request-only", 1, 0, 'R'},
77+ {"reqid", 1, 0, 'i'},
78+ {"verbose", 0, 0, 'V'},
79+ {"quiet", 0, 0, 'q'},
80+ {"help", 0, 0, '?'},
81+ {0, 0, 0, 0}
82+ };
83+#endif
84+ int sockfd, rc, ret = 0;
85+ vm_message msg;
86+
87+ while (1) {
88+#ifdef HAVE_GETOPT_H
89+ flag = getopt_long(argc, argv, OPTARGS, long_opts, &opt_idx);
90+#else
91+ flag = getopt(argc, argv, OPTARGS);
92+#endif
93+ if (flag == -1)
94+ break;
95+ switch (flag) {
96+ case 't':
97+ if (safe_str_eq(optarg, "monitor"))
98+ type = T_MOD_MONITOR;
99+ else if (safe_str_eq(optarg, "stonith"))
100+ type = T_MOD_STONITH;
101+ break;
102+ case 'r':
103+ request = crm_strdup(optarg);
104+ break;
105+ case 'R':
106+ request_only = TRUE;
107+ request = crm_strdup(optarg);
108+ break;
109+ case 'i':
110+ reqid = crm_strdup(optarg);
111+ break;
112+ case 'V':
113+ loglevel++;
114+ verbose = TRUE;
115+ break;
116+ case 'q':
117+ quiet = TRUE;
118+ break;
119+ case '?':
120+ usage(basename(argv[0]), LSB_EXIT_GENERIC);
121+ break;
122+ default:
123+ fprintf(stderr, "Argument code 0%o (%c) is not (?yet?) supported",
124+ flag, flag);
125+ argerr++;
126+ break;
127+ }
128+ }
129+
130+ if (!quiet)
131+ crm_log_init(basename(argv[0]), loglevel, TRUE, verbose, argc, argv);
132+ else
133+ crm_log_init(basename(argv[0]), loglevel, TRUE, verbose, 0, NULL);
134+
135+ if (optind < argc) {
136+ fprintf(stderr, "non-option ARGV-elements: ");
137+ while (optind < argc)
138+ fprintf(stderr, "%s", argv[optind++]);
139+ fprintf(stderr, "\n");
140+ argerr++;
141+ }
142+ if (argerr || type == -1 || !request)
143+ usage(crm_system_name, LSB_EXIT_GENERIC);
144+
145+ on_host = FALSE;
146+ if ((sockfd = connect_to(SOCK_PATH, 0)) < 0)
147+ return 1;
148+ if (send_message(sockfd, type, reqid, request) < 0) {
149+ ret = 1;
150+ goto end;
151+ }
152+ if (request_only == TRUE)
153+ goto end;
154+
155+ while (1) {
156+ rc = receive_msg(sockfd, &msg);
157+ if (rc == 0) {
158+ if (reqid && safe_str_neq(reqid, msg.info.id)) {
159+ crm_info("request ID (%s) is unmatch : %s", reqid, msg.info.id);
160+ continue;
161+ }
162+ else {
163+ fprintf(stdout, "%s\n", msg.data);
164+ goto end;
165+ }
166+ }
167+ else if (rc == 2) {
168+ /* migration occurred.. */
169+ ret = 2;
170+ goto end;
171+ }
172+ ret = 1;
173+ goto end;
174+ }
175+end:
176+ close(sockfd);
177+ return ret;
178+}
diff -r 000000000000 -r 441a96ed873f tools/vm-connectd.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/vm-connectd.c Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,688 @@
1+/*
2+ * vm_connectd : Daemon program which communicates between host and guest.
3+ *
4+ * Copyright (C) 2010 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
5+ *
6+ * This program is free software; you can redistribute it and/or
7+ * modify it under the terms of the GNU General Public
8+ * License as published by the Free Software Foundation; either
9+ * version 2.1 of the License, or (at your option) any later version.
10+ *
11+ * This software is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+ * General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public
17+ * License along with this library; if not, write to the Free Software
18+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+ */
20+
21+#include <config.h>
22+#include <stdio.h>
23+#include <stdlib.h>
24+#include <unistd.h>
25+#include <sys/types.h>
26+#include <sys/stat.h>
27+#include <sys/socket.h>
28+#include <sys/un.h>
29+#include <libgen.h>
30+#include <glib.h>
31+#include <gio/gio.h>
32+#include <vm_connect.h>
33+#include <crm/crm.h>
34+#include <crm/common/util.h>
35+#include <crm/cib.h>
36+
37+GMainLoop *mainloop = NULL;
38+GFileMonitor *monitor = NULL;
39+const char *sock_dir = NULL;
40+cib_t *cib_conn = NULL;
41+gboolean need_shutdown = FALSE;
42+GHashTable *guest_hash = NULL;
43+GIOChannel *hostch = NULL;
44+int listen_sock;
45+int local_sock;
46+int evid;
47+
48+extern int sock_host; /* lib/vm_connect.c */
49+extern gboolean on_host; /* lib/vm_connect.c */
50+extern GHashTable *io_watch; /* lib/vm_connect.c */
51+
52+typedef struct guest_s {
53+ char *id;
54+ char *name;
55+ char *conf_path;
56+ char *sock_path;
57+ GIOChannel *ioch;
58+ int *sockfd;
59+ guint *sourceid;
60+ int reconnect_timer;
61+ gboolean connected;
62+} guest_t;
63+
64+static void
65+file_monitor_callback(GFileMonitor *monitor, GFile *file, GFile *other,
66+ GFileMonitorEvent event_type, gchar *unused);
67+static int connect_to_host(const char *port);
68+static int connect_to_guest(guest_t *guest);
69+
70+static void
71+free_guest_info(gpointer data)
72+{
73+ guest_t *guest = (guest_t *)data;
74+
75+ crm_free(guest->id);
76+ crm_free(guest->name);
77+ crm_free(guest->conf_path);
78+ crm_free(guest->sock_path);
79+ crm_free(guest);
80+
81+ return;
82+}
83+
84+static void
85+close_to_hash_socket(gpointer sockfd, gpointer sourceid, gpointer user_data)
86+{
87+ crm_debug_3("close to socket %p[%d]", sockfd, *(int*)sockfd);
88+ close(*(int*)sockfd);
89+
90+ return;
91+}
92+
93+static void
94+vm_connectd_shutdown(int nsig)
95+{
96+ crm_debug_2("called..");
97+
98+ need_shutdown = TRUE;
99+
100+ close(listen_sock);
101+ unlink(SOCK_PATH);
102+
103+ g_hash_table_foreach(io_watch, close_to_hash_socket, NULL);
104+ g_hash_table_destroy(io_watch);
105+
106+ if(on_host) {
107+ g_hash_table_destroy(guest_hash);
108+ }
109+
110+ if(mainloop != NULL && g_main_loop_is_running(mainloop)) {
111+ g_main_loop_quit(mainloop);
112+ } else {
113+ exit(0);
114+ }
115+
116+ return;
117+}
118+
119+static void
120+vm_connectd_cib_connection_destroy(gpointer user_data)
121+{
122+ crm_debug_2("called..");
123+ if(need_shutdown) {
124+ crm_info("Connection to the CIB terminated...");
125+ } else {
126+ crm_err("Connection to the CIB terminated...");
127+ exit(1);
128+ }
129+
130+ return;
131+}
132+
133+static int
134+cib_connect(void *user_data)
135+{
136+ enum cib_errors rc = cib_not_connected;
137+ int attempts = 0;
138+ int max_retry = 20;
139+ gboolean was_err = FALSE;
140+
141+ crm_debug_2("called..");
142+ cib_conn = cib_new();
143+
144+ while(rc != cib_ok) {
145+ attempts++;
146+ crm_debug("CIB signon attempt %d.", attempts);
147+ rc = cib_conn->cmds->signon(cib_conn, "vm-connectd", cib_command);
148+
149+ if(rc != cib_ok && attempts >= max_retry) {
150+ crm_err("Signon to CIB failed: %s", cib_error2string(rc));
151+ was_err = TRUE;
152+ break;
153+ }
154+ sleep(1);
155+ }
156+
157+ crm_info("Connected to the CIB after %d signon attempts.", attempts);
158+
159+ if(was_err == FALSE) {
160+ rc = cib_conn->cmds->set_connection_dnotify(
161+ cib_conn, vm_connectd_cib_connection_destroy);
162+ if(rc != cib_ok) {
163+ crm_err("Could not set dnotify callback.");
164+ was_err = TRUE;
165+ }
166+ }
167+
168+ if(was_err) {
169+ crm_err("Aborting startup.");
170+ return -1;
171+ }
172+
173+ return 0;
174+}
175+
176+static GFileMonitor *
177+set_file_monitor(const char *monitor_path)
178+{
179+ GFile *gfile = NULL;
180+ GError *error = NULL;
181+ GFileMonitor *gfile_monitor = NULL;
182+
183+ crm_debug_2("called..");
184+ /* set socket file monitor */
185+ g_type_init();
186+
187+ gfile = g_file_new_for_path(monitor_path);
188+ gfile_monitor = g_file_monitor(gfile, G_FILE_MONITOR_NONE, NULL, &error);
189+
190+ if(gfile_monitor == NULL) {
191+ crm_err("g_file_monitor() failed: [%s].", error->message);
192+ return NULL;
193+ }
194+
195+ g_signal_connect(gfile_monitor, "changed", G_CALLBACK(file_monitor_callback), NULL);
196+
197+ return gfile_monitor;
198+}
199+
200+static gboolean
201+reconnect_to_guest(gpointer data)
202+{
203+ int rc;
204+ guest_t *guest = (guest_t *)data;
205+
206+ crm_debug_2("called..");
207+ rc = connect_to_guest(guest);
208+ if(rc <= 0) {
209+ guest->reconnect_timer = 0;
210+ return FALSE;
211+ }
212+
213+ return TRUE;
214+}
215+
216+static void
217+guest_connection_destroy_notify(gpointer data)
218+{
219+ guest_t *guest = data;
220+
221+ crm_debug_2("called..");
222+ guest->connected = FALSE;
223+
224+ return;
225+}
226+
227+static int
228+connect_to_guest(guest_t *guest)
229+{
230+ int rc, ret;
231+ struct sockaddr_un addr;
232+
233+ crm_debug_2("called..");
234+
235+ if(guest->connected == FALSE) {
236+ guest->sockfd = g_new(int, 1);
237+ *guest->sockfd = socket(PF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, 0);
238+ if (*guest->sockfd < 0) {
239+ crm_perror(LOG_ERR, "socket(2) call failed:");
240+ crm_free(guest->sockfd);
241+ return -1;
242+ }
243+
244+ crm_debug_3("connecting to [%s]", guest->sock_path);
245+ memset(&addr, 0, sizeof(struct sockaddr_un));
246+ addr.sun_family = AF_UNIX;
247+ g_strlcpy(addr.sun_path, guest->sock_path, sizeof(addr.sun_path)-1);
248+
249+ rc = connect(*guest->sockfd, (struct sockaddr*)&addr, sizeof(struct sockaddr_un));
250+ if (rc < 0) {
251+ if(errno == EAGAIN) {
252+ ret = 1;
253+ goto failed;
254+ } else if(errno == ENOENT) {
255+ crm_info("socket of guest [%s] is not yet made.", guest->name);
256+ } else if(errno == ECONNREFUSED){
257+ crm_info("socket file is exist, but a guest [%s] does not started.",
258+ guest->name);
259+ } else {
260+ crm_perror(LOG_ERR, "connect(2) call failed:");
261+ }
262+ ret = -1;
263+ goto failed;
264+ }
265+
266+ guest->connected = TRUE;
267+ guest->sourceid = g_new(guint, 1);
268+ guest->ioch = g_io_channel_unix_new(*guest->sockfd);
269+ g_io_channel_set_flags(guest->ioch, !G_IO_FLAG_NONBLOCK, NULL);
270+ /* ゲストからのメッセージ待ち受けハンドラ設定 */
271+ *guest->sourceid = g_io_add_watch_full(guest->ioch,
272+ G_PRIORITY_DEFAULT, G_IO_IN|G_IO_ERR|G_IO_HUP,
273+ on_msg_arrived, guest, guest_connection_destroy_notify);
274+ crm_debug_4("create guest [%s] io watch source id [%d].",
275+ guest->name, *guest->sourceid);
276+ g_hash_table_insert(io_watch, guest->sockfd, guest->sourceid);
277+ crm_info("succeeded in connection to guest [%s] socket [%d].",
278+ guest->name, *guest->sockfd);
279+ /* live migration対応 */
280+ send_message(*guest->sockfd, T_MIGRATION_OCCURRED, NULL, NULL);
281+
282+ return 0;
283+ }
284+ crm_debug_3("already connected guest [%s] socket [%d].", guest->name, *guest->sockfd);
285+ return 0;
286+
287+failed:
288+ close(*guest->sockfd);
289+ crm_free(guest->sockfd);
290+ return ret;
291+}
292+
293+static char *
294+optimize_path(const char *path)
295+{
296+ char *return_path = NULL;
297+ int i, j=0;
298+ gboolean flg = FALSE;
299+
300+ if(path == NULL) {
301+ return NULL;
302+ }
303+
304+ crm_malloc0(return_path, strlen(path)+1);
305+ for(i=0;i<strlen(path);i++) {
306+ if(path[i] == '/') {
307+ if(flg) continue;
308+ flg = TRUE;
309+ } else {
310+ flg = FALSE;
311+ }
312+ return_path[j++] = path[i];
313+ }
314+ crm_debug_3("optimized path[%s]", return_path);
315+
316+ return return_path;
317+}
318+
319+static void
320+parse_libvirt_conf(gpointer key, gpointer value, gpointer user_data)
321+{
322+ int rc;
323+ guest_t *guest = (guest_t *)value;
324+ char *sock_path = NULL;
325+ xmlNode *conf_root = NULL;
326+ xmlNode *device_root = NULL;
327+ xmlNode *name_root = NULL;
328+ xmlNode *target_node = NULL;
329+ xmlNode *source_node = NULL;
330+
331+ crm_debug_3("parse to guest config [%s].", guest->conf_path);
332+ conf_root = filename2xml(guest->conf_path);
333+ if(conf_root == NULL) {
334+ crm_err("failed to convert a file into XML [%s].", guest->conf_path);
335+ return;
336+ }
337+
338+ /* search guest name */
339+ name_root = find_xml_node(conf_root, "name", FALSE);
340+ if(name_root == NULL || name_root->children == NULL) {
341+ crm_err("failed in the getting of the name of guest [%s] config file [%s].",
342+ (char *)key, guest->conf_path);
343+ goto end;
344+ }
345+
346+ crm_free(guest->name);
347+ guest->name = g_strdup((const char*)name_root->children->content);
348+ crm_debug_3("guest name [%s].", guest->name);
349+
350+ /*
351+ * search guest socket path
352+ */
353+ device_root = find_xml_node(conf_root, "devices", FALSE);
354+ if(device_root == NULL) {
355+ crm_err("guest [%s] does not have setting of device.", guest->name);
356+ goto end;
357+ }
358+
359+ crm_free(guest->sock_path);
360+ xml_child_iter_filter(device_root, channel, "channel",
361+ target_node = find_xml_node(channel, "target", FALSE);
362+ /* excludes you anything other than <target name=vmconnectd> */
363+ if(safe_str_neq(SCD_NAME, crm_element_value(target_node, "name"))) {
364+ crm_debug_3("target [%s] which this channel has is not [%s].",
365+ crm_element_value(target_node, "name"), SCD_NAME);
366+ continue;
367+ }
368+
369+ /* When multiple effective sock_path are set; an error */
370+ if(guest->sock_path != NULL) {
371+ crm_err("multiple channels for vm-connectd are set");
372+ goto end;
373+ }
374+
375+ source_node = find_xml_node(channel, "source", FALSE);
376+ sock_path = optimize_path(crm_element_value(source_node, "path"));
377+ if(sock_path == NULL) {
378+ crm_err("guest resource [%s] does not have an effective socket path [%s].",
379+ (char *)key, guest->conf_path);
380+ goto end;
381+ }
382+
383+ /* check guest directory path */
384+ if(g_ascii_strncasecmp(sock_dir, sock_path, strlen(sock_dir)) != 0) {
385+ crm_err("not the socket path [%s] of the guest in a setting directory [%s].",
386+ sock_path, sock_dir);
387+ goto end;
388+ } else if(g_strrstr(sock_path+strlen(sock_dir), "/") != NULL) {
389+ crm_err("not the socket path [%s] of the guest in a setting directory [%s].",
390+ sock_path, sock_dir);
391+ goto end;
392+ }
393+
394+ guest->sock_path = crm_strdup(sock_path);
395+ crm_debug_3("socket file path [%s].", guest->sock_path);
396+ crm_free(sock_path);
397+ );
398+
399+ /* connect to guest socket */
400+ if(guest->sock_path != NULL) {
401+ rc = connect_to_guest(guest);
402+ if(rc > 0 && guest->reconnect_timer == 0) {
403+ crm_info("guest [%s] tries connection again.", guest->name);
404+ guest->reconnect_timer = g_timeout_add(1000, reconnect_to_guest, guest);
405+ crm_debug_3("guest reconnect timer [%d].", guest->reconnect_timer);
406+ }
407+ }
408+
409+end:
410+ crm_free(sock_path);
411+ free_xml(conf_root);
412+ return;
413+}
414+
415+static int
416+create_guest_info_for_cib(void)
417+{
418+ guest_t *guest = NULL;
419+ const char *conf_path = NULL;
420+ xmlNode *cib_copy = NULL;
421+ xmlNode *resources = NULL;
422+ xmlNode *attr_set = NULL;
423+
424+ crm_debug_2("called..");
425+ cib_copy = get_cib_copy(cib_conn);
426+ if(cib_copy == NULL) {
427+ crm_err("failed to get cib copy.");
428+ return -1;
429+ }
430+
431+ resources = get_object_root(XML_CIB_TAG_RESOURCES, cib_copy);
432+ if(resources == NULL) {
433+ crm_err("failed to get resources node.");
434+ free_xml(cib_copy);
435+ return -1;
436+ }
437+
438+ xml_child_iter_filter(resources, resource, XML_CIB_TAG_RESOURCE,
439+ /* VirtualDomain RA search */
440+ if(safe_str_neq("VirtualDomain", crm_element_value(resource, XML_ATTR_TYPE))) {
441+ continue;
442+ }
443+ attr_set = find_xml_node(resource, XML_TAG_ATTR_SETS, FALSE);
444+ xml_child_iter_filter(attr_set, param, XML_CIB_TAG_NVPAIR,
445+ if(safe_str_neq(crm_element_value(param, XML_NVPAIR_ATTR_NAME), "config")) {
446+ continue;
447+ }
448+ guest = g_hash_table_lookup(guest_hash, ID(resource));
449+ conf_path = crm_element_value(param, XML_NVPAIR_ATTR_VALUE);
450+ if(guest == NULL) {
451+ /* create new guest info */
452+ crm_malloc0(guest, sizeof(guest_t));
453+ guest->id = crm_strdup(ID(resource));
454+ guest->conf_path = crm_strdup(conf_path);
455+ g_hash_table_insert(guest_hash, guest->id, guest);
456+ } else {
457+ if(safe_str_eq(conf_path, guest->conf_path)) {
458+ crm_debug_3("already store guest config path %s.",
459+ guest->conf_path);
460+ continue;
461+ }
462+ crm_free(guest->conf_path);
463+ guest->conf_path = crm_strdup(conf_path);
464+ }
465+ );
466+ );
467+
468+ g_hash_table_foreach(guest_hash, parse_libvirt_conf, NULL);
469+ free_xml(cib_copy);
470+
471+ return 0;
472+}
473+
474+static void
475+file_monitor_callback(GFileMonitor *monitor, GFile *file, GFile *other,
476+ GFileMonitorEvent event_type, gchar *unused)
477+{
478+ gchar *path = g_file_get_path(file);
479+ time_t timer = time(NULL);
480+ struct tm *date = localtime(&timer);
481+ char timestr[128];
482+ int rc;
483+
484+ crm_debug_2("called..");
485+ strftime(timestr, sizeof(timestr)-1, "%H:%M:%S - ", date);
486+
487+ switch (event_type) {
488+ case G_FILE_MONITOR_EVENT_CREATED:
489+ crm_debug_3("%s[%s] : CREATED.", timestr, path);
490+ /* GUEST INFO RECHECK */
491+ rc = create_guest_info_for_cib();
492+ if(rc < 0) {
493+ crm_err("failed to create guest information.");
494+ }
495+ break;
496+ default:
497+ break;
498+ }
499+ crm_free(path);
500+
501+ return;
502+}
503+
504+static int
505+connect_to_host(const char *port)
506+{
507+ int fd;
508+
509+ crm_debug_2("called..");
510+ /* open host connection */
511+ fd = open(port, O_RDWR|O_NONBLOCK);
512+ if(fd < 0) {
513+ crm_perror(LOG_ERR, "No port found %s:", port);
514+ return -1;
515+ }
516+
517+ /* set io watch event */
518+ hostch = g_io_channel_unix_new(fd);
519+ evid = g_io_add_watch_full(hostch, G_PRIORITY_DEFAULT, G_IO_IN|G_IO_ERR|G_IO_HUP,
520+ on_msg_arrived, NULL, NULL);
521+ crm_info("create host socket [%d] io watch event id [%d].", fd, evid);
522+
523+ return fd;
524+}
525+
526+static struct crm_option long_options[] = {
527+ /* Top-level Options */
528+ {"type", 1, 0, 't', "\tset the type of the domain to carry out in \"host\" or \"guest\""},
529+ {"daemonize", 0, 0, 'D', "\tRun in daemon mode"},
530+ {"pid-file", 1, 0, 'p', "\tFile in which to store the process' PID"},
531+ {"sock-dir", 1, 0, 'd', "\tSocket file directory"},
532+ {"verbose", 0, 0, 'V', "\t\tIncrease debug output"},
533+ {"help", 0, 0, '?', "\t\tThis text"},
534+ {0, 0, 0, 0}
535+};
536+
537+static gboolean
538+detect_connection_client(GIOChannel *channel, GIOCondition condition, gpointer unused)
539+{
540+ crm_debug_2("called..");
541+ if(g_main_context_find_source_by_id(NULL, evid) == NULL) {
542+ crm_info("reconnect it to a host.");
543+ sock_host = connect_to_host(SCD_PATH "/" SCD_NAME);
544+ return TRUE;
545+ }
546+ crm_debug_3("already connected to the host.");
547+
548+ return TRUE;
549+}
550+
551+int
552+main(int argc, char **argv)
553+{
554+ int argerr = 0;
555+ int option_index = 0;
556+ int flag;
557+ int rc;
558+ const char *domain_type = NULL;
559+ const char *pid_file = NULL;
560+ gboolean daemonize = FALSE;
561+ pid_file = "/var/run/vm-connectd.pid";
562+ sock_dir = GUEST_SOCKDIR;
563+
564+ signal(SIGINT, vm_connectd_shutdown);
565+ signal(SIGTERM, vm_connectd_shutdown);
566+ signal(SIGPIPE, SIG_IGN);
567+ crm_log_init(basename(argv[0]), LOG_INFO, TRUE, FALSE, argc, argv);
568+ crm_set_options("Dp:d:V?t:", "-t [host|guest] [Options]", long_options,
569+ "This daemon performs a host, communication"
570+ " between guests with serial communication.");
571+
572+ /* option */
573+ while (1) {
574+ flag = crm_get_option(argc, argv, &option_index);
575+ if (flag == -1)
576+ break;
577+
578+ switch(flag) {
579+ case 'D':
580+ daemonize = TRUE;
581+ break;
582+ case 'p':
583+ pid_file = optarg;
584+ break;
585+ case 'd':
586+ sock_dir = optarg;
587+ break;
588+ case 'V':
589+ cl_log_enable_stderr(TRUE);
590+ alter_debug(DEBUG_INC);
591+ break;
592+ case '?':
593+ crm_help(flag, LSB_EXIT_OK);
594+ break;
595+ case 't':
596+ domain_type = optarg;
597+ break;
598+ default:
599+ printf("Argument code 0%o (%c) is not (?yet?) supported\n",
600+ flag, flag);
601+ crm_err("Argument code 0%o (%c) is not (?yet?) supported",
602+ flag, flag);
603+ ++argerr;
604+ break;
605+ }
606+ }
607+
608+ if (optind < argc) {
609+ crm_err("non-option ARGV-elements: ");
610+ printf("non-option ARGV-elements: ");
611+ while (optind < argc) {
612+ crm_err("%s ", argv[optind]);
613+ printf("%s ", argv[optind++]);
614+ }
615+ printf("\n");
616+ }
617+
618+ if (argerr) {
619+ crm_help(flag, LSB_EXIT_GENERIC);
620+ }
621+
622+ if(safe_str_neq("host", domain_type) && safe_str_neq("guest", domain_type)) {
623+ crm_err("There was not the setting of the type.");
624+ crm_help(flag, LSB_EXIT_GENERIC);
625+ }
626+
627+ crm_make_daemon(crm_system_name, daemonize, pid_file);
628+
629+ /* create daemon socket */
630+ listen_sock = listen_to(SOCK_PATH);
631+ if(listen_sock < 0) {
632+ crm_err("failed to create listen socket.");
633+ exit(1);
634+ }
635+
636+ if(safe_str_eq("host", domain_type)) {
637+ char *tmp = NULL;
638+ tmp = g_strjoin("", sock_dir, "/", NULL);
639+ sock_dir = optimize_path(tmp);
640+ g_free(tmp);
641+ /* create guest hash */
642+ guest_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
643+ NULL, free_guest_info);
644+
645+ rc = cib_connect(NULL);
646+ if(rc < 0) {
647+ crm_err("failed to connect to cib.");
648+ exit(1);
649+ }
650+
651+ rc = create_guest_info_for_cib();
652+ if(rc < 0) {
653+ crm_err("failed to create guest information.");
654+ exit(1);
655+ }
656+
657+ set_file_monitor(sock_dir);
658+
659+ } else if(safe_str_eq("guest", domain_type)) {
660+ on_host = FALSE;
661+ sock_host = connect_to_host(SCD_PATH "/" SCD_NAME);
662+ if(sock_host < 0) {
663+ crm_err("failed to connect to host.");
664+ exit(1);
665+ }
666+
667+ g_io_add_watch_full(g_io_channel_unix_new(listen_sock),
668+ G_PRIORITY_DEFAULT, G_IO_IN|G_IO_HUP,
669+ detect_connection_client, NULL, NULL);
670+
671+ }
672+
673+ mainloop = g_main_loop_new(NULL, FALSE);
674+ mainloop_add_signal(SIGTERM, vm_connectd_shutdown);
675+ mainloop_add_signal(SIGINT, vm_connectd_shutdown);
676+
677+ crm_info("Starting.");
678+ g_main_loop_run(mainloop);
679+
680+ if(cib_conn) {
681+ cib_conn->cmds->signoff(cib_conn);
682+ cib_delete(cib_conn);
683+ }
684+
685+ crm_info("Exitting.");
686+ return 0;
687+}
688+
diff -r 000000000000 -r 441a96ed873f tools/vm-managerd.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/vm-managerd.c Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,778 @@
1+/* -------------------------------------------------------------------------
2+ * vm_manager --- monitors shared disk.
3+ * This applied pingd mechanism to disk monitor.
4+ *
5+ * This program is free software; you can redistribute it and/or
6+ * modify it under the terms of the GNU General Public
7+ * License as published by the Free Software Foundation; either
8+ * version 2.1 of the License, or (at your option) any later version.
9+ *
10+ * This software is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+ * General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public
16+ * License along with this library; if not, write to the Free Software
17+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+ *
19+ * Copyright (c) 2008 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
20+ *
21+ * -------------------------------------------------------------------------
22+ */
23+
24+#include <sys/param.h>
25+
26+#include <stdio.h>
27+#include <sys/types.h>
28+#include <sys/stat.h>
29+#include <sys/ioctl.h>
30+#include <sys/utsname.h>
31+#include <unistd.h>
32+
33+#include <stdlib.h>
34+#include <errno.h>
35+#include <fcntl.h>
36+#include <libgen.h>
37+#include <time.h>
38+#include <string.h>
39+
40+#include <vm_connect.h>
41+
42+#include <crm/common/ipc.h>
43+#include <crm/common/xml.h>
44+#include <crm/common/cluster.h>
45+#include <crm/msg_xml.h>
46+#include <crm/crm.h>
47+#include <crm/cib.h>
48+
49+#ifdef HAVE_GETOPT_H
50+# include <getopt.h>
51+#endif
52+
53+#define OPTARGS "p:c:n:v:DV?"
54+#define CONFIG_FILE "/etc/vm-manager.conf"
55+#define PID_FILE "/var/run/vm-managerd.pid"
56+
57+static gboolean vm_manager_reveive_message(GIOChannel *source, GIOCondition condition, gpointer data);
58+static char * get_attribute_from_cib(const char *attr_name);
59+static char * convert_attribute(const char *attr_name, char *attr_value);
60+static int convert_rule_to_int(char *expr);
61+
62+static int cib_connect(void);
63+static void cib_connection_destroy(gpointer user_data);
64+static gboolean cib_reconnect(gpointer data);
65+
66+static void re_read_config(int nsig);
67+static int read_config(char *config_file);
68+
69+static void clean_up(int rc);
70+static void attr_hash_cleanup(gpointer data);
71+static void vm_manager_shutdown(int nsig);
72+static void usage(const char *cmd, int exit_status);
73+
74+const char *crm_system_name = "vm-managerd";
75+
76+enum convert_exprs {
77+ expr_unknown = -1,
78+ expr_eq = 0,
79+ expr_ne = 1,
80+ expr_lt = 2,
81+ expr_gt = 3,
82+ expr_lte = 4,
83+ expr_gte = 5,
84+};
85+
86+typedef struct attribute_s
87+{
88+ char *name;
89+ GList *rule_list;
90+} attribute_t;
91+
92+typedef struct rule_s
93+{
94+ int expr;
95+ char *conparison;
96+ char *convert_string;
97+} rule_t;
98+
99+GMainLoop* mainloop = NULL;
100+GIOChannel* connect_ch = NULL;
101+GHashTable *attribute_hash = NULL;
102+
103+guint timer_id = 0;
104+
105+crm_node_t *self = NULL;
106+cib_t *cib = NULL;
107+char *pid_file = NULL;
108+char *config_file = NULL;
109+char *default_attr_name = NULL;
110+char *default_attr_value = NULL;
111+
112+static gboolean
113+connect_to_vmconnect(gpointer data)
114+{
115+ int wfd;
116+
117+ crm_debug_2("connected to vm-connectd.");
118+ wfd = connect_to(SOCK_PATH, T_MOD_MONITOR);
119+ if (wfd < 0) {
120+ return FALSE;
121+ }
122+ crm_debug_2("succeeded in connection of vm-connectd.");
123+
124+ connect_ch = g_io_channel_unix_new(wfd);
125+ g_io_add_watch(connect_ch, G_IO_IN, vm_manager_reveive_message, NULL);
126+
127+ return TRUE;
128+}
129+
130+static gboolean
131+vm_manager_reveive_message(GIOChannel *source, GIOCondition condition, gpointer data)
132+{
133+ char *tmp_buffer = NULL;
134+ char **msg_array;
135+ char *attr_value = NULL;
136+ char *res_value = crm_strdup("");
137+ int i, rc;
138+ int sockfd;
139+ vm_message msg;
140+
141+ sockfd = g_io_channel_unix_get_fd(source);
142+ crm_debug_2("server connect socket [%d].", sockfd);
143+
144+ rc = receive_msg(sockfd, &msg);
145+ if(rc < 0) {
146+ crm_err("failed to receive message.");
147+ return TRUE;
148+ } else if(rc == 1) {
149+ crm_info("session with the server was disconnected.");
150+ vm_manager_shutdown(0);
151+ return FALSE;
152+ }
153+
154+ if(msg.info.datalen == 0) {
155+ crm_warn("The data which had been sent by guest [%d] were empty.",
156+ msg.info.sock_guest);
157+ return TRUE;
158+ }
159+
160+ msg_array = g_strsplit(msg.data, " ", 0);
161+ crm_free(msg.data);
162+
163+ /* tokenを1つずつ処理 */
164+ for(i = 0; i < g_strv_length(msg_array); i++) {
165+ crm_debug_2("request [%d][%s]\n", i, msg_array[i]);
166+ /* tokenが空の場合は無視する */
167+ if(strlen(msg_array[i]) == 0) {
168+ crm_debug_2("ignore the empty token.");
169+ continue;
170+ }
171+
172+ attr_value = get_attribute_from_cib(msg_array[i]);
173+
174+ if(i < g_strv_length(msg_array)-1) {
175+ tmp_buffer = g_strconcat(res_value, msg_array[i],
176+ "=", attr_value != NULL ? attr_value : "", ",", NULL);
177+ } else {
178+ tmp_buffer = g_strconcat(res_value, msg_array[i],
179+ "=", attr_value != NULL ? attr_value : "", NULL);
180+ }
181+
182+ crm_free(res_value);
183+ res_value = crm_strdup(tmp_buffer);
184+ crm_free(tmp_buffer);
185+ crm_free(attr_value);
186+ crm_debug_2("result[%s]\n", res_value);
187+ }
188+
189+ msg.data = res_value;
190+ msg.info.datalen = strlen(res_value);
191+ /* vm-clientに結果を送信 */
192+ rc = send_msg(sockfd, &msg);
193+ if(rc < 0) {
194+ crm_err("failed to send a %s", res_value);
195+ }
196+
197+ crm_free(res_value);
198+ g_strfreev(msg_array);
199+
200+ return TRUE;
201+}
202+
203+/* shutdown用処理 */
204+static void
205+vm_manager_shutdown(int nsig)
206+{
207+ if (mainloop != NULL && g_main_is_running(mainloop)) {
208+ g_main_quit(mainloop);
209+ } else {
210+ clean_up(LSB_EXIT_OK);
211+ }
212+
213+ return;
214+}
215+
216+/*
217+ * CIB解析処理(属性名に対応した属性値取得)
218+ */
219+static char *
220+get_attribute_from_cib(const char *attr_name)
221+{
222+ int rc = cib_ok;
223+ char *attr_value = NULL;
224+ char *return_string = NULL;
225+
226+ rc = read_attr(cib, XML_CIB_TAG_STATUS, self->uuid, NULL, NULL,
227+ attr_name, &attr_value, FALSE);
228+
229+ if(rc != cib_ok) {
230+ crm_warn("failed to get attribute %s: %s", attr_name, cib_error2string(rc));
231+ return NULL;
232+ }
233+
234+ return_string = crm_strdup(convert_attribute(attr_name, attr_value));
235+ crm_free(attr_value);
236+
237+ /* free after use */
238+ return return_string;
239+}
240+
241+/* CIB再接続処理 */
242+static gboolean
243+cib_reconnect(gpointer data)
244+{
245+ int rc = cib_ok;
246+
247+ if(timer_id > 0) {
248+ g_source_remove(timer_id);
249+ }
250+
251+ rc = cib_connect();
252+
253+ if(rc != cib_ok) {
254+ timer_id = g_timeout_add(1000, cib_reconnect, NULL);
255+ }
256+
257+ return FALSE;
258+}
259+
260+/* CIB切断時処理 */
261+static void
262+cib_connection_destroy(gpointer user_data)
263+{
264+ crm_info("Connection to the CIB terminated");
265+
266+ if(cib) {
267+ crm_info("CIB Reconnecting...");
268+ cib->cmds->signoff(cib);
269+ timer_id = g_timeout_add(1000, cib_reconnect, NULL);
270+ }
271+
272+ return;
273+}
274+
275+/* CIB接続処理 */
276+static int cib_connect(void)
277+{
278+ int rc = cib_ok;
279+ CRM_CHECK(cib != NULL, return cib_missing);
280+
281+ /* cib接続確認 */
282+ if(cib->state != cib_connected_query
283+ && cib->state != cib_connected_command) {
284+ crm_debug("Connecting to the CIB");
285+
286+ rc = cib->cmds->signon(cib, crm_system_name, cib_query);
287+
288+ if(rc != cib_ok) {
289+ crm_err("CIB signon failure: %s",
290+ cib_error2string(rc));
291+ return rc;
292+ }
293+
294+ if(rc == cib_ok) {
295+ /* CIB切断時に呼び出される関数をセット */
296+ rc = cib->cmds->set_connection_dnotify(cib, cib_connection_destroy);
297+ if(rc == cib_NOTSUPPORTED) {
298+ crm_info("Notification setup failed, won't be able to"
299+ " reconnect after failure");
300+ rc = cib_ok;
301+ }
302+
303+ }
304+
305+ if(rc != cib_ok) {
306+ crm_err("Notification setup failed, could not monitor CIB actions: %s",
307+ cib_error2string(rc));
308+ clean_up(LSB_EXIT_GENERIC);
309+ }
310+ }
311+
312+ return rc;
313+}
314+
315+static char *
316+convert_attribute(const char *attr_name, char *attr_value)
317+{
318+ int left;
319+ int right;
320+ char *check_ptr = NULL;
321+ attribute_t *attr = NULL;
322+ GList *list = NULL;
323+
324+ attr = g_hash_table_lookup(attribute_hash, attr_name);
325+
326+ /* 該当する変換ルールが存在しなかった */
327+ if(attr == NULL) {
328+ crm_debug_2("There was not the conversion rule of %s.", attr_name);
329+ return attr_value;
330+ }
331+
332+ for(list = g_list_first(attr->rule_list);
333+ list != NULL; list = g_list_next(list)) {
334+
335+ rule_t *rule = list->data;
336+
337+ /* 文字列型チェック */
338+ switch(rule->expr) {
339+ case expr_eq:
340+ crm_debug_3("eq expr\n");
341+ if(safe_str_eq(attr_value, rule->conparison)) {
342+ return rule->convert_string;
343+ }
344+ break;
345+ case expr_ne:
346+ crm_debug_3("ne expr\n");
347+ if(! safe_str_eq(attr_value, rule->conparison)) {
348+ return rule->convert_string;
349+ }
350+ break;
351+ }
352+
353+ left = crm_int_helper(attr_value, &check_ptr);
354+ if(errno != 0 || check_ptr[0] != '\0') {
355+ continue;
356+ }
357+ crm_debug_3("before[%s]/after[%d]\n", attr_value, left);
358+
359+ right = crm_int_helper(rule->conparison, &check_ptr);
360+ if(errno != 0 || check_ptr[0] != '\0') {
361+ continue;
362+ }
363+ crm_debug_3("before[%s]/after[%d]\n", rule->conparison, right);
364+
365+ /* 数値型チェック */
366+ switch(rule->expr) {
367+ case expr_lt:
368+ crm_debug_3("lt expr\n");
369+ if(left < right) {
370+ return rule->convert_string;
371+ }
372+ break;
373+ case expr_gt:
374+ crm_debug_3("gt expr\n");
375+ if(left > right) {
376+ return rule->convert_string;
377+ }
378+ break;
379+ case expr_lte:
380+ crm_debug_3("lte expr\n");
381+ if(left <= right) {
382+ return rule->convert_string;
383+ }
384+ break;
385+ case expr_gte:
386+ crm_debug_3("gte expr\n");
387+ if(left >= right) {
388+ return rule->convert_string;
389+ }
390+ break;
391+ }
392+ }
393+
394+ crm_debug_2("attribute %s fulfilled no rule.", attr_value);
395+ return attr_value;
396+}
397+
398+static int
399+convert_rule_to_int(char *expr)
400+{
401+ int expr_num = expr_unknown;
402+
403+ if(safe_str_eq(expr, "eq")) {
404+ expr_num = expr_eq;
405+ } else if(safe_str_eq(expr, "ne")) {
406+ expr_num = expr_ne;
407+ } else if(safe_str_eq(expr, "lt")) {
408+ expr_num = expr_lt;
409+ } else if(safe_str_eq(expr, "gt")) {
410+ expr_num = expr_gt;
411+ } else if(safe_str_eq(expr, "lte")) {
412+ expr_num = expr_lte;
413+ } else if(safe_str_eq(expr, "gte")) {
414+ expr_num = expr_gte;
415+ }
416+
417+ return expr_num;
418+}
419+
420+static void
421+re_read_config(int nsig)
422+{
423+ int rc;
424+
425+ if(attribute_hash) {
426+ g_hash_table_destroy(attribute_hash);
427+ }
428+
429+ /* configファイル読み込み */
430+ rc = read_config(config_file);
431+ if(rc != 0) {
432+ crm_err("failed to re-read config file.");
433+ clean_up(LSB_EXIT_GENERIC);
434+ }
435+
436+ return;
437+}
438+
439+static int
440+read_config(char *config_file)
441+{
442+ int i, j;
443+ gboolean rc;
444+ GError *error = NULL;
445+ gsize groups_length = 0;
446+ gsize keys_length = 0;
447+ GKeyFile *conf = g_key_file_new();
448+ char **groups = NULL;
449+ char **keys = NULL;
450+ char *value = NULL;
451+ char **split_str = NULL;
452+ attribute_t *attribute = NULL;
453+
454+ attribute_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, attr_hash_cleanup);
455+ crm_info("read a configuration file %s", config_file);
456+
457+ /* configファイル読み込み */
458+ rc = g_key_file_load_from_file(conf, config_file, G_KEY_FILE_NONE, &error);
459+ if(rc == FALSE) {
460+ crm_err("%s: %s", config_file, error->message);
461+ g_key_file_free(conf);
462+ g_error_free(error);
463+ return 1;
464+ }
465+
466+ /* セクション情報読み込み */
467+ groups = g_key_file_get_groups(conf, &groups_length);
468+
469+ for(i = 0; i < groups_length; i++) {
470+ crm_debug_2("group [%s]", groups[i]);
471+ /* 各セクションからkey情報読み込み */
472+ keys = g_key_file_get_keys(conf, groups[i], &keys_length, &error);
473+ if(keys == NULL) {
474+ crm_err("group[%s]: %s", groups[i], error->message);
475+ g_key_file_free(conf);
476+ g_error_free(error);
477+ return 1;
478+ }
479+
480+ crm_malloc0(attribute, sizeof(attribute_t));
481+ attribute->name = crm_strdup(groups[i]);
482+
483+ for(j = 0; j < keys_length; j++) {
484+ rule_t *convert_rule = NULL;
485+ crm_debug_2("group [%s] key [%s]", groups[i], keys[j]);
486+ crm_malloc0(convert_rule, sizeof(rule_t));
487+
488+ /* keyに対応した値の読み込み */
489+ value = g_key_file_get_value(conf, groups[i], keys[j], &error);
490+ if(value == NULL) {
491+ crm_err("group[%s]/key[%s]: %s", groups[i], keys[j], error->message);
492+ g_key_file_free(conf);
493+ g_error_free(error);
494+ return 1;
495+ }
496+
497+ /* 値を式と変換値に分割 */
498+ split_str = g_strsplit(value, " ", 2);
499+ if(g_strv_length(split_str) < 2) {
500+ crm_warn("unjust rule [%s], ignore this section[%s] key[%s].",
501+ value, groups[i], keys[j]);
502+ crm_free(convert_rule);
503+ goto free;
504+ }
505+
506+ convert_rule->expr = convert_rule_to_int(split_str[0]);
507+
508+ if(convert_rule->expr == expr_unknown) {
509+ crm_warn("unjust expression [%s], ignore this section[%s] key[%s].",
510+ split_str[0], groups[i], keys[j]);
511+ crm_free(convert_rule);
512+ goto free;
513+ }
514+
515+ crm_debug("group [%s] key [%s] value [%s]", groups[i], keys[j], value);
516+ convert_rule->convert_string = crm_strdup(keys[j]);
517+ convert_rule->conparison = crm_strdup(split_str[1]);
518+
519+ attribute->rule_list = g_list_append(attribute->rule_list, convert_rule);
520+free:
521+ g_strfreev(split_str);
522+ crm_free(value);
523+ }
524+ g_hash_table_insert(attribute_hash, attribute->name, attribute);
525+ g_strfreev(keys);
526+ }
527+
528+ g_strfreev(groups);
529+ g_key_file_free(conf);
530+
531+ return 0;
532+}
533+
534+static void
535+usage(const char *cmd, int exit_status)
536+{
537+ FILE *stream;
538+
539+ stream = exit_status ? stderr : stdout;
540+
541+ fprintf(stream, "usage: %s [-%s]\n", cmd, OPTARGS);
542+ fprintf(stream, " Basic options\n");
543+ fprintf(stream, "\t--%s (-%c) <filename>\t\tFile in which to store the process' PID\n"
544+ "\t\t\t\t\t\t* Default=%s\n", "pid-file", 'p', PID_FILE);
545+ fprintf(stream, "\t--%s (-%c) <filename>\t\tconfig file\n"
546+ "\t\t\t\t\t\t* Default=%s\n", "config", 'c', CONFIG_FILE);
547+ fprintf(stream, "\t--%s (-%c) <name>\t\t\tThe name of the attribute to update in CIB.\n", "attr-name", 'n');
548+ fprintf(stream, "\t--%s (-%c) <value>\t\tThe value of the attribute to update in CIB.\n", "attr-value", 'v');
549+ fprintf(stream, "\t--%s (-%c) \t\t\tRun in daemon mode\n", "daemonize", 'D');
550+ fprintf(stream, "\t--%s (-%c) \t\t\t\tRun in verbose mode\n", "verbose", 'V');
551+ fprintf(stream, "\t--%s (-%c) \t\t\t\tThis text\n", "help", '?');
552+
553+ fflush(stream);
554+
555+ clean_up(exit_status);
556+}
557+
558+int
559+main(int argc, char **argv)
560+{
561+ int rc;
562+ gboolean judge;
563+ int argerr = 0;
564+ int flag;
565+ gboolean daemonize = FALSE;
566+ struct utsname name;
567+
568+#ifdef HAVE_GETOPT_H
569+ int option_index = 0;
570+ static struct option long_options[] = {
571+ /* Top-level Options */
572+ {"verbose", 0, 0, 'V'},
573+ {"help", 0, 0, '?'},
574+ {"pid-file", 1, 0, 'p'},
575+ {"config", 1, 0, 'c'},
576+ {"attr-name", 1, 0, 'n'},
577+ {"attr-value", 1, 0, 'v'},
578+ {"daemonize", 0, 0, 'D'},
579+ {0, 0, 0, 0}
580+ };
581+#endif
582+ signal(SIGTERM, vm_manager_shutdown);
583+ signal(SIGINT, vm_manager_shutdown);
584+
585+ crm_malloc0(self, sizeof(crm_node_t));
586+ pid_file = crm_strdup(PID_FILE);
587+ config_file = crm_strdup(CONFIG_FILE);
588+ crm_system_name = basename(argv[0]);
589+
590+
591+ crm_log_init(basename(argv[0]), LOG_INFO, TRUE, FALSE, argc, argv);
592+
593+ /* オプション解析 */
594+ while (1) {
595+#ifdef HAVE_GETOPT_H
596+ flag = getopt_long(argc, argv, OPTARGS,
597+ long_options, &option_index);
598+#else
599+ flag = getopt(argc, argv, OPTARGS);
600+#endif
601+ if (flag == -1)
602+ break;
603+
604+ switch(flag) {
605+ case 'V':
606+ cl_log_enable_stderr(TRUE);
607+ alter_debug(DEBUG_INC);
608+ break;
609+ case 'p':
610+ crm_free(pid_file);
611+ pid_file = crm_strdup(optarg);
612+ break;
613+ case 'c':
614+ crm_free(config_file);
615+ config_file = crm_strdup(optarg);
616+ break;
617+ case 'n':
618+ default_attr_name = crm_strdup(optarg);
619+ break;
620+ case 'v':
621+ default_attr_value = crm_strdup(optarg);
622+ break;
623+ case 'D':
624+ daemonize = TRUE;
625+ break;
626+ case '?':
627+ usage(crm_system_name, LSB_EXIT_GENERIC);
628+ break;
629+ default:
630+ printf ("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
631+ crm_err("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag);
632+ ++argerr;
633+ break;
634+ }
635+ }
636+
637+
638+ if (optind < argc) {
639+ crm_err("non-option ARGV-elements: ");
640+ printf ("non-option ARGV-elements: ");
641+ while (optind < argc) {
642+ crm_err("%s ", argv[optind]);
643+ printf("%s ", argv[optind]);
644+ optind++;
645+ }
646+ printf("\n");
647+ argerr ++;
648+ }
649+
650+ if (argerr > 0 || (default_attr_name != NULL && default_attr_value == NULL) ||
651+ (default_attr_name == NULL && default_attr_value != NULL)) {
652+ usage(crm_system_name, LSB_EXIT_GENERIC);
653+ }
654+
655+ /* デーモン化 */
656+ crm_make_daemon(crm_system_name, daemonize, pid_file);
657+
658+ /* CIB接続 */
659+ cib = cib_new();
660+ do {
661+ crm_debug_2("connect to cib.");
662+ rc = cib_connect();
663+ if(rc != cib_ok) {
664+ sleep(1);
665+ }
666+
667+ } while(rc == cib_connection);
668+
669+ /* ユーザー指定の属性をCIBに更新 */
670+ if(default_attr_name != NULL && default_attr_value != NULL) {
671+ judge = attrd_lazy_update('U', NULL,
672+ default_attr_name, default_attr_value, NULL, NULL, 0);
673+ if(judge == FALSE) {
674+ crm_err("failed in update of the attribute value.");
675+ clean_up(LSB_EXIT_GENERIC);
676+ }
677+ }
678+
679+ /* 自ノード名の取得 */
680+ rc = uname(&name);
681+ if(rc < 0) {
682+ crm_perror(LOG_ERR, "uname(2) call failed");
683+ clean_up(LSB_EXIT_GENERIC);
684+ }
685+
686+ self->uname = crm_strdup(name.nodename);
687+ crm_info("Detected uname: %s", self->uname);
688+
689+ /* 自ノードのuuid取得 */
690+ rc = query_node_uuid(cib, self->uname, &self->uuid);
691+ if(rc != 0) {
692+ crm_err("failed to get node uuid.");
693+ clean_up(LSB_EXIT_GENERIC);
694+ }
695+
696+ /* configファイル読み込み */
697+ rc = read_config(config_file);
698+ if(rc != 0) {
699+ crm_err("failed to read config file.");
700+ clean_up(LSB_EXIT_GENERIC);
701+ }
702+
703+ /* vm-connectd接続開始 */
704+ judge = connect_to_vmconnect(NULL);
705+ if(judge == FALSE) {
706+ crm_err("failed to connect vm-connectd");
707+ exit(1);
708+ }
709+
710+ crm_info("Starting %s", crm_system_name);
711+
712+ /* mainloop開始 */
713+ mainloop = g_main_new(FALSE);
714+ mainloop_add_signal(SIGTERM, vm_manager_shutdown);
715+ mainloop_add_signal(SIGINT, vm_manager_shutdown);
716+ mainloop_add_signal(SIGHUP, re_read_config);
717+ g_main_run(mainloop);
718+
719+ crm_info("Exiting %s", crm_system_name);
720+
721+ clean_up(LSB_EXIT_OK);
722+
723+ return 0;
724+}
725+
726+/*
727+ * 終了時クリーンアップ
728+ */
729+static void attr_hash_cleanup(gpointer data)
730+{
731+ attribute_t *attr = data;
732+ GList *list = NULL;
733+
734+ for(list = g_list_first(attr->rule_list);
735+ list != NULL; list = g_list_next(list)) {
736+ rule_t *rule = list->data;
737+ crm_free(rule->conparison);
738+ crm_free(rule->convert_string);
739+ crm_free(rule);
740+ }
741+
742+ crm_free(attr);
743+
744+}
745+static void clean_up(int rc)
746+{
747+ gboolean judge;
748+
749+ crm_info("clean up to %s.", crm_system_name);
750+
751+ if(cib != NULL) {
752+ cib->cmds->signoff(cib);
753+ cib_delete(cib);
754+ }
755+
756+ if(attribute_hash) {
757+ g_hash_table_destroy(attribute_hash);
758+ }
759+
760+ if(default_attr_name != NULL && default_attr_value != NULL) {
761+ judge = attrd_lazy_update('D', NULL, default_attr_name, NULL, NULL, NULL, 0);
762+ if(judge == FALSE) {
763+ crm_warn("failed in deletion of the attribute value.");
764+ }
765+ crm_free(default_attr_name);
766+ crm_free(default_attr_value);
767+ }
768+
769+ crm_free(pid_file);
770+ crm_free(config_file);
771+
772+ if(rc >= 0) {
773+ exit(rc);
774+ }
775+
776+ return;
777+}
778+
diff -r 000000000000 -r 441a96ed873f tools/vm-stonithd.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/vm-stonithd.c Thu Sep 15 15:27:04 2011 +0900
@@ -0,0 +1,1003 @@
1+/*
2+ * vm-stonithd : vm-stonith daemon for host.
3+ *
4+ * Copyright (C) 2010 NIPPON TELEGRAPH AND TELEPHONE CORPORATION
5+ *
6+ * This program is free software; you can redistribute it and/or
7+ * modify it under the terms of the GNU General Public
8+ * License as published by the Free Software Foundation; either
9+ * version 2.1 of the License, or (at your option) any later version.
10+ *
11+ * This software is distributed in the hope that it will be useful,
12+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+ * General Public License for more details.
15+ *
16+ * You should have received a copy of the GNU General Public
17+ * License along with this library; if not, write to the Free Software
18+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19+ */
20+
21+#include <stdio.h>
22+#include <libgen.h>
23+#include <sys/wait.h>
24+#include <glib.h>
25+#include <vm_connect.h>
26+#include <clplumbing/proctrack.h>
27+#include <clplumbing/cl_signal.h>
28+#include <crm/cib.h>
29+#include <crm/pengine/status.h>
30+
31+#ifdef HAVE_GETOPT_H
32+# include <getopt.h>
33+#endif
34+
35+#define OPTARGS "Vc:ie:?"
36+#define RESULT_OK "OK"
37+#define RESULT_NG "NG"
38+#define ATTR_NAME_PREFIX "force_stop-"
39+#define ATTR_VALUE "true"
40+
41+enum stonith_op {
42+ OP_UNKNOWN = 0,
43+ OP_POWERON,
44+ OP_POWEROFF,
45+ OP_RESET,
46+ OP_STATUS,
47+ OP_TIMEOUT
48+};
49+
50+struct {
51+ char *opstr;
52+ enum stonith_op op;
53+} opmap[] = {
54+ {"on", OP_POWERON},
55+ {"off", OP_POWEROFF},
56+ {"reset", OP_RESET},
57+ {"status", OP_STATUS},
58+ {"timeout", OP_TIMEOUT},
59+ {0, OP_UNKNOWN}
60+};
61+
62+struct request_s {
63+ enum stonith_op op;
64+ char *arg;
65+ char *id;
66+ ProcTrackKillInfo killseq[2];
67+};
68+
69+enum rsc_status {
70+ RS_STARTED = 1,
71+ RS_STOPPED,
72+ RS_UNDEFINED,
73+ RS_GETERROR
74+};
75+
76+struct chld_status {
77+ struct request_s *req;
78+ char *prev_role;
79+ char *started_node;
80+ char *started_uuid;
81+};
82+
83+void usage(const char *cmd, int exitcode);
84+static void chldDied(ProcTrack *p, int status, int signo, int exitcode, int waslogged);
85+static void chldRegistered(ProcTrack *p);
86+static const char *chldName(ProcTrack *p);
87+void sighdr_term(int signo);
88+void sighdr_term_chld(int signo);
89+void kill_chld(gpointer key, gpointer value, gpointer userdata);
90+gboolean do_shutdown(gpointer unused);
91+void free_chldhash(gpointer data);
92+const char *op2str(enum stonith_op op);
93+gboolean connect_vmconnectd(void);
94+gboolean do_stonith(GIOChannel *channel, GIOCondition condition, gpointer unused);
95+struct request_s *parse_request(const vm_message *msg);
96+gboolean stonith_operate(struct chld_status *stat, vm_message *msg);
97+char *decrypt_data(const char *encrypted_data);
98+char *read_file(const char *path, int max_bufsize);
99+gboolean connect_cib(void);
100+void disconnect_cib(void);
101+gboolean get_pe_dataset(void);
102+void free_pe_dataset(void);
103+enum rsc_status get_rsc_status(const char *rscid, char **started_node, char **started_uuid);
104+gboolean check_rsc_meta(GHashTable *rsc_meta);
105+gboolean start_resource(const char *rscid, char **prev_role);
106+gboolean stop_resource(const char *rscid, char **prev_role, char **started_node, char **started_uuid);
107+gboolean update_status_attr(char command, const char *rscid, const char *node, const char *uuid);
108+gboolean set_rsc_role(const char *rscid, const char *value, char **prev_role);
109+enum cib_errors find_meta_attr(const char *rscid, const char *name, char **id, char **value);
110+
111+GMainLoop *mainloop = NULL;
112+GHashTable *chldhash = NULL;
113+int sockfd = -1;
114+uint gsourceid = 0;
115+char *decrypt_cmd = NULL;
116+gboolean cmd_read_stdin = FALSE;
117+int cmd_ok_exitcode = 0;
118+cib_t *cib_conn = NULL;
119+pe_working_set_t pe_dataset;
120+struct chld_status chldstat;
121+int exit_code = 0;
122+
123+
124+void
125+usage(const char *cmd, int exitcode)
126+{
127+ fprintf(stderr, "usage: %s [options]\n", cmd);
128+ fprintf(stderr, "\nOptions:\n");
129+ fprintf(stderr, " -%c, --%s\t\t\tThis text\n", '?', "help");
130+ fprintf(stderr, " -%c, --%s\t\t\tIncrease the debug output\n", 'V', "verbose");
131+ fprintf(stderr, " -%c, --%s\t\tCommand for decrypting encrypted resource ID\n"
132+ "\t\t\t\t* Required option\n", 'c', "decrypt-cmd");
133+ fprintf(stderr, " -%c, --%s\t\tDecrypting command reads encrypted data from"
134+ " standard input\n", 'i', "cmd-read-stdin");
135+ fprintf(stderr, " -%c, --%s\t\tExit code in case the result of decryption"
136+ " command is OK\n\t\t\t\t* Default=0\n\n", 'e', "cmd-ok-exitcode");
137+ exit(exitcode);
138+}
139+
140+static ProcTrack_ops ChldTrackOps =
141+{
142+ chldDied,
143+ chldRegistered,
144+ chldName
145+};
146+
147+static void
148+chldDied(ProcTrack *p, int status, int signo, int exitcode, int waslogged)
149+{
150+ pid_t pid = proctrack_pid(p);
151+
152+ crm_debug_2("called..");
153+
154+ g_hash_table_remove(chldhash, &pid);
155+ crm_free(p->privatedata);
156+ reset_proctrack_data(p);
157+ return;
158+}
159+
160+static void
161+chldRegistered(ProcTrack *p)
162+{
163+ crm_debug_2("called..");
164+ crm_info("Child process %s started (pid=%d)", p->ops->proctype(p), proctrack_pid(p));
165+ return;
166+}
167+
168+static const char *
169+chldName(ProcTrack *p)
170+{
171+ crm_debug_2("called..");
172+ crm_debug_2("process name: %s", (char*)proctrack_data(p));
173+ return (char*)proctrack_data(p);
174+}
175+
176+void
177+sighdr_term(int signo)
178+{
179+ crm_debug_2("called..");
180+
181+ g_source_remove(gsourceid);
182+ g_hash_table_foreach(chldhash, kill_chld, NULL);
183+ g_timeout_add(1000, do_shutdown, NULL);
184+ return;
185+}
186+
187+#define BOOL2MSGSTR(bool) bool == TRUE ? "Succeed" : "Failed"
188+void
189+sighdr_term_chld(int signo)
190+{
191+ gboolean ret = TRUE;
192+ char *msgupd = NULL, *msgdel = NULL;
193+ struct request_s *req = chldstat.req;
194+
195+ crm_debug_2("called..");
196+
197+ /* First, logging the all messages about processing to be performed from now on. */
198+ if (chldstat.prev_role != NULL) {
199+ msgupd = g_strdup_printf("update meta attribute: name=%s value=%s of rsc=%s",
200+ XML_RSC_ATTR_TARGET_ROLE, chldstat.prev_role, req->arg);
201+ crm_notice("%s", msgupd);
202+ }
203+ if (chldstat.started_node != NULL) {
204+ msgdel = g_strdup_printf("delete attribute: name=%s%s of %s",
205+ ATTR_NAME_PREFIX, req->arg, chldstat.started_node);
206+ crm_notice("%s", msgdel);
207+ }
208+
209+ /* Then, actually performs. */
210+ if (chldstat.prev_role != NULL) {
211+ ret = set_rsc_role(req->arg, chldstat.prev_role, NULL);
212+ crm_notice("%s : %s", msgupd, BOOL2MSGSTR(ret));
213+ }
214+ if (chldstat.started_node != NULL) {
215+ crm_debug("op=%s, rscid=%s, node=%s",
216+ op2str(req->op), req->arg, chldstat.started_node);
217+ gboolean rc = update_status_attr('D', req->arg,
218+ chldstat.started_node, chldstat.started_uuid);
219+ if (rc == FALSE) {
220+ ret = FALSE;
221+ }
222+ crm_notice("%s : %s", msgdel, BOOL2MSGSTR(rc));
223+ }
224+ disconnect_cib();
225+ crm_info("Exiting %s child process", crm_system_name);
226+ exit(ret == TRUE ? 0 : 1);
227+}
228+
229+void
230+kill_chld(gpointer key, gpointer value, gpointer userdata)
231+{
232+ pid_t *pid = key;
233+ struct request_s *req = value;
234+
235+ crm_debug_2("called..");
236+
237+ if (userdata != NULL && safe_str_neq(req->id, (char*)userdata)) {
238+ return;
239+ }
240+
241+ crm_info("send SIGTERM to %s process [%s %s] (pid=%d)",
242+ crm_system_name, op2str(req->op), req->arg, *pid);
243+ if (CL_KILL(*pid, SIGTERM) < 0) {
244+ if (errno == ESRCH) {
245+ return;
246+ }
247+ crm_err("kill (%d, %d) failed", *pid, SIGTERM);
248+ }
249+ req->killseq[0].mstimeout = 10 * 1000;
250+ req->killseq[0].signalno = SIGKILL;
251+ req->killseq[1].mstimeout = 5 * 1000;
252+ req->killseq[1].signalno = 0;
253+ SetTrackedProcTimeouts(*pid, req->killseq);
254+ return;
255+}
256+
257+gboolean
258+do_shutdown(gpointer unused)
259+{
260+ crm_debug_2("called..");
261+
262+ if (g_hash_table_size(chldhash) > 0) {
263+ crm_debug_2("waiting..");
264+ return TRUE;
265+ }
266+ g_hash_table_destroy(chldhash);
267+ close(sockfd);
268+ g_main_loop_quit(mainloop);
269+ return FALSE;
270+}
271+
272+void
273+free_chldhash(gpointer data)
274+{
275+ crm_debug_2("called..");
276+
277+ crm_free(((struct request_s*)data)->arg);
278+ crm_free(((struct request_s*)data)->id);
279+ crm_free(data);
280+ return;
281+}
282+
283+const char *
284+op2str(enum stonith_op op)
285+{
286+ int i;
287+ for (i = 0; opmap[i].opstr; i++) {
288+ if (opmap[i].op == op) {
289+ return opmap[i].opstr;
290+ }
291+ }
292+ return NULL;
293+}
294+
295+gboolean
296+connect_vmconnectd(void)
297+{
298+ crm_debug_2("called..");
299+
300+ sockfd = connect_to(SOCK_PATH, T_MOD_STONITH);
301+ if (sockfd < 0) {
302+ return FALSE;
303+ }
304+ gsourceid = g_io_add_watch(g_io_channel_unix_new(sockfd), G_IO_IN, do_stonith, NULL);
305+ return TRUE;
306+}
307+
308+gboolean
309+do_stonith(GIOChannel *channel, GIOCondition condition, gpointer unused)
310+{
311+ vm_message msg;
312+ struct request_s *req;
313+ pid_t *pid;
314+ int ret;
315+
316+ crm_debug_2("called...");
317+
318+ ret = receive_msg(g_io_channel_unix_get_fd(channel), &msg);
319+ if (ret < 0) {
320+ crm_err("receive message failed");
321+ return TRUE;
322+ }
323+ else if (ret == 1) {
324+ crm_err("connection with vm-connectd was closed");
325+ g_hash_table_foreach(chldhash, kill_chld, NULL);
326+ g_timeout_add(1000, do_shutdown, NULL);
327+ exit_code = 1;
328+ return FALSE;
329+ }
330+ req = parse_request(&msg);
331+ crm_free(msg.data);
332+ msg.data = NULL;
333+ msg.info.datalen = 0;
334+
335+ if (req->op == OP_TIMEOUT) {
336+ g_hash_table_foreach(chldhash, kill_chld, req->id);
337+ free_chldhash(req);
338+ return TRUE;
339+ }
340+
341+ crm_malloc0(pid, sizeof(pid_t));
342+ *pid = fork();
343+ if (*pid < 0) {
344+ crm_err("fork(2) call failed, could not STONITH [op=%s, rsc=%s]",
345+ op2str(req->op), req->arg);
346+ free_chldhash(req);
347+ return TRUE;
348+ }
349+ else if (*pid > 0) {
350+ NewTrackedProc(*pid, 0, PT_LOGVERBOSE,
351+ g_strconcat(crm_system_name, "-", op2str(req->op), NULL), &ChldTrackOps);
352+ g_hash_table_insert(chldhash, pid, req);
353+ return TRUE;
354+ }
355+ crm_free(pid);
356+ setpgid(0, 0);
357+ g_main_loop_quit(mainloop);
358+ memset(&chldstat, 0, sizeof(struct chld_status));
359+ chldstat.req = req;
360+ CL_SIGNAL(SIGTERM, sighdr_term_chld);
361+ cl_signal_set_interrupt(SIGTERM, TRUE);
362+ ret = stonith_operate(&chldstat, &msg);
363+ disconnect_cib();
364+ exit(ret == TRUE ? 0 : 1);
365+}
366+
367+struct request_s *
368+parse_request(const vm_message *msg)
369+{
370+ struct request_s *req;
371+ char **args;
372+
373+ crm_debug_2("called..");
374+
375+ crm_malloc0(req, sizeof(struct request_s));
376+ req->op = OP_UNKNOWN;
377+
378+ args = g_strsplit(msg->data, " ", 0);
379+ if (g_strv_length(args) >= 1 && args[0] != NULL) {
380+ int i;
381+ for (i = 0; opmap[i].opstr; i++) {
382+ if (safe_str_eq(opmap[i].opstr, args[0])) {
383+ req->op = opmap[i].op;
384+ break;
385+ }
386+ }
387+ }
388+ if (g_strv_length(args) >= 2 && args[1] != NULL) {
389+ req->arg = decrypt_data(args[1]);
390+ }
391+ g_strfreev(args);
392+
393+ if (msg->info.id[0] != 0) {
394+ req->id = crm_strdup(msg->info.id);
395+ }
396+ crm_info("Request: op=%s, arg=%s", op2str(req->op), req->arg);
397+ crm_debug("Request: id=%s", req->id);
398+ return req;
399+}
400+
401+#define BOOL2RESULT(bool) bool == TRUE ? RESULT_OK : RESULT_NG
402+gboolean
403+stonith_operate(struct chld_status *stat, vm_message *msg)
404+{
405+ gboolean ret = FALSE;
406+ enum rsc_status rstat;
407+ int rc;
408+
409+ crm_debug_2("called..");
410+
411+ switch (stat->req->op) {
412+ case OP_POWERON:
413+ ret = start_resource(stat->req->arg, &stat->prev_role);
414+ break;
415+ case OP_POWEROFF:
416+ ret = stop_resource(stat->req->arg, &stat->prev_role,
417+ &stat->started_node, &stat->started_uuid);
418+ break;
419+ case OP_RESET:
420+ if (stop_resource(stat->req->arg, &stat->prev_role,
421+ &stat->started_node, &stat->started_uuid) == TRUE) {
422+ ret = start_resource(stat->req->arg, &stat->prev_role);
423+ }
424+ break;
425+ case OP_STATUS:
426+ rstat = get_rsc_status(stat->req->arg, NULL, NULL);
427+ if (rstat == RS_STARTED || rstat == RS_STOPPED) {
428+ ret = TRUE;
429+ }
430+ break;
431+ default:
432+ crm_warn("undefined request was received");
433+ break;
434+ }
435+ msg->data = crm_strdup(BOOL2RESULT(ret));
436+ msg->info.datalen = strlen(msg->data);
437+
438+ crm_info("Replying: %s", msg->data);
439+ rc = send_msg(sockfd, msg);
440+ if (rc < 0) {
441+ ret = FALSE;
442+ }
443+ crm_debug_2("end..");
444+ return ret;
445+}
446+
447+char *
448+decrypt_data(const char *encrypted_data)
449+{
450+ char *buf;
451+ const uint bufsize = 2048;
452+ int ret;
453+ static char *tmpfile = NULL;
454+
455+ crm_debug_2("called..");
456+
457+ if (tmpfile == NULL) {
458+ tmpfile = g_strdup_printf("/tmp/vmstonith.%d", getpid());
459+ }
460+
461+ crm_malloc0(buf, bufsize);
462+ if (cmd_read_stdin == TRUE) {
463+ sprintf(buf, "echo \"%s\"|%s>%s", encrypted_data, decrypt_cmd, tmpfile);
464+ }
465+ else {
466+ sprintf(buf, "%s \"%s\">%s", decrypt_cmd, encrypted_data, tmpfile);
467+ }
468+ crm_debug("decrypt command: %s", buf);
469+
470+ ret = system(buf);
471+ if (ret == -1 || !WIFEXITED(ret)) {
472+ cl_perror("system(3) call failed");
473+ goto fail;
474+ }
475+ ret = WEXITSTATUS(ret);
476+ crm_debug("command's exit code [%d]", ret);
477+ if (cmd_ok_exitcode != ret) {
478+ crm_err("command [%s] failed, exit code %d", buf, ret);
479+ goto fail;
480+ }
481+ crm_debug("decrypt command succeed");
482+
483+ crm_free(buf);
484+ buf = read_file(tmpfile, bufsize);
485+ if (buf == NULL) {
486+ goto fail;
487+ }
488+ if (buf[strlen(buf)-1] == '\n') {
489+ buf[strlen(buf)-1] = 0;
490+ }
491+ crm_debug("decrypted [%s]", buf);
492+ unlink(tmpfile);
493+ return buf;
494+fail:
495+ unlink(tmpfile);
496+ crm_free(buf);
497+ return NULL;
498+}
499+
500+char *
501+read_file(const char *path, int max_bufsize)
502+{
503+ int fd;
504+ char *buf, *p = NULL;
505+ ssize_t size;
506+
507+ crm_debug_2("called..");
508+
509+ fd = open(path, 'r');
510+ if (fd < 0) {
511+ cl_perror("open(2) call failed");
512+ return NULL;
513+ }
514+ crm_malloc0(buf, max_bufsize);
515+ memset(buf, 0, max_bufsize);
516+ size = read(fd, buf, max_bufsize-1);
517+ if (size < 0) {
518+ cl_perror("read(2) call failed");
519+ }
520+ close(fd);
521+ if (size > 0) {
522+ p = crm_strdup(buf);
523+ crm_debug_2("read success: %s", p);
524+ }
525+ crm_free(buf);
526+ return p;
527+}
528+
529+gboolean
530+connect_cib(void)
531+{
532+ enum cib_errors rc = cib_ok;
533+ int attempts;
534+
535+ crm_debug_2("called..");
536+
537+ if (cib_conn != NULL) {
538+ return TRUE;
539+ }
540+ memset(&pe_dataset, 0, sizeof(pe_working_set_t));
541+
542+ cib_conn = cib_new();
543+ if (cib_conn == NULL) {
544+ crm_err("cib connection initialization failed");
545+ return FALSE;
546+ }
547+ for (attempts = 0; attempts < 20; attempts++) {
548+ if (attempts) {
549+ sleep(1);
550+ }
551+ crm_debug("connect to cib attempt: %d", attempts+1);
552+ rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
553+ if (rc == cib_ok) {
554+ break;
555+ }
556+ }
557+ if (rc != cib_ok) {
558+ crm_err("failed to signon to cib: %s", cib_error2string(rc));
559+ return FALSE;
560+ }
561+ crm_debug("succeed at connect to cib");
562+ return TRUE;
563+}
564+
565+void
566+disconnect_cib(void)
567+{
568+ crm_debug_2("called..");
569+
570+ if (cib_conn != NULL) {
571+ cib_conn->cmds->signoff(cib_conn);
572+ cib_delete(cib_conn);
573+ cib_conn = NULL;
574+ }
575+ free_pe_dataset();
576+ return;
577+}
578+
579+gboolean
580+get_pe_dataset(void)
581+{
582+ crm_data_t *cib;
583+ unsigned int loglevel;
584+
585+ crm_debug_2("called..");
586+
587+ if (connect_cib() == FALSE) {
588+ return FALSE;
589+ }
590+ free_pe_dataset();
591+ cib = get_cib_copy(cib_conn);
592+ set_working_set_defaults(&pe_dataset);
593+ pe_dataset.input = cib;
594+ pe_dataset.now = new_ha_date(TRUE);
595+
596+ /* log output of the level below LOG_ERR is deterred */
597+ loglevel = get_crm_log_level();
598+ set_crm_log_level(LOG_ERR);
599+ cluster_status(&pe_dataset);
600+ set_crm_log_level(loglevel);
601+
602+ return TRUE;
603+}
604+
605+void
606+free_pe_dataset(void)
607+{
608+ crm_debug_2("called..");
609+
610+ if (pe_dataset.input == NULL) {
611+ return;
612+ }
613+ free_xml(pe_dataset.input);
614+ pe_dataset.input = NULL;
615+ cleanup_calculations(&pe_dataset);
616+ memset(&pe_dataset, 0, sizeof(pe_working_set_t));
617+ return;
618+}
619+
620+enum rsc_status
621+get_rsc_status(const char *rscid, char **started_node, char **started_uuid)
622+{
623+ resource_t *rsc;
624+
625+ crm_debug_2("called..");
626+
627+ if (rscid == NULL) {
628+ return FALSE;
629+ }
630+ if (get_pe_dataset() == FALSE) {
631+ return RS_GETERROR;
632+ }
633+
634+ /* find out from RUNNING resources */
635+ slist_iter(node, node_t, pe_dataset.nodes, lpc,
636+ slist_iter(rsc, resource_t, node->details->running_rsc, lpc,
637+ crm_debug("started rscid [%s]", rsc->id);
638+ if (safe_str_eq(rscid, rsc->id)) {
639+ if (check_rsc_meta(rsc->meta) == FALSE) {
640+ return RS_UNDEFINED;
641+ }
642+ if (started_node != NULL && *started_node == NULL) {
643+ *started_node = crm_strdup(node->details->uname);
644+ *started_uuid = crm_strdup(node->details->id);
645+ crm_debug("get started_node: %s (%s)",
646+ *started_node, *started_uuid);
647+ }
648+ return RS_STARTED;
649+ }
650+ );
651+ );
652+
653+ /* find out from ALL resources */
654+ rsc = pe_find_resource(pe_dataset.resources, rscid);
655+ if (rsc != NULL) {
656+ crm_debug("stopped rscid [%s]", rsc->id);
657+ if (check_rsc_meta(rsc->meta) == TRUE) {
658+ return RS_STOPPED;
659+ }
660+ }
661+ return RS_UNDEFINED;
662+}
663+
664+gboolean
665+check_rsc_meta(GHashTable *rsc_meta)
666+{
667+ const char *value;
668+
669+ crm_debug_2("called..");
670+
671+ value = g_hash_table_lookup(rsc_meta, XML_AGENT_ATTR_CLASS);
672+ crm_debug("%s=%s", XML_AGENT_ATTR_CLASS, value);
673+ if (value == NULL || safe_str_neq(value, "ocf")) {
674+ return FALSE;
675+ }
676+
677+ value = g_hash_table_lookup(rsc_meta, XML_AGENT_ATTR_PROVIDER);
678+ crm_debug("%s=%s", XML_AGENT_ATTR_PROVIDER, value);
679+ if (value == NULL || safe_str_neq(value, "extra")) {
680+ return FALSE;
681+ }
682+
683+ value = g_hash_table_lookup(rsc_meta, XML_ATTR_TYPE);
684+ crm_debug("%s=%s", XML_ATTR_TYPE, value);
685+ if (value == NULL || safe_str_neq(value, "VirtualDomain")) {
686+ return FALSE;
687+ }
688+ return TRUE;
689+}
690+
691+gboolean
692+start_resource(const char *rscid, char **prev_role)
693+{
694+ gboolean updated_cib = FALSE;
695+
696+ crm_debug_2("called..");
697+
698+ if (rscid == NULL) {
699+ return FALSE;
700+ }
701+
702+check:
703+ switch (get_rsc_status(rscid, NULL, NULL)) {
704+ case RS_STARTED:
705+ crm_info("resource %s started", rscid);
706+ return TRUE;
707+ case RS_STOPPED:
708+ if (updated_cib == FALSE) {
709+ if (set_rsc_role(rscid, RSC_ROLE_STARTED_S, prev_role) == FALSE) {
710+ return FALSE;
711+ }
712+ updated_cib = TRUE;
713+ }
714+ crm_debug_2("waiting..");
715+ sleep(1);
716+ goto check;
717+ default:
718+ return FALSE;
719+ }
720+}
721+
722+gboolean
723+stop_resource(const char *rscid, char **prev_role, char **started_node, char **started_uuid)
724+{
725+ gboolean updated_cib = FALSE;
726+
727+ crm_debug_2("called..");
728+
729+ if (rscid == NULL) {
730+ return FALSE;
731+ }
732+
733+check:
734+ switch (get_rsc_status(rscid, started_node, started_uuid)) {
735+ case RS_STARTED:
736+ if (updated_cib == FALSE) {
737+ if (update_status_attr('U', rscid, *started_node, *started_uuid) == FALSE) {
738+ return FALSE;
739+ }
740+ if (set_rsc_role(rscid, RSC_ROLE_STOPPED_S, prev_role) == FALSE) {
741+ update_status_attr('D', rscid, *started_node, *started_uuid);
742+ return FALSE;
743+ }
744+ updated_cib = TRUE;
745+ }
746+ crm_debug_2("waiting..");
747+ sleep(1);
748+ goto check;
749+ case RS_STOPPED:
750+ crm_info("resource %s stopped", rscid);
751+ if (updated_cib == FALSE) {
752+ return TRUE;
753+ }
754+ return update_status_attr('D', rscid, *started_node, *started_uuid);
755+ default:
756+ return FALSE;
757+ }
758+}
759+
760+/*
761+ * The cluster node attribute is updated for RA which controls a virtual machine.
762+ */
763+gboolean
764+update_status_attr(char command, const char *rscid, const char *node, const char *uuid)
765+{
766+ char *name = g_strdup_printf("%s%s", ATTR_NAME_PREFIX, rscid);
767+ char *value;
768+ gboolean ret;
769+
770+ crm_debug_2("called..");
771+
772+ switch (command) {
773+ case 'U':
774+ value = ATTR_VALUE;
775+ break;
776+ case 'D':
777+ value = NULL;
778+ break;
779+ default:
780+ return FALSE;
781+ }
782+ crm_info("Update attribute: %s=%s for %s", name, value, node);
783+
784+ ret = attrd_lazy_update(command, node, name, value, XML_CIB_TAG_STATUS, NULL, NULL);
785+ if (ret == TRUE) {
786+ enum cib_errors rc;
787+ value = NULL;
788+ while (1) {
789+ crm_debug_2("waiting..");
790+ sleep(1);
791+ rc = read_attr(cib_conn, XML_CIB_TAG_STATUS,
792+ uuid, NULL, NULL, name, &value, FALSE);
793+ crm_debug("command [%c], rc [%d], value [%s]", command, rc, value);
794+ if (rc == cib_ok) {
795+ if (command == 'U' && !g_strcmp0(value, ATTR_VALUE)) {
796+ break;
797+ }
798+ }
799+ else if (rc == cib_NOTEXISTS) {
800+ if (command == 'D') {
801+ break;
802+ }
803+ }
804+ else {
805+ ret = FALSE;
806+ break;
807+ }
808+ crm_free(value);
809+ }
810+ crm_free(value);
811+ }
812+ crm_free(name);
813+ return ret;
814+}
815+
816+/*
817+ * ref. pacemaker/tools/crm_resource.c
818+ */
819+gboolean
820+set_rsc_role(const char *rscid, const char *value, char **prev_role)
821+{
822+ resource_t *rsc;
823+ char *id = NULL;
824+ xmlNode *xml_top = NULL, *xml_obj = NULL;
825+ enum cib_errors rc;
826+ const char *name = XML_RSC_ATTR_TARGET_ROLE;
827+
828+ crm_debug_2("called..");
829+
830+ rsc = pe_find_resource(pe_dataset.resources, rscid);
831+ if (rsc == NULL) {
832+ return FALSE;
833+ }
834+
835+ rc = find_meta_attr(rscid, name, &id, prev_role);
836+ if (rc == cib_ok) {
837+ crm_debug("Found a match for name=%s: id=%s", name, id);
838+ }
839+ else if (rc == cib_NOTEXISTS) {
840+ char *set;
841+ set = crm_concat(rscid, XML_TAG_META_SETS, '-');
842+ id = crm_concat(set, name, '-');
843+ xml_top = create_xml_node(NULL, crm_element_name(rsc->xml));
844+ crm_xml_add(xml_top, XML_ATTR_ID, rscid);
845+ xml_obj = create_xml_node(xml_top, XML_TAG_META_SETS);
846+ crm_xml_add(xml_obj, XML_ATTR_ID, set);
847+ crm_free(set);
848+
849+ if (prev_role != NULL && *prev_role == NULL) {
850+ *prev_role = crm_strdup(RSC_ROLE_STARTED_S);
851+ crm_debug("get prev_role: %s", *prev_role);
852+ }
853+ }
854+ else {
855+ return FALSE;
856+ }
857+
858+ xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR);
859+ if (xml_top == NULL) {
860+ xml_top = xml_obj;
861+ }
862+ crm_xml_add(xml_obj, XML_ATTR_ID, id);
863+ crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, name);
864+ crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, value);
865+ crm_log_xml(LOG_INFO, "Update", xml_top);
866+
867+ rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_RESOURCES, xml_top, cib_sync_call);
868+ if (rc != cib_ok) {
869+ crm_err("failed to modify to cib: %s", cib_error2string(rc));
870+ }
871+ free_xml(xml_top);
872+ crm_free(id);
873+ return rc == cib_ok ? TRUE : FALSE;
874+}
875+
876+/*
877+ * ref. pacemaker/tools/crm_resource.c
878+ */
879+enum cib_errors
880+find_meta_attr(const char *rscid, const char *name, char **id, char **value)
881+{
882+ char *xpath;
883+ xmlNode *xml_search = NULL;
884+ const char *p;
885+ enum cib_errors rc;
886+
887+ crm_debug_2("called..");
888+
889+ xpath = g_strdup_printf("%s/*[@id=\"%s\"]/%s/nvpair[@name=\"%s\"]",
890+ get_object_path("resources"), rscid, XML_TAG_META_SETS, name);
891+ crm_debug("query=%s", xpath);
892+
893+ rc = cib_conn->cmds->query(cib_conn, xpath, &xml_search,
894+ cib_sync_call | cib_scope_local | cib_xpath);
895+ if (rc != cib_ok) {
896+ if (rc != cib_NOTEXISTS) {
897+ crm_err("failed to query to cib: %s", cib_error2string(rc));
898+ }
899+ crm_free(xpath);
900+ return rc;
901+ }
902+ crm_log_xml_debug(xml_search, "Match");
903+
904+ p = crm_element_value(xml_search, XML_ATTR_ID);
905+ if (p != NULL) {
906+ *id = crm_strdup(p);
907+ }
908+ if (value != NULL && *value == NULL) {
909+ p = crm_element_value(xml_search, XML_NVPAIR_ATTR_VALUE);
910+ if (p != NULL) {
911+ *value = crm_strdup(p);
912+ crm_debug("get prev_value: %s", *value);
913+ }
914+ }
915+ crm_free(xpath);
916+ free_xml(xml_search);
917+ return rc;
918+}
919+
920+int
921+main(int argc, char **argv)
922+{
923+ int argerr = 0, flag;
924+#ifdef HAVE_GETOPT_H
925+ int opt_idx = 0;
926+ static struct option long_opts[] = {
927+ {"verbose", 0, 0, 'V'},
928+ {"decrypt-cmd", 1, 0, 'c'},
929+ {"cmd-read-stdin", 0, 0, 'i'},
930+ {"cmd-ok-exitcode", 1, 0, 'e'},
931+ {"help", 0, 0, '?'},
932+ {0, 0, 0, 0}
933+ };
934+#endif
935+ crm_log_init(basename(argv[0]), LOG_INFO, TRUE, FALSE, argc, argv);
936+ mainloop_add_signal(SIGTERM, sighdr_term);
937+ mainloop_add_signal(SIGINT, sighdr_term);
938+ set_sigchld_proctrack(G_PRIORITY_HIGH, 10 * DEFAULT_MAXDISPATCHTIME);
939+
940+ while (1) {
941+#ifdef HAVE_GETOPT_H
942+ flag = getopt_long(argc, argv, OPTARGS, long_opts, &opt_idx);
943+#else
944+ flag = getopt(argc, argv, OPTARGS);
945+#endif
946+ if (flag == -1) {
947+ break;
948+ }
949+ switch (flag) {
950+ case 'V':
951+ cl_log_enable_stderr(TRUE);
952+ alter_debug(DEBUG_INC);
953+ break;
954+ case 'c':
955+ crm_debug("decrypt-cmd: [%s]", optarg);
956+ decrypt_cmd = crm_strdup(optarg);
957+ break;
958+ case 'i':
959+ cmd_read_stdin = TRUE;
960+ break;
961+ case 'e':
962+ crm_debug("cmd-ok-exitcode: [%s]", optarg);
963+ cmd_ok_exitcode = crm_parse_int(optarg, "-1");
964+ if (cmd_ok_exitcode < 0) {
965+ fprintf(stderr,
966+ "Invalid exit code is specified. [%s]\n", optarg);
967+ argerr++;
968+ }
969+ break;
970+ case '?':
971+ usage(crm_system_name, 1);
972+ break;
973+ default:
974+ fprintf(stderr, "Argument code 0%o (%c) is not (?yet?) supported",
975+ flag, flag);
976+ argerr++;
977+ break;
978+ }
979+ }
980+
981+ if (optind < argc) {
982+ fprintf(stderr, "non-option ARGV-elements: ");
983+ while (optind < argc) {
984+ fprintf(stderr, "%s", argv[optind++]);
985+ }
986+ fprintf(stderr, "\n");
987+ argerr++;
988+ }
989+ if (argerr || decrypt_cmd == NULL || decrypt_cmd[0] == 0) {
990+ usage(crm_system_name, 1);
991+ }
992+ chldhash = g_hash_table_new_full(g_str_hash, g_int_equal, g_free, free_chldhash);
993+
994+ if (connect_vmconnectd() == FALSE) {
995+ crm_info("Exiting %s", crm_system_name);
996+ return 1;
997+ }
998+ mainloop = g_main_loop_new(NULL, FALSE);
999+ crm_info("Starting %s", crm_system_name);
1000+ g_main_loop_run(mainloop);
1001+ crm_info("Exiting %s", crm_system_name);
1002+ return exit_code;
1003+}
Show on old repository browser