4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * Copyright 2016 RackTop Systems.
30 * transition.c - Graph State Machine
32 * The graph state machine is implemented here, with a typical approach
33 * of a function per state. Separating the implementation allows more
34 * clarity into the actions taken on notification of state change, as well
35 * as a place for future expansion including hooks for configurable actions.
36 * All functions are called with dgraph_lock held.
38 * The start action for this state machine is not explicit. The states
39 * (ONLINE and DEGRADED) which need to know when they're entering the state
40 * due to a daemon restart implement this understanding by checking for
41 * transition from uninitialized. In the future, this would likely be better
42 * as an explicit start action instead of relying on an overloaded transition.
44 * All gt_enter functions use the same set of return codes.
46 * ECONNABORTED repository connection aborted
52 gt_running(restarter_instance_state_t state
)
54 if (state
== RESTARTER_STATE_ONLINE
||
55 state
== RESTARTER_STATE_DEGRADED
)
62 gt_enter_uninit(scf_handle_t
*h
, graph_vertex_t
*v
,
63 restarter_instance_state_t old_state
, restarter_error_t rerr
)
68 /* Initialize instance by refreshing it. */
70 err
= libscf_fmri_get_instance(h
, v
->gv_name
, &inst
);
76 return (ECONNABORTED
);
84 bad_error("libscf_fmri_get_instance", err
);
87 err
= refresh_vertex(v
, inst
);
89 graph_enable_by_vertex(v
, v
->gv_flags
& GV_ENABLED
, 0);
91 scf_instance_destroy(inst
);
93 /* If the service was running, propagate a stop event. */
94 if (gt_running(old_state
)) {
95 log_framework(LOG_DEBUG
, "Propagating stop of %s.\n",
98 graph_transition_propagate(v
, PROPAGATE_STOP
, rerr
);
101 graph_transition_sulogin(RESTARTER_STATE_UNINIT
, old_state
);
107 gt_enter_maint(scf_handle_t
*h
, graph_vertex_t
*v
,
108 restarter_instance_state_t old_state
, restarter_error_t rerr
)
110 int to_offline
= v
->gv_flags
& GV_TOOFFLINE
;
113 * If the service was running, propagate a stop event. If the
114 * service was not running the maintenance transition may satisfy
115 * optional dependencies and should be propagated to determine
116 * whether new dependents are satisfiable.
117 * Instances that transition to maintenance and have the GV_TOOFFLINE
118 * flag are special because they can expose new subtree leaves so
119 * propagate the offline to the instance dependencies.
122 /* instance transitioning to maintenance is considered disabled */
123 v
->gv_flags
&= ~GV_TODISABLE
;
124 v
->gv_flags
&= ~GV_TOOFFLINE
;
126 if (gt_running(old_state
)) {
128 * Handle state change during instance disabling.
129 * Propagate offline to the new exposed leaves.
132 log_framework(LOG_DEBUG
, "%s removed from subtree\n",
135 graph_offline_subtree_leaves(v
, (void *)h
);
138 log_framework(LOG_DEBUG
, "Propagating maintenance (stop) of "
139 "%s.\n", v
->gv_name
);
141 graph_transition_propagate(v
, PROPAGATE_STOP
, rerr
);
144 * The maintenance transition may satisfy optional_all/restart
145 * dependencies and should be propagated to determine
146 * whether new dependents are satisfiable.
148 graph_transition_propagate(v
, PROPAGATE_SAT
, rerr
);
150 log_framework(LOG_DEBUG
, "Propagating maintenance of %s.\n",
153 graph_transition_propagate(v
, PROPAGATE_SAT
, rerr
);
156 graph_transition_sulogin(RESTARTER_STATE_MAINT
, old_state
);
162 gt_enter_offline(scf_handle_t
*h
, graph_vertex_t
*v
,
163 restarter_instance_state_t old_state
, restarter_error_t rerr
)
165 int to_offline
= v
->gv_flags
& GV_TOOFFLINE
;
166 int to_disable
= v
->gv_flags
& GV_TODISABLE
;
168 v
->gv_flags
&= ~GV_TOOFFLINE
;
171 * If the instance should be enabled, see if we can start it.
172 * Otherwise send a disable command.
173 * If a instance has the GV_TOOFFLINE flag set then it must
174 * remains offline until the disable process completes.
176 if (v
->gv_flags
& GV_ENABLED
) {
177 if (to_offline
== 0 && to_disable
== 0)
178 graph_start_if_satisfied(v
);
180 if (gt_running(old_state
) && v
->gv_post_disable_f
)
181 v
->gv_post_disable_f();
183 vertex_send_event(v
, RESTARTER_EVENT_TYPE_DISABLE
);
187 * If the service was running, propagate a stop event. If the
188 * service was not running the offline transition may satisfy
189 * optional dependencies and should be propagated to determine
190 * whether new dependents are satisfiable.
191 * Instances that transition to offline and have the GV_TOOFFLINE flag
192 * are special because they can expose new subtree leaves so propagate
193 * the offline to the instance dependencies.
195 if (gt_running(old_state
)) {
197 * Handle state change during instance disabling.
198 * Propagate offline to the new exposed leaves.
201 log_framework(LOG_DEBUG
, "%s removed from subtree\n",
204 graph_offline_subtree_leaves(v
, (void *)h
);
207 log_framework(LOG_DEBUG
, "Propagating stop of %s.\n",
210 graph_transition_propagate(v
, PROPAGATE_STOP
, rerr
);
213 * The offline transition may satisfy require_any/restart
214 * dependencies and should be propagated to determine
215 * whether new dependents are satisfiable.
217 graph_transition_propagate(v
, PROPAGATE_SAT
, rerr
);
219 log_framework(LOG_DEBUG
, "Propagating offline of %s.\n",
222 graph_transition_propagate(v
, PROPAGATE_SAT
, rerr
);
225 graph_transition_sulogin(RESTARTER_STATE_OFFLINE
, old_state
);
231 gt_enter_disabled(scf_handle_t
*h
, graph_vertex_t
*v
,
232 restarter_instance_state_t old_state
, restarter_error_t rerr
)
234 int to_offline
= v
->gv_flags
& GV_TOOFFLINE
;
236 v
->gv_flags
&= ~GV_TODISABLE
;
237 v
->gv_flags
&= ~GV_TOOFFLINE
;
240 * If the instance should be disabled, no problem. Otherwise,
241 * send an enable command, which should result in the instance
242 * moving to OFFLINE unless the instance is part of a subtree
243 * (non root) and in this case the result is unpredictable.
245 if (v
->gv_flags
& GV_ENABLED
) {
246 vertex_send_event(v
, RESTARTER_EVENT_TYPE_ENABLE
);
247 } else if (gt_running(old_state
) && v
->gv_post_disable_f
) {
248 v
->gv_post_disable_f();
252 * If the service was running, propagate this as a stop. If the
253 * service was not running the disabled transition may satisfy
254 * optional dependencies and should be propagated to determine
255 * whether new dependents are satisfiable.
257 if (gt_running(old_state
)) {
259 * We need to propagate the offline to new exposed leaves in
260 * case we've just disabled an instance that was part of a
264 log_framework(LOG_DEBUG
, "%s removed from subtree\n",
268 * Handle state change during instance disabling.
269 * Propagate offline to the new exposed leaves.
271 graph_offline_subtree_leaves(v
, (void *)h
);
275 log_framework(LOG_DEBUG
, "Propagating stop of %s.\n",
278 graph_transition_propagate(v
, PROPAGATE_STOP
, rerr
);
281 * The disable transition may satisfy optional_all/restart
282 * dependencies and should be propagated to determine
283 * whether new dependents are satisfiable.
285 graph_transition_propagate(v
, PROPAGATE_SAT
, rerr
);
287 log_framework(LOG_DEBUG
, "Propagating disable of %s.\n",
290 graph_transition_propagate(v
, PROPAGATE_SAT
, rerr
);
293 graph_transition_sulogin(RESTARTER_STATE_DISABLED
, old_state
);
298 gt_internal_online_or_degraded(scf_handle_t
*h
, graph_vertex_t
*v
,
299 restarter_instance_state_t old_state
, restarter_error_t rerr
)
304 * If the instance has just come up, update the start
307 if (gt_running(old_state
) == 0) {
309 * Don't fire if we're just recovering state
312 if (old_state
!= RESTARTER_STATE_UNINIT
&&
314 v
->gv_post_online_f();
316 r
= libscf_snapshots_poststart(h
, v
->gv_name
, B_TRUE
);
321 * If ENOENT, the instance must have been
322 * deleted. Pretend we were successful since
323 * we should get a delete event later.
328 return (ECONNABORTED
);
333 bad_error("libscf_snapshots_poststart", r
);
337 if (!(v
->gv_flags
& GV_ENABLED
)) {
338 vertex_send_event(v
, RESTARTER_EVENT_TYPE_DISABLE
);
339 } else if (v
->gv_flags
& GV_TOOFFLINE
) {
341 * If the vertex has the GV_TOOFFLINE flag set then that's
342 * because the instance was transitioning from offline to
343 * online and the reverse disable algorithm doesn't offline
344 * those instances because it was already appearing offline.
350 if (gt_running(old_state
) == 0) {
351 log_framework(LOG_DEBUG
, "Propagating start of %s.\n",
354 graph_transition_propagate(v
, PROPAGATE_START
, rerr
);
355 } else if (rerr
== RERR_REFRESH
) {
356 /* For refresh we'll get a message sans state change */
358 log_framework(LOG_DEBUG
, "Propagating refresh of %s.\n",
361 graph_transition_propagate(v
, PROPAGATE_STOP
, rerr
);
368 gt_enter_online(scf_handle_t
*h
, graph_vertex_t
*v
,
369 restarter_instance_state_t old_state
, restarter_error_t rerr
)
373 r
= gt_internal_online_or_degraded(h
, v
, old_state
, rerr
);
377 graph_transition_sulogin(RESTARTER_STATE_ONLINE
, old_state
);
382 gt_enter_degraded(scf_handle_t
*h
, graph_vertex_t
*v
,
383 restarter_instance_state_t old_state
, restarter_error_t rerr
)
387 r
= gt_internal_online_or_degraded(h
, v
, old_state
, rerr
);
391 graph_transition_sulogin(RESTARTER_STATE_DEGRADED
, old_state
);
396 * gt_transition() implements the state transition for the graph
397 * state machine. It can return:
399 * ECONNABORTED repository connection aborted
401 * v->gv_state should be set to the state we're transitioning to before
402 * calling this function.
405 gt_transition(scf_handle_t
*h
, graph_vertex_t
*v
, restarter_error_t rerr
,
406 restarter_instance_state_t old_state
)
409 int lost_repository
= 0;
412 * If there's a common set of work to be done on exit from the
413 * old_state, include it as a separate set of functions here. For
414 * now there's no such work, so there are no gt_exit functions.
417 err
= vertex_subgraph_dependencies_shutdown(h
, v
, old_state
);
427 bad_error("vertex_subgraph_dependencies_shutdown", err
);
431 * Now call the appropriate gt_enter function for the new state.
433 switch (v
->gv_state
) {
434 case RESTARTER_STATE_UNINIT
:
435 err
= gt_enter_uninit(h
, v
, old_state
, rerr
);
438 case RESTARTER_STATE_DISABLED
:
439 err
= gt_enter_disabled(h
, v
, old_state
, rerr
);
442 case RESTARTER_STATE_OFFLINE
:
443 err
= gt_enter_offline(h
, v
, old_state
, rerr
);
446 case RESTARTER_STATE_ONLINE
:
447 err
= gt_enter_online(h
, v
, old_state
, rerr
);
450 case RESTARTER_STATE_DEGRADED
:
451 err
= gt_enter_degraded(h
, v
, old_state
, rerr
);
454 case RESTARTER_STATE_MAINT
:
455 err
= gt_enter_maint(h
, v
, old_state
, rerr
);
459 /* Shouldn't be in an invalid state. */
461 uu_warn("%s:%d: Invalid state %d.\n", __FILE__
, __LINE__
,
478 "gt_enter_%s() failed with unexpected error %d.\n",
479 __FILE__
, __LINE__
, instance_state_str
[v
->gv_state
], err
);
484 return (lost_repository
? ECONNABORTED
: 0);