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; }