1 /*
2  * Copyright (c) 2015-2017 Red Hat, Inc.
3  *
4  * All rights reserved.
5  *
6  * Author: Jan Friesse (jfriesse@redhat.com)
7  *
8  * This software licensed under BSD license, the text of which follows:
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions are met:
12  *
13  * - Redistributions of source code must retain the above copyright notice,
14  *   this list of conditions and the following disclaimer.
15  * - Redistributions in binary form must reproduce the above copyright notice,
16  *   this list of conditions and the following disclaimer in the documentation
17  *   and/or other materials provided with the distribution.
18  * - Neither the name of the Red Hat, Inc. nor the names of its
19  *   contributors may be used to endorse or promote products derived from this
20  *   software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
32  * THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "qdevice-config.h"
36 #include "qdevice-instance.h"
37 #include "qdevice-heuristics-exec-list.h"
38 #include "qdevice-log.h"
39 #include "qdevice-model.h"
40 #include "utils.h"
41 
42 int
qdevice_instance_init(struct qdevice_instance * instance,const struct qdevice_advanced_settings * advanced_settings)43 qdevice_instance_init(struct qdevice_instance *instance,
44     const struct qdevice_advanced_settings *advanced_settings)
45 {
46 
47 	memset(instance, 0, sizeof(*instance));
48 
49 	node_list_init(&instance->config_node_list);
50 
51 	instance->vq_last_poll = ((time_t) -1);
52 	instance->advanced_settings = advanced_settings;
53 
54 	return (0);
55 }
56 
57 int
qdevice_instance_destroy(struct qdevice_instance * instance)58 qdevice_instance_destroy(struct qdevice_instance *instance)
59 {
60 
61 	node_list_free(&instance->config_node_list);
62 
63 	return (0);
64 }
65 
66 int
qdevice_instance_configure_from_cmap_heuristics(struct qdevice_instance * instance)67 qdevice_instance_configure_from_cmap_heuristics(struct qdevice_instance *instance)
68 {
69 	char *str;
70 	long long int lli;
71 	int i;
72 	int res;
73 	cs_error_t cs_err;
74 	cmap_iter_handle_t iter_handle;
75 	char key_name[CMAP_KEYNAME_MAXLEN + 1];
76 	size_t value_len;
77 	cmap_value_types_t type;
78 	struct qdevice_heuristics_exec_list tmp_exec_list;
79 	struct qdevice_heuristics_exec_list *exec_list;
80 	char *command;
81 	char exec_name[CMAP_KEYNAME_MAXLEN + 1];
82 	char tmp_key[CMAP_KEYNAME_MAXLEN + 1];
83 	size_t no_execs;
84 	int send_exec_list;
85 
86 	instance->heuristics_instance.timeout = instance->heartbeat_interval / 2;
87 	if (cmap_get_string(instance->cmap_handle,
88 	    "quorum.device.heuristics.timeout", &str) == CS_OK) {
89 		if (utils_strtonum(str, instance->advanced_settings->heuristics_min_timeout,
90 		    instance->advanced_settings->heuristics_max_timeout, &lli) == -1) {
91 			qdevice_log(LOG_ERR, "heuristics.timeout must be valid number in "
92 			    "range <%"PRIu32",%"PRIu32">",
93 			    instance->advanced_settings->heuristics_min_timeout,
94 			    instance->advanced_settings->heuristics_max_timeout);
95 
96 			free(str);
97 			return (-1);
98 		} else {
99 			instance->heuristics_instance.timeout = lli;
100 		}
101 
102 		free(str);
103 	}
104 
105 	instance->heuristics_instance.sync_timeout = instance->sync_heartbeat_interval / 2;
106 	if (cmap_get_string(instance->cmap_handle,
107 	    "quorum.device.heuristics.sync_timeout", &str) == CS_OK) {
108 		if (utils_strtonum(str, instance->advanced_settings->heuristics_min_timeout,
109 		    instance->advanced_settings->heuristics_max_timeout, &lli) == -1) {
110 			qdevice_log(LOG_ERR, "heuristics.sync_timeout must be valid number in "
111 			    "range <%"PRIu32",%"PRIu32">",
112 			    instance->advanced_settings->heuristics_min_timeout,
113 			    instance->advanced_settings->heuristics_max_timeout);
114 
115 			free(str);
116 			return (-1);
117 		} else {
118 			instance->heuristics_instance.sync_timeout = lli;
119 		}
120 
121 		free(str);
122 	}
123 
124 	instance->heuristics_instance.interval = instance->heartbeat_interval * 3;
125 	if (cmap_get_string(instance->cmap_handle,
126 	    "quorum.device.heuristics.interval", &str) == CS_OK) {
127 		if (utils_strtonum(str, instance->advanced_settings->heuristics_min_interval,
128 		    instance->advanced_settings->heuristics_max_interval, &lli) == -1) {
129 			qdevice_log(LOG_ERR, "heuristics.interval must be valid number in "
130 			    "range <%"PRIu32",%"PRIu32">",
131 			    instance->advanced_settings->heuristics_min_interval,
132 			    instance->advanced_settings->heuristics_max_interval);
133 
134 			free(str);
135 			return (-1);
136 		} else {
137 			instance->heuristics_instance.interval = lli;
138 		}
139 
140 		free(str);
141 	}
142 
143 	instance->heuristics_instance.mode = QDEVICE_DEFAULT_HEURISTICS_MODE;
144 
145 	if (cmap_get_string(instance->cmap_handle, "quorum.device.heuristics.mode", &str) == CS_OK) {
146 		if ((i = utils_parse_bool_str(str)) == -1) {
147 			if (strcasecmp(str, "sync") != 0) {
148 				qdevice_log(LOG_ERR, "quorum.device.heuristics.mode value is not valid.");
149 
150 				free(str);
151 				return (-1);
152 			} else {
153 				instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_SYNC;
154 			}
155 		} else {
156 			if (i == 1) {
157 				instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_ENABLED;
158 			} else {
159 				instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_DISABLED;
160 			}
161 		}
162 
163 		free(str);
164 	}
165 
166 	send_exec_list = 0;
167 	exec_list = NULL;
168 	qdevice_heuristics_exec_list_init(&tmp_exec_list);
169 
170 	if (instance->heuristics_instance.mode == QDEVICE_HEURISTICS_MODE_DISABLED) {
171 		exec_list = NULL;
172 		send_exec_list = 1;
173 	} else if (instance->heuristics_instance.mode == QDEVICE_HEURISTICS_MODE_ENABLED ||
174 	    instance->heuristics_instance.mode == QDEVICE_HEURISTICS_MODE_SYNC) {
175 		/*
176 		 * Walk thru list of commands to exec
177 		 */
178 		cs_err = cmap_iter_init(instance->cmap_handle, "quorum.device.heuristics.exec_", &iter_handle);
179 		if (cs_err != CS_OK) {
180 			qdevice_log(LOG_ERR, "Can't iterate quorum.device.heuristics.exec_ keys. "
181 			    "Error %s", cs_strerror(cs_err));
182 
183 			return (-1);
184 		}
185 
186 		while ((cs_err = cmap_iter_next(instance->cmap_handle, iter_handle, key_name,
187 		    &value_len, &type)) == CS_OK) {
188 			if (type != CMAP_VALUETYPE_STRING) {
189 				qdevice_log(LOG_WARNING, "%s key is not of string type. Ignoring");
190 				continue ;
191 			}
192 
193 			res = sscanf(key_name, "quorum.device.heuristics.exec_%[^.]%s", exec_name, tmp_key);
194 			if (res != 1) {
195 				qdevice_log(LOG_WARNING, "%s key is not correct heuristics exec name. Ignoring");
196 				continue ;
197 			}
198 
199 			cs_err = cmap_get_string(instance->cmap_handle, key_name, &command);
200 			if (cs_err != CS_OK) {
201 				qdevice_log(LOG_WARNING, "Can't get value of %s key. Ignoring");
202 				continue ;
203 			}
204 
205 			if (qdevice_heuristics_exec_list_add(&tmp_exec_list, exec_name, command) == NULL) {
206 				qdevice_log(LOG_WARNING, "Can't store value of %s key into list. Ignoring");
207 			}
208 
209 			free(command);
210 		}
211 
212 		no_execs = qdevice_heuristics_exec_list_size(&tmp_exec_list);
213 
214 		if (no_execs == 0) {
215 			qdevice_log(LOG_INFO, "No valid heuristics execs defined. Disabling heuristics.");
216 			instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_DISABLED;
217 			exec_list = NULL;
218 			send_exec_list = 1;
219 		} else if (no_execs > instance->advanced_settings->heuristics_max_execs) {
220 			qdevice_log(LOG_ERR, "Too much (%zu) heuristics execs defined (max is %zu)."
221 			    " Disabling heuristics.", no_execs,
222 			    instance->advanced_settings->heuristics_max_execs);
223 			instance->heuristics_instance.mode = QDEVICE_HEURISTICS_MODE_DISABLED;
224 			exec_list = NULL;
225 			send_exec_list = 1;
226 		} else if (qdevice_heuristics_exec_list_eq(&tmp_exec_list,
227 		    &instance->heuristics_instance.exec_list) == 1) {
228 			qdevice_log(LOG_DEBUG, "Heuristics list is unchanged");
229 			send_exec_list = 0;
230 		} else {
231 			qdevice_log(LOG_DEBUG, "Heuristics list changed");
232 			exec_list = &tmp_exec_list;
233 			send_exec_list = 1;
234 		}
235 
236 	} else {
237 		qdevice_log(LOG_CRIT, "Undefined heuristics mode");
238 		exit(1);
239 	}
240 
241 	if (send_exec_list) {
242 		if (qdevice_heuristics_change_exec_list(&instance->heuristics_instance,
243 		    exec_list, instance->sync_in_progress) != 0) {
244 			return (-1);
245 		}
246 	}
247 
248 	qdevice_heuristics_exec_list_free(&tmp_exec_list);
249 
250 	return (0);
251 }
252 
253 int
qdevice_instance_configure_from_cmap(struct qdevice_instance * instance)254 qdevice_instance_configure_from_cmap(struct qdevice_instance *instance)
255 {
256 	char *str;
257 
258 	if (cmap_get_string(instance->cmap_handle, "quorum.device.model", &str) != CS_OK) {
259 		qdevice_log(LOG_ERR, "Can't read quorum.device.model cmap key.");
260 
261 		return (-1);
262 	}
263 
264 	if (qdevice_model_str_to_type(str, &instance->model_type) != 0) {
265 		qdevice_log(LOG_ERR, "Configured device model %s is not supported.", str);
266 		free(str);
267 
268 		return (-1);
269 	}
270 	free(str);
271 
272 	if (cmap_get_uint32(instance->cmap_handle, "runtime.votequorum.this_node_id",
273 	    &instance->node_id) != CS_OK) {
274 		qdevice_log(LOG_ERR, "Unable to retrieve this node nodeid.");
275 
276 		return (-1);
277 	}
278 
279 	if (cmap_get_uint32(instance->cmap_handle, "quorum.device.timeout", &instance->heartbeat_interval) != CS_OK) {
280 		instance->heartbeat_interval = VOTEQUORUM_QDEVICE_DEFAULT_TIMEOUT;
281 	}
282 
283 	if (cmap_get_uint32(instance->cmap_handle, "quorum.device.sync_timeout",
284 	    &instance->sync_heartbeat_interval) != CS_OK) {
285 		instance->sync_heartbeat_interval = VOTEQUORUM_QDEVICE_DEFAULT_SYNC_TIMEOUT;
286 	}
287 
288 	if (qdevice_instance_configure_from_cmap_heuristics(instance) != 0) {
289 		return (-1);
290 	}
291 
292 	return (0);
293 }
294