※ リポジトリは、https://github.com/linux-ha-japan/pm_kvm_tools-1.0 へ移行しました。
仮想環境監視連携機能とSTONITH機能。KVM版。
Revision | 441a96ed873fa1f33e8bae467a4fcbc81d50fbc5 (tree) |
---|---|
Zeit | 2011-09-15 15:27:04 |
Autor | Kazunori INOUE <inouekazu@inte...> |
Commiter | Kazunori INOUE |
Initial commit for pm_kvm_tools - tools of pacemaker for the KVM virtual environment
@@ -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$ |
@@ -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) |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -0,0 +1,1 @@ | ||
1 | +MAINTAINERCLEANFILES = Makefile.in config.h.in |
@@ -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 */ |
@@ -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 |
@@ -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 | + |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 $? |
@@ -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 |
@@ -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 | + |
@@ -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 |
@@ -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 | +} |
@@ -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 | + |
@@ -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 | + |
@@ -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 | +} |