2
% Copyright (C) 1997-2003 Sistina Software, Inc. All rights reserved.
3
% Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
5
% This program is free software; you can redistribute it and/or
6
% modify it under the terms of the GNU General Public License
7
% as published by the Free Software Foundation; either version 2
8
% of the License, or (at your option) any later version.
10
% This program 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
13
% GNU General Public License for more details.
15
% You should have received a copy of the GNU General Public License
16
% along with this program; if not, write to the Free Software
17
% Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
define node_in_set(node_list, node)
25
len = length(node_list);
26
for (x = 0; x < len; x++) {
27
if (node_list[x] == node)
36
% Returns 3 node lists:
37
% (1) Nodes with no services
38
% (2) Nodes with non-exclusive services
39
% (3) Nodes with exclusive services
41
% NOTE: This function currently defenstrates failover domain rules
43
define separate_nodes(node_list)
45
variable services = service_list();
46
variable nodes_empty, nodes_services, nodes_excl;
48
variable owner, state, excl, ns = 0, nx = 0;
50
nodes_empty = node_list;
52
% Most Awesome Initializer EVER!!!
53
nodes_services = subtract([0], 0);
54
nodes_excl = subtract([0], 0);
56
len = length(services);
57
for (x = 0; x < len; x++) {
59
(,,, owner, state) = service_status(services[x]);
64
excl = atoi(service_property(services[x], "exclusive"));
65
nodes_empty = subtract(nodes_empty, owner);
67
nodes_excl = union(nodes_excl, owner);
69
nodes_services = union(nodes_services, owner);
73
return (nodes_empty, nodes_services, nodes_excl);
77
define exclusive_prioritize(svc, node_list)
79
variable services = service_list();
80
variable len, x, y, owner, state, preferred_owner;
81
variable svc_excl, other_excl;
82
variable nodes_x, nodes_s, nodes_e;
85
% Not exclusive? Don't care!
87
svc_excl = atoi(service_property(svc, "exclusive"));
92
(nodes_e, nodes_s, nodes_x) = separate_nodes(node_list);
93
debug("Nodes - Empty: ", nodes_e, " w/Services: ", nodes_s, " w/Excl: ", nodes_x);
94
if (length(nodes_e) > 0) {
96
% If we've got an exclusive service, only allow it to start on
102
if (length(nodes_x) == 0) {
104
% If we've got NO nodes with other exclusive services
105
% and no empty nodes, the service can not be started
107
notice("No empty / exclusive nodes available; cannot restart ", svc);
112
% Prioritization of exclusive services: pancake a service and replace it
113
% with this service if this services is a higher priority.
115
len = length(services);
116
for (x = 0; x < len; x++) {
117
if (svc == services[x]) {
118
% don't do anything to ourself!
122
(,,, owner, state) = service_status(services[x]);
127
if (node_in_set(node_list, owner) == 0) {
131
other_excl = atoi(service_property(services[x], "exclusive"));
132
if (other_excl == 0) {
137
% If we're a higher priority (lower #) exclusive
138
% Stop the exclusive service that node and move that
141
if (svc_excl >= other_excl) {
148
warning("STOPPING service ", services[x], " because ", svc, " is a higher priority.");
149
() = service_stop(services[x]);
152
% Return just the one node.
154
node_list = subtract([0], 0);
155
node_list = union(node_list, owner);
163
define move_or_start(service, node_list)
166
variable state, owner;
169
depends = service_property(service, "depend");
171
(,,, owner, state) = service_status(depends);
173
debug(service, " is not runnable; dependency not met");
178
(,,, owner, state) = service_status(service);
179
debug("Evaluating ", service, " state=", state, " owner=", owner);
180
if ((event_type == EVENT_NODE) and (node_id == owner) and
181
(node_state == NODE_OFFLINE)) {
182
info("Marking service ", service, " on down member ",
183
owner, " as stopped");
184
if (service_stop(service) < 0) {
189
len = length(node_list);
191
notice(service, " is not runnable - restricted domain offline");
192
()=service_stop(service);
196
if (((event_type != EVENT_USER) and (state == "disabled")) or
197
((state == "failed") or (state == "frozen"))) {
199
% Commenting out this block will -not- allow you to
200
% recover failed services from event scripts. Sorry.
201
% All it will get you is a false log message about
202
% starting this service.
204
% You may enable disabled services, but I recommend
207
debug(service, " is not runnable");
211
if (node_list[0] == owner) {
212
debug(service, " is already running on best node");
216
if ((owner >= 0) and (node_in_set(node_list, owner) == 1)) {
217
notice("Moving ", service, " from ", owner,
219
if (service_stop(service) < 0) {
223
node_list = exclusive_prioritize(service, node_list);
224
notice("Starting ", service, " on ", node_list);
227
if (length(node_list) == 0) {
230
return service_start(service, node_list);
235
% Returns the set of online nodes in preferred/shuffled order which
236
% are allowed to run this service. Gives highest preference to current
237
% owner if nofailback is specified.
239
define allowed_nodes(service)
243
variable nodes_domain;
244
variable ordered, restricted, nofailback;
245
variable state, owner;
248
(nofailback, restricted, ordered, nodes_domain) =
249
service_domain_info(service);
251
(,,, owner, state) = service_status(service);
253
anodes = nodes_online();
255
% Shuffle the array so we don't start all services on the same
256
% node. TODO - add RR, Least-services, placement policies...
257
online = shuffle(anodes);
259
if (restricted == 1) {
260
anodes = intersection(nodes_domain, online);
262
% Ordered failover domains (nodes_domain) unioned with the
263
% online nodes basically just reorders the online node list
264
% according to failover domain priority rules.
265
anodes = union(intersection(nodes_domain, online),
269
if ((nofailback == 1) or (ordered == 0)) {
271
if ((owner < 0) or (node_in_set(anodes, owner) == 0)) {
275
% Because union takes left as priority, we can
276
% return the union of the current owner with the
277
% allowed node list. This means the service will
278
% remain on the same node it's currently on.
279
return union(owner, anodes);
286
% Returns the set of online nodes in preferred/shuffled order which
287
% are allowed to run this service. Gives highest preference to current
288
% owner if nofailback is specified.
290
define allowed_nodes(service)
294
variable nodes_domain;
295
variable ordered, restricted, nofailback;
296
variable state, owner;
299
(nofailback, restricted, ordered, nodes_domain) =
300
service_domain_info(service);
301
(,,, owner, state) = service_status(service);
303
anodes = nodes_online();
305
% Shuffle the array so we don't start all services on the same
306
% node. TODO - add RR, Least-services, placement policies...
307
online = shuffle(anodes);
309
if (restricted == 1) {
310
anodes = intersection(nodes_domain, online);
312
% Ordered failover domains (nodes_domain) unioned with the
313
% online nodes basically just reorders the online node list
314
% according to failover domain priority rules.
315
anodes = union(intersection(nodes_domain, online),
319
if ((nofailback == 1) or (ordered == 0)) {
321
if ((owner < 0) or (node_in_set(anodes, owner) == 0)) {
325
% Because union takes left as priority, we can
326
% return the union of the current owner with the
327
% allowed node list. This means the service will
328
% remain on the same node it's currently on.
329
return union(owner, anodes);
335
define string_list(thelist, delimiter)
340
if (length(thelist) == 0) {
344
for (index=0; index < length(thelist)-1; index++) {
345
output=output+string(thelist[index])+delimiter;
347
return output+thelist[index];
350
% this function gets the smallest property from a given list of services
351
% if the list only exists of one element the property itself is returned
352
% if the given property is not found 0 is returned
353
define services_min_attribute(services, property)
356
variable min_property=-1;
357
variable tmp_property;
359
for (x = 0; x < length(services); x++) {
360
tmp_property=service_property(services[x], property);
361
if (tmp_property == NULL) {
364
tmp_property=atoi(tmp_property);
366
if ((min_property < 0) or (tmp_property < min_property)) {
367
min_property=tmp_property;
369
%debug("services_min_attribute: ",services[x]," attribute: ",min_property, "tmp: ", tmp_property, " min: ", min_property);
372
%debug("services_min_attribute: (", string_list(services, ", "),")[",property,"]: ",min_property);
377
% This function will sort a given service_list by the given attribute name and
379
define sorted_service_list(services, attribute)
381
variable work_queue={};
382
variable sorted_list={}, tmp, tmp2;
384
variable cur_min_prop=0;
385
variable service_prop=0;
388
%debug("sorted_service_list: ", strjoin(services, ", "));
389
for (x=0; x<length(services); x++) {
390
list_append(work_queue, string(services[x]));
393
%debug("sorted_service_list: work_queue ", string_list(work_queue, ", "));
394
while (length(work_queue) > 0) {
395
cur_min_prop=services_min_attribute(work_queue, attribute);
396
%debug("sorted_service_list sorting services list for attribute ", attribute, " cur_min: ",cur_min_prop);
397
for (x = 0; x < length(work_queue); x++) {
398
service_prop=service_property(work_queue[x], "priority");
399
if (service_prop == NULL) {
402
service_prop=atoi(service_prop);
404
%debug("sorted_service_list: ",work_queue[x], " property[", attribute,"]: ",service_prop);
405
if (cur_min_prop==service_prop) {
406
%debug("sorted_service_list: adding service ",work_queue[x]," to sorted. work_queue: ", string_list(work_queue, ", "));
407
list_append(sorted_list, work_queue[x]);
408
%debug("sorted_service_list: sorted_list: ", string_list(sorted_list, ", "));
409
%debug("sorted_service_list: removing service ",work_queue[x], " from work_queue ", string_list(work_queue, ", "));
410
list_delete(work_queue, x);
412
%debug("sorted_service_list: work_queue: ",string_list(work_queue, ", "));
418
debug("sorted_service_list ", string_list(sorted_list, ", "));
422
define sortedservices_node_event_handler(services, attribute) {
426
services=sorted_service_list(services, attribute);
427
for (x = 0; x < length(services); x++) {
428
debug("Executing sortedservices node event handler for service: ", services[x]);
429
nodes = allowed_nodes(services[x]);
430
()=move_or_start(services[x], nodes);
434
define default_node_event_handler()
436
variable services = service_list();
440
debug("Executing default node event handler");
441
for (x = 0; x < length(services); x++) {
442
nodes = allowed_nodes(services[x]);
443
()=move_or_start(services[x], nodes);
448
define default_service_event_handler()
450
variable services = service_list();
453
variable depend_mode;
460
debug("Executing default service event handler");
462
if (service_state == "recovering") {
464
policy = service_property(service_name, "recovery");
466
" Service: ", service_name,
467
" Last owner: ", service_last_owner,
469
" RTE: ", service_restarts_exceeded);
471
if (policy == "disable") {
472
() = service_stop(service_name, 1);
476
nodes = allowed_nodes(service_name);
477
if (policy == "restart" and service_restarts_exceeded == 0) {
478
nodes = union(service_last_owner, nodes);
481
tmp = subtract(nodes, service_last_owner);
482
if (length(tmp) == 0) {
483
() = service_stop(service_name,0);
487
nodes = union(tmp, service_last_owner);
490
()=move_or_start(service_name, nodes);
495
for (x = 0; x < length(services); x++) {
496
if (service_name == services[x]) {
497
% don't do anything to ourself!
502
% Simplistic dependency handling
504
depends = service_property(services[x], "depend");
505
depend_mode = service_property(services[x], "depend_mode");
507
% No dependency; do nothing
508
if (depends != service_name) {
512
(,,, owner, state) = service_status(services[x]);
513
if ((service_state == "started") and (owner < 0) and
514
(state == "stopped")) {
515
info("Dependency met; starting ", services[x]);
516
nodes = allowed_nodes(services[x]);
517
()=move_or_start(services[x], nodes);
520
% service died - stop service(s) that depend on the dead
521
if ((service_owner < 0) and (owner >= 0) and
522
(depend_mode != "soft")) {
523
info("Dependency lost; stopping ", services[x]);
524
()=service_stop(services[x]);
529
define default_config_event_handler()
531
debug("Executing default config event handler");
534
define default_user_event_handler()
540
variable target = user_target;
542
variable owner, state;
544
nodes = allowed_nodes(service_name);
545
(,,, owner, state) = service_status(service_name);
547
if (user_request == USER_RESTART) {
550
reordered = union(owner, nodes);
554
notice("Stopping ", service_name, " for relocate to ", nodes);
556
found = service_stop(service_name);
561
ret = move_or_start(service_name, nodes);
563
} else if ((user_request == USER_RELOCATE) or
564
(user_request == USER_ENABLE)) {
566
if (user_target > 0) {
567
for (x = 0; x < length(nodes); x++) {
569
% Put the preferred node at the front of the
570
% list for a user-relocate operation
572
if (nodes[x] == user_target) {
573
reordered = union(user_target, nodes);
580
warning("User specified node ", user_target,
585
if ((owner >= 0) and (user_request == USER_RELOCATE)) {
586
if (service_stop(service_name) < 0) {
591
% The current owner shouldn't be the default
592
% for a relocate operation
594
reordered = subtract(nodes, owner);
595
nodes = union(reordered, owner);
598
ret = move_or_start(service_name, nodes);
600
} else if (user_request == USER_DISABLE) {
602
ret = service_stop(service_name, 1);
604
} else if (user_request == USER_STOP) {
606
ret = service_stop(service_name);
608
} else if (user_request == USER_FREEZE) {
610
ret = service_freeze(service_name);
612
} else if (user_request == USER_UNFREEZE) {
614
ret = service_unfreeze(service_name);
616
} else if (user_request == USER_MIGRATE) {
618
ret = service_migrate(service_name, user_target);
620
} else if (user_request == USER_CONVALESCE) {
622
ret = service_convalesce(service_name);
629
if (event_type == EVENT_NODE)
630
sortedservices_node_event_handler(service_list(), "priority");
631
if (event_type == EVENT_SERVICE)
632
default_service_event_handler();
633
if (event_type == EVENT_CONFIG)
634
default_config_event_handler();
635
if (event_type == EVENT_USER)
636
user_return=default_user_event_handler();