/* Remove a realserver IPVS rule */ static int clear_service_rs(virtual_server_t * vs, list l) { element e; real_server_t *rs; long weight_sum; long down_threshold = vs->quorum - vs->hysteresis; for (e = LIST_HEAD(l); e; ELEMENT_NEXT(e)) { rs = ELEMENT_DATA(e); if (ISALIVE(rs)) { log_message(LOG_INFO, "Removing service %s from VS %s" , FMT_RS(rs) , FMT_VS(vs)); if (!ipvs_cmd(LVS_CMD_DEL_DEST, vs, rs)) return 0; UNSET_ALIVE(rs); if (!vs->omega) continue; /* In Omega mode we call VS and RS down notifiers * all the way down the exit, as necessary. */ if (rs->notify_down) { log_message(LOG_INFO, "Executing [%s] for service %s in VS %s" , rs->notify_down , FMT_RS(rs) , FMT_VS(vs)); notify_exec(rs->notify_down); } #ifdef _WITH_SNMP_ check_snmp_rs_trap(rs, vs); #endif /* Sooner or later VS will lose the quorum (if any). However, * we don't push in a sorry server then, hence the regression * is intended. */ weight_sum = weigh_live_realservers(vs); if (vs->quorum_state == UP && ( !weight_sum || weight_sum < down_threshold) ) { vs->quorum_state = DOWN; if (vs->quorum_down) { log_message(LOG_INFO, "Executing [%s] for VS %s" , vs->quorum_down , FMT_VS(vs)); notify_exec(vs->quorum_down); } #ifdef _WITH_SNMP_ check_snmp_quorum_trap(vs); #endif } } } return 1; }
/* Store new weight in real_server struct and then update kernel. */ void update_svr_wgt(int weight, virtual_server_t * vs, real_server_t * rs , int update_quorum) { if (weight != rs->weight) { log_message(LOG_INFO, "Changing weight from %d to %d for %s service %s of VS %s" , rs->weight , weight , ISALIVE(rs) ? "active" : "inactive" , FMT_RS(rs) , FMT_VS(vs)); rs->weight = weight; /* * Have weight change take effect now only if rs is in * the pool and alive and the quorum is met (or if * there is no sorry server). If not, it will take * effect later when it becomes alive. */ if (rs->set && ISALIVE(rs) && (vs->quorum_state == UP || !vs->s_svr || !ISALIVE(vs->s_svr))) ipvs_cmd(LVS_CMD_EDIT_DEST, vs, rs); if (update_quorum) update_quorum_state(vs); } }
/* Set a realserver IPVS rules */ static int init_service_rs(virtual_server_t * vs) { element e; real_server_t *rs; if (LIST_ISEMPTY(vs->rs)) { log_message(LOG_WARNING, "VS [%s] has no configured RS! Skipping RS activation." , FMT_VS(vs)); return 1; } for (e = LIST_HEAD(vs->rs); e; ELEMENT_NEXT(e)) { rs = ELEMENT_DATA(e); if (rs->reloaded) { if (rs->iweight != rs->pweight) update_svr_wgt(rs->iweight, vs, rs, 0); /* Do not re-add failed RS instantly on reload */ continue; } /* In alpha mode, be pessimistic (or realistic?) and don't * add real servers into the VS pool. They will get there * later upon healthchecks recovery (if ever). */ if (!vs->alpha && !ISALIVE(rs)) { ipvs_cmd(LVS_CMD_ADD_DEST, vs, rs); SET_ALIVE(rs); } } return 1; }
/* add or remove _alive_ real servers from a virtual server */ static void perform_quorum_state(virtual_server_t *vs, int add) { element e; real_server_t *rs; if (LIST_ISEMPTY(vs->rs)) return; log_message(LOG_INFO, "%s the pool for VS %s" , add?"Adding alive servers to":"Removing alive servers from" , FMT_VS(vs)); for (e = LIST_HEAD(vs->rs); e; ELEMENT_NEXT(e)) { rs = ELEMENT_DATA(e); if (!ISALIVE(rs)) /* We only handle alive servers */ continue; if (add) rs->alive = 0; ipvs_cmd(add?LVS_CMD_ADD_DEST:LVS_CMD_DEL_DEST, vs, rs); rs->alive = 1; } }
/* manipulate add/remove rs according to alive state */ static int perform_svr_state(int alive, virtual_server_t * vs, real_server_t * rs) { /* * | ISALIVE(rs) | alive | context * | 0 | 0 | first check failed under alpha mode, unreachable here * | 0 | 1 | RS went up, add it to the pool * | 1 | 0 | RS went down, remove it from the pool * | 1 | 1 | first check succeeded w/o alpha mode, unreachable here */ if (!ISALIVE(rs) && alive) { log_message(LOG_INFO, "%s service %s to VS %s" , (rs->inhibit) ? "Enabling" : "Adding" , FMT_RS(rs) , FMT_VS(vs)); /* Add only if we have quorum or no sorry server */ if (vs->quorum_state == UP || !vs->s_svr || !ISALIVE(vs->s_svr)) { if (ipvs_cmd(LVS_CMD_ADD_DEST, vs, rs)) return -1; } rs->alive = alive; if (rs->notify_up) { log_message(LOG_INFO, "Executing [%s] for service %s in VS %s" , rs->notify_up , FMT_RS(rs) , FMT_VS(vs)); notify_exec(rs->notify_up); } #ifdef _WITH_SNMP_CHECKER_ check_snmp_rs_trap(rs, vs); #endif /* We may have gained quorum */ update_quorum_state(vs); } if (ISALIVE(rs) && !alive) { log_message(LOG_INFO, "%s service %s from VS %s" , (rs->inhibit) ? "Disabling" : "Removing" , FMT_RS(rs) , FMT_VS(vs)); /* server is down, it is removed from the LVS realserver pool * Remove only if we have quorum or no sorry server */ if (vs->quorum_state == UP || !vs->s_svr || !ISALIVE(vs->s_svr)) { if (ipvs_cmd(LVS_CMD_DEL_DEST, vs, rs)) return -1; } rs->alive = alive; if (rs->notify_down) { log_message(LOG_INFO, "Executing [%s] for service %s in VS %s" , rs->notify_down , FMT_RS(rs) , FMT_VS(vs)); notify_exec(rs->notify_down); } #ifdef _WITH_SNMP_CHECKER_ check_snmp_rs_trap(rs, vs); #endif /* We may have lost quorum */ update_quorum_state(vs); } return 0; }
/* set quorum state depending on current weight of real servers */ static void update_quorum_state(virtual_server_t * vs) { long weight_sum = weigh_live_realservers(vs); long up_threshold = vs->quorum + vs->hysteresis; long down_threshold = vs->quorum - vs->hysteresis; /* If we have just gained quorum, it's time to consider notify_up. */ if (vs->quorum_state == DOWN && weight_sum >= up_threshold) { vs->quorum_state = UP; log_message(LOG_INFO, "Gained quorum %lu+%lu=%li <= %li for VS %s" , vs->quorum , vs->hysteresis , up_threshold , weight_sum , FMT_VS(vs)); if (vs->s_svr && ISALIVE(vs->s_svr)) { log_message(LOG_INFO, "%s sorry server %s from VS %s" , (vs->s_svr->inhibit ? "Disabling" : "Removing") , FMT_RS(vs->s_svr) , FMT_VS(vs)); ipvs_cmd(LVS_CMD_DEL_DEST, vs, vs->s_svr); vs->s_svr->alive = 0; /* Adding back alive real servers */ perform_quorum_state(vs, 1); } if (vs->quorum_up) { log_message(LOG_INFO, "Executing [%s] for VS %s" , vs->quorum_up , FMT_VS(vs)); notify_exec(vs->quorum_up); } #ifdef _WITH_SNMP_CHECKER_ check_snmp_quorum_trap(vs); #endif return; } /* If we have just lost quorum for the VS, we need to consider * VS notify_down and sorry_server cases */ if (vs->quorum_state == UP && (!weight_sum || weight_sum < down_threshold) ) { vs->quorum_state = DOWN; log_message(LOG_INFO, "Lost quorum %lu-%lu=%li > %li for VS %s" , vs->quorum , vs->hysteresis , down_threshold , weight_sum , FMT_VS(vs)); if (vs->quorum_down) { log_message(LOG_INFO, "Executing [%s] for VS %s" , vs->quorum_down , FMT_VS(vs)); notify_exec(vs->quorum_down); } if (vs->s_svr) { log_message(LOG_INFO, "%s sorry server %s to VS %s" , (vs->s_svr->inhibit ? "Enabling" : "Adding") , FMT_RS(vs->s_svr) , FMT_VS(vs)); /* the sorry server is now up in the pool, we flag it alive */ ipvs_cmd(LVS_CMD_ADD_DEST, vs, vs->s_svr); vs->s_svr->alive = 1; /* Remove remaining alive real servers */ perform_quorum_state(vs, 0); } #ifdef _WITH_SNMP_CHECKER_ check_snmp_quorum_trap(vs); #endif return; } }