Print this page
7267 SMF is fast and loose with optional dependencies
Reviewed by: Dan McDonald <danmcd@omniti.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>
Reviewed by: Albert Lee <trisk@omniti.com>
Reviewed by: Gordon Ross <gordon.w.ross@gmail.com>
*** 21,30 ****
--- 21,31 ----
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, Syneto S.R.L. All rights reserved.
* Copyright 2016 Toomas Soome <tsoome@me.com>
+ * Copyright 2016 RackTop Systems.
*/
/*
* graph.c - master restarter graph engine
*
*** 206,218 ****
(state) == RESTARTER_STATE_DEGRADED || \
(state) == RESTARTER_STATE_OFFLINE)
#define is_depgrp_bypassed(v) ((v->gv_type == GVT_GROUP) && \
((v->gv_depgroup == DEPGRP_EXCLUDE_ALL) || \
- (v->gv_depgroup == DEPGRP_OPTIONAL_ALL) || \
(v->gv_restart < RERR_RESTART)))
static uu_list_pool_t *graph_edge_pool, *graph_vertex_pool;
static uu_list_t *dgraph;
static pthread_mutex_t dgraph_lock;
/*
--- 207,222 ----
(state) == RESTARTER_STATE_DEGRADED || \
(state) == RESTARTER_STATE_OFFLINE)
#define is_depgrp_bypassed(v) ((v->gv_type == GVT_GROUP) && \
((v->gv_depgroup == DEPGRP_EXCLUDE_ALL) || \
(v->gv_restart < RERR_RESTART)))
+ #define is_inst_bypassed(v) ((v->gv_type == GVT_INST) && \
+ ((v->gv_flags & GV_TODISABLE) || \
+ (v->gv_flags & GV_TOOFFLINE)))
+
static uu_list_pool_t *graph_edge_pool, *graph_vertex_pool;
static uu_list_t *dgraph;
static pthread_mutex_t dgraph_lock;
/*
*** 1296,1306 ****
if (satbility && s == 0)
satisfiable = B_TRUE;
}
! return (!satbility || satisfiable ? 0 : -1);
}
/*
* An optional_all dependency only considers elements which are configured,
* enabled, and not in maintenance. If any are unsatisfied, then the dependency
--- 1300,1310 ----
if (satbility && s == 0)
satisfiable = B_TRUE;
}
! return ((!satbility || satisfiable) ? 0 : -1);
}
/*
* An optional_all dependency only considers elements which are configured,
* enabled, and not in maintenance. If any are unsatisfied, then the dependency
*** 1327,1363 ****
edge = uu_list_next(groupv->gv_dependencies, edge)) {
v = edge->ge_vertex;
switch (v->gv_type) {
case GVT_INST:
! /* Skip missing or disabled instances */
! if ((v->gv_flags & (GV_CONFIGURED | GV_ENABLED)) !=
! (GV_CONFIGURED | GV_ENABLED))
continue;
if (v->gv_state == RESTARTER_STATE_MAINT)
continue;
- if (v->gv_flags & GV_TOOFFLINE)
- continue;
-
any_qualified = B_TRUE;
! if (v->gv_state == RESTARTER_STATE_OFFLINE) {
/*
! * For offline dependencies, treat unsatisfiable
! * as satisfied.
*/
i = dependency_satisfied(v, B_TRUE);
if (i == -1)
i = 1;
- } else if (v->gv_state == RESTARTER_STATE_DISABLED) {
- /*
- * The service is enabled, but hasn't
- * transitioned out of disabled yet. Treat it
- * as unsatisfied (not unsatisfiable).
- */
- i = 0;
} else {
i = dependency_satisfied(v, satbility);
}
break;
--- 1331,1357 ----
edge = uu_list_next(groupv->gv_dependencies, edge)) {
v = edge->ge_vertex;
switch (v->gv_type) {
case GVT_INST:
! /* Skip missing instances */
! if ((v->gv_flags & GV_CONFIGURED) == 0)
continue;
if (v->gv_state == RESTARTER_STATE_MAINT)
continue;
any_qualified = B_TRUE;
! if (v->gv_state == RESTARTER_STATE_OFFLINE ||
! v->gv_state == RESTARTER_STATE_DISABLED) {
/*
! * For offline/disabled dependencies,
! * treat unsatisfiable as satisfied.
*/
i = dependency_satisfied(v, B_TRUE);
if (i == -1)
i = 1;
} else {
i = dependency_satisfied(v, satbility);
}
break;
*** 1366,1440 ****
i = dependency_satisfied(v, satbility);
break;
case GVT_SVC: {
! boolean_t svc_any_qualified;
! boolean_t svc_satisfied;
! boolean_t svc_satisfiable;
! graph_vertex_t *v2;
! graph_edge_t *e2;
- svc_any_qualified = B_FALSE;
- svc_satisfied = B_FALSE;
- svc_satisfiable = B_FALSE;
-
- for (e2 = uu_list_first(v->gv_dependencies);
- e2 != NULL;
- e2 = uu_list_next(v->gv_dependencies, e2)) {
- v2 = e2->ge_vertex;
- assert(v2->gv_type == GVT_INST);
-
- if ((v2->gv_flags &
- (GV_CONFIGURED | GV_ENABLED)) !=
- (GV_CONFIGURED | GV_ENABLED))
- continue;
-
- if (v2->gv_state == RESTARTER_STATE_MAINT)
- continue;
-
- if (v2->gv_flags & GV_TOOFFLINE)
- continue;
-
- svc_any_qualified = B_TRUE;
-
- if (v2->gv_state == RESTARTER_STATE_OFFLINE) {
- /*
- * For offline dependencies, treat
- * unsatisfiable as satisfied.
- */
- i = dependency_satisfied(v2, B_TRUE);
- if (i == -1)
- i = 1;
- } else if (v2->gv_state ==
- RESTARTER_STATE_DISABLED) {
- i = 0;
- } else {
- i = dependency_satisfied(v2, satbility);
- }
-
- if (i == 1) {
- svc_satisfied = B_TRUE;
break;
}
- if (i == 0)
- svc_satisfiable = B_TRUE;
- }
- if (!svc_any_qualified)
- continue;
- any_qualified = B_TRUE;
- if (svc_satisfied) {
- i = 1;
- } else if (svc_satisfiable) {
- i = 0;
- } else {
- i = -1;
- }
- break;
- }
-
case GVT_GROUP:
default:
#ifndef NDEBUG
uu_warn("%s:%d: Unexpected vertex type %d.\n", __FILE__,
__LINE__, v->gv_type);
--- 1360,1375 ----
i = dependency_satisfied(v, satbility);
break;
case GVT_SVC: {
! any_qualified = B_TRUE;
! i = optional_all_satisfied(v, satbility);
break;
}
case GVT_GROUP:
default:
#ifndef NDEBUG
uu_warn("%s:%d: Unexpected vertex type %d.\n", __FILE__,
__LINE__, v->gv_type);
*** 1607,1634 ****
}
return (-1);
}
/*
! * Any vertex with the GV_TOOFFLINE flag set is guaranteed
! * to have its dependencies unsatisfiable.
*/
- if (v->gv_flags & GV_TOOFFLINE)
- return (-1);
-
switch (v->gv_state) {
case RESTARTER_STATE_ONLINE:
case RESTARTER_STATE_DEGRADED:
return (1);
case RESTARTER_STATE_OFFLINE:
! if (!satbility)
! return (0);
return (instance_satisfied(v, satbility) != -1 ?
0 : -1);
case RESTARTER_STATE_DISABLED:
case RESTARTER_STATE_MAINT:
return (-1);
case RESTARTER_STATE_UNINIT:
return (0);
--- 1542,1580 ----
}
return (-1);
}
/*
! * Vertices may be transitioning so we try to figure out if
! * the end state is likely to satisfy the dependency instead
! * of assuming the dependency is unsatisfied/unsatisfiable.
! *
! * Support for optional_all dependencies depends on us getting
! * this right because unsatisfiable dependencies are treated
! * as being satisfied.
*/
switch (v->gv_state) {
case RESTARTER_STATE_ONLINE:
case RESTARTER_STATE_DEGRADED:
+ if (v->gv_flags & GV_TODISABLE)
+ return (-1);
+ if (v->gv_flags & GV_TOOFFLINE)
+ return (0);
return (1);
case RESTARTER_STATE_OFFLINE:
! if (!satbility || v->gv_flags & GV_TODISABLE)
! return (satbility ? -1 : 0);
return (instance_satisfied(v, satbility) != -1 ?
0 : -1);
case RESTARTER_STATE_DISABLED:
+ if (!satbility || !(v->gv_flags & GV_ENABLED))
+ return (satbility ? -1 : 0);
+ return (instance_satisfied(v, satbility) != -1 ?
+ 0 : -1);
+
case RESTARTER_STATE_MAINT:
return (-1);
case RESTARTER_STATE_UNINIT:
return (0);
*** 1727,1736 ****
--- 1673,1685 ----
*/
/*ARGSUSED*/
static int
satbility_cb(graph_vertex_t *v, void *arg)
{
+ if (is_inst_bypassed(v))
+ return (UU_WALK_NEXT);
+
if (v->gv_type == GVT_INST)
graph_start_if_satisfied(v);
return (UU_WALK_NEXT);
}
*** 1741,1769 ****
graph_walk(v, WALK_DEPENDENTS, satbility_cb, NULL, NULL);
}
static void propagate_stop(graph_vertex_t *, void *);
! /* ARGSUSED */
static void
propagate_start(graph_vertex_t *v, void *arg)
{
switch (v->gv_type) {
case GVT_INST:
graph_start_if_satisfied(v);
break;
case GVT_GROUP:
if (v->gv_depgroup == DEPGRP_EXCLUDE_ALL) {
graph_walk_dependents(v, propagate_stop,
(void *)RERR_RESTART);
break;
}
/* FALLTHROUGH */
case GVT_SVC:
! graph_walk_dependents(v, propagate_start, NULL);
break;
case GVT_FILE:
#ifndef NDEBUG
uu_warn("%s:%d: propagate_start() encountered GVT_FILE.\n",
--- 1690,1740 ----
graph_walk(v, WALK_DEPENDENTS, satbility_cb, NULL, NULL);
}
static void propagate_stop(graph_vertex_t *, void *);
! /*
! * propagate_start()
! *
! * This function is used to propagate a start event to the dependents of the
! * given vertex. Any dependents that are offline but have their dependencies
! * satisfied are started. Any dependents that are online and have restart_on
! * set to "restart" or "refresh" are restarted because their dependencies have
! * just changed. This only happens with optional_all dependencies.
! */
static void
propagate_start(graph_vertex_t *v, void *arg)
{
+ restarter_error_t err = (restarter_error_t)arg;
+
+ if (is_inst_bypassed(v))
+ return;
+
switch (v->gv_type) {
case GVT_INST:
+ /* Restarter */
+ if (inst_running(v)) {
+ if (err == RERR_RESTART || err == RERR_REFRESH) {
+ vertex_send_event(v,
+ RESTARTER_EVENT_TYPE_STOP_RESET);
+ }
+ } else {
graph_start_if_satisfied(v);
+ }
break;
case GVT_GROUP:
if (v->gv_depgroup == DEPGRP_EXCLUDE_ALL) {
graph_walk_dependents(v, propagate_stop,
(void *)RERR_RESTART);
break;
}
+ err = v->gv_restart;
/* FALLTHROUGH */
case GVT_SVC:
! graph_walk_dependents(v, propagate_start, (void *)err);
break;
case GVT_FILE:
#ifndef NDEBUG
uu_warn("%s:%d: propagate_start() encountered GVT_FILE.\n",
*** 1779,1795 ****
#endif
abort();
}
}
static void
propagate_stop(graph_vertex_t *v, void *arg)
{
- graph_edge_t *e;
- graph_vertex_t *svc;
restarter_error_t err = (restarter_error_t)arg;
switch (v->gv_type) {
case GVT_INST:
/* Restarter */
if (err > RERR_NONE && inst_running(v)) {
if (err == RERR_RESTART || err == RERR_REFRESH) {
--- 1750,1776 ----
#endif
abort();
}
}
+ /*
+ * propagate_stop()
+ *
+ * This function is used to propagate a stop event to the dependents of the
+ * given vertex. Any dependents that are online (or in degraded state) with
+ * the restart_on property set to "restart" or "refresh" will be stopped as
+ * their dependencies have just changed, propagate_start() will start them
+ * again once their dependencies have been re-satisfied.
+ */
static void
propagate_stop(graph_vertex_t *v, void *arg)
{
restarter_error_t err = (restarter_error_t)arg;
+ if (is_inst_bypassed(v))
+ return;
+
switch (v->gv_type) {
case GVT_INST:
/* Restarter */
if (err > RERR_NONE && inst_running(v)) {
if (err == RERR_RESTART || err == RERR_REFRESH) {
*** 1813,1842 ****
abort();
/* NOTREACHED */
case GVT_GROUP:
if (v->gv_depgroup == DEPGRP_EXCLUDE_ALL) {
! graph_walk_dependents(v, propagate_start, NULL);
break;
}
if (err == RERR_NONE || err > v->gv_restart)
break;
! assert(uu_list_numnodes(v->gv_dependents) == 1);
! e = uu_list_first(v->gv_dependents);
! svc = e->ge_vertex;
!
! if (inst_running(svc)) {
! if (err == RERR_RESTART || err == RERR_REFRESH) {
! vertex_send_event(svc,
! RESTARTER_EVENT_TYPE_STOP_RESET);
! } else {
! vertex_send_event(svc,
! RESTARTER_EVENT_TYPE_STOP);
! }
! }
break;
default:
#ifndef NDEBUG
uu_warn("%s:%d: Unknown vertex type %d.\n", __FILE__, __LINE__,
--- 1794,1812 ----
abort();
/* NOTREACHED */
case GVT_GROUP:
if (v->gv_depgroup == DEPGRP_EXCLUDE_ALL) {
! graph_walk_dependents(v, propagate_start,
! (void *)RERR_NONE);
break;
}
if (err == RERR_NONE || err > v->gv_restart)
break;
! graph_walk_dependents(v, propagate_stop, arg);
break;
default:
#ifndef NDEBUG
uu_warn("%s:%d: Unknown vertex type %d.\n", __FILE__, __LINE__,
*** 4391,4403 ****
if ((vv->gv_state == RESTARTER_STATE_ONLINE) ||
(vv->gv_state == RESTARTER_STATE_DEGRADED))
return (B_FALSE);
} else {
/*
! * Skip all excluded and optional_all dependencies
! * and decide whether to offline the service based
! * on restart_on attribute.
*/
if (is_depgrp_bypassed(vv))
continue;
/*
--- 4361,4373 ----
if ((vv->gv_state == RESTARTER_STATE_ONLINE) ||
(vv->gv_state == RESTARTER_STATE_DEGRADED))
return (B_FALSE);
} else {
/*
! * Skip all excluded dependents and decide whether
! * to offline the service based on the restart_on
! * attribute.
*/
if (is_depgrp_bypassed(vv))
continue;
/*
*** 4958,4968 ****
restarter_error_t rerr)
{
if (type == PROPAGATE_STOP) {
graph_walk_dependents(v, propagate_stop, (void *)rerr);
} else if (type == PROPAGATE_START || type == PROPAGATE_SAT) {
! graph_walk_dependents(v, propagate_start, NULL);
if (type == PROPAGATE_SAT)
propagate_satbility(v);
} else {
#ifndef NDEBUG
--- 4928,4938 ----
restarter_error_t rerr)
{
if (type == PROPAGATE_STOP) {
graph_walk_dependents(v, propagate_stop, (void *)rerr);
} else if (type == PROPAGATE_START || type == PROPAGATE_SAT) {
! graph_walk_dependents(v, propagate_start, (void *)RERR_NONE);
if (type == PROPAGATE_SAT)
propagate_satbility(v);
} else {
#ifndef NDEBUG
*** 5029,5039 ****
graph_walk_dependents(v, propagate_stop, (void *)RERR_RESTART);
v->gv_flags &= ~GV_CONFIGURED;
v->gv_flags &= ~GV_DEATHROW;
! graph_walk_dependents(v, propagate_start, NULL);
propagate_satbility(v);
/*
* If there are no (non-service) dependents, the vertex can be
* completely removed.
--- 4999,5009 ----
graph_walk_dependents(v, propagate_stop, (void *)RERR_RESTART);
v->gv_flags &= ~GV_CONFIGURED;
v->gv_flags &= ~GV_DEATHROW;
! graph_walk_dependents(v, propagate_start, (void *)RERR_NONE);
propagate_satbility(v);
/*
* If there are no (non-service) dependents, the vertex can be
* completely removed.
*** 5445,5465 ****
if (v->gv_flags & GV_TOOFFLINE)
return (UU_WALK_NEXT);
switch (v->gv_type) {
case GVT_INST:
! /* If the instance is already disabled, skip it. */
! if (!(v->gv_flags & GV_ENABLED))
return (UU_WALK_NEXT);
v->gv_flags |= GV_TOOFFLINE;
log_framework(LOG_DEBUG, "%s added to subtree\n", v->gv_name);
break;
case GVT_GROUP:
/*
! * Skip all excluded and optional_all dependencies and decide
! * whether to offline the service based on restart_on attribute.
*/
if (is_depgrp_bypassed(v))
return (UU_WALK_NEXT);
break;
}
--- 5415,5435 ----
if (v->gv_flags & GV_TOOFFLINE)
return (UU_WALK_NEXT);
switch (v->gv_type) {
case GVT_INST:
! /* If the instance is already offline, skip it. */
! if (!inst_running(v))
return (UU_WALK_NEXT);
v->gv_flags |= GV_TOOFFLINE;
log_framework(LOG_DEBUG, "%s added to subtree\n", v->gv_name);
break;
case GVT_GROUP:
/*
! * Skip all excluded dependents and decide whether to offline
! * the service based on the restart_on attribute.
*/
if (is_depgrp_bypassed(v))
return (UU_WALK_NEXT);
break;
}