diff --git a/include/haproxy/event_hdl-t.h b/include/haproxy/event_hdl-t.h index 77e7d933c..55d4da8a7 100644 --- a/include/haproxy/event_hdl-t.h +++ b/include/haproxy/event_hdl-t.h @@ -268,6 +268,8 @@ struct event_hdl_sub { #define EVENT_HDL_SUB_SERVER_DEL EVENT_HDL_SUB_TYPE(1,2) #define EVENT_HDL_SUB_SERVER_UP EVENT_HDL_SUB_TYPE(1,3) #define EVENT_HDL_SUB_SERVER_DOWN EVENT_HDL_SUB_TYPE(1,4) +/* server state change */ +#define EVENT_HDL_SUB_SERVER_STATE EVENT_HDL_SUB_TYPE(1,5) /* --------------------------------------- */ diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h index 69099b065..c9986dbd7 100644 --- a/include/haproxy/server-t.h +++ b/include/haproxy/server-t.h @@ -441,6 +441,7 @@ struct event_hdl_cb_data_server { * EVENT_HDL_SUB_SERVER_DEL * EVENT_HDL_SUB_SERVER_UP * EVENT_HDL_SUB_SERVER_DOWN + * EVENT_HDL_SUB_SERVER_STATE */ struct { /* safe data can be safely used from both @@ -468,6 +469,59 @@ struct event_hdl_cb_data_server { } unsafe; }; +/* check result snapshot provided through some event_hdl server events */ +struct event_hdl_cb_data_server_checkres { + uint8_t agent; /* 1 = agent check, 0 = health check */ + enum chk_result result; /* failed, passed, condpass (CHK_RES_*) */ + long duration; /* total check duration in ms */ + struct { + short status; /* check status as in check->status */ + short code; /* provided with some check statuses */ + } reason; + struct { + int cur; /* dynamic (= check->health) */ + int rise, fall; /* config dependant */ + } health; /* check's health, see check-t.h */ +}; + +/* data provided to EVENT_HDL_SUB_SERVER_STATE handlers through + * event_hdl facility + * + * Note that this may be casted to regular event_hdl_cb_data_server if + * you don't care about state related optional info + */ +struct event_hdl_cb_data_server_state { + /* provided by: + * EVENT_HDL_SUB_SERVER_STATE + */ + struct event_hdl_cb_data_server server; /* must be at the beginning */ + struct { + uint8_t type; /* 0 = operational, 1 = administrative */ + enum srv_state old_state, new_state; /* updated by both operational and admin changes */ + uint32_t requeued; /* requeued connections due to server state change */ + union { + /* state change cause: + * + * look for op_st_chg for operational state change, + * and adm_st_chg for administrative state change + */ + struct { + enum srv_op_st_chg_cause cause; + union { + /* check result is provided with + * cause == SRV_OP_STCHGC_HEALTH or cause == SRV_OP_STCHGC_AGENT + */ + struct event_hdl_cb_data_server_checkres check; + }; + } op_st_chg; + struct { + enum srv_adm_st_chg_cause cause; + } adm_st_chg; + }; + } safe; + /* no unsafe data */ +}; + /* Storage structure to load server-state lines from a flat file into * an ebtree, for faster processing */ diff --git a/src/event_hdl.c b/src/event_hdl.c index e2f01241e..87b77ebfc 100644 --- a/src/event_hdl.c +++ b/src/event_hdl.c @@ -29,6 +29,7 @@ static struct event_hdl_sub_type_map event_hdl_sub_type_map[] = { {"SERVER_DEL", EVENT_HDL_SUB_SERVER_DEL}, {"SERVER_UP", EVENT_HDL_SUB_SERVER_UP}, {"SERVER_DOWN", EVENT_HDL_SUB_SERVER_DOWN}, + {"SERVER_STATE", EVENT_HDL_SUB_SERVER_STATE}, }; /* internal types (only used in this file) */ diff --git a/src/server.c b/src/server.c index b634510e6..5bc73ccd5 100644 --- a/src/server.c +++ b/src/server.c @@ -189,6 +189,51 @@ static inline void _srv_event_hdl_prepare(struct event_hdl_cb_data_server *cb_da cb_data->unsafe.srv_lock = !thread_isolate; } +/* take an event-check snapshot from a live check */ +void _srv_event_hdl_prepare_checkres(struct event_hdl_cb_data_server_checkres *checkres, + struct check *check) +{ + checkres->agent = !!(check->state & CHK_ST_AGENT); + checkres->result = check->result; + checkres->duration = check->duration; + checkres->reason.status = check->status; + checkres->reason.code = check->code; + checkres->health.cur = check->health; + checkres->health.rise = check->rise; + checkres->health.fall = check->fall; +} + +/* Prepare SERVER_STATE event + * + * This special event will contain extra hints related to the state change + * + * Must be called with server lock held + */ +void _srv_event_hdl_prepare_state(struct event_hdl_cb_data_server_state *cb_data, + struct server *srv, int type, int cause, + enum srv_state prev_state, int requeued) +{ + /* state event provides additional info about the server state change */ + cb_data->safe.type = type; + cb_data->safe.new_state = srv->cur_state; + cb_data->safe.old_state = prev_state; + cb_data->safe.requeued = requeued; + if (type) { + /* administrative */ + cb_data->safe.adm_st_chg.cause = cause; + } + else { + /* operational */ + cb_data->safe.op_st_chg.cause = cause; + if (cause == SRV_OP_STCHGC_HEALTH || cause == SRV_OP_STCHGC_AGENT) { + struct check *check = (cause == SRV_OP_STCHGC_HEALTH) ? &srv->check : &srv->agent; + + /* provide additional check-related state change result */ + _srv_event_hdl_prepare_checkres(&cb_data->safe.op_st_chg.check, check); + } + } +} + /* server event publishing helper: publish in both global and * server dedicated subscription list. */ @@ -5744,11 +5789,19 @@ static void srv_update_status(struct server *s, int type, int cause) { int prev_srv_count = s->proxy->srv_bck + s->proxy->srv_act; enum srv_state srv_prev_state = s->cur_state; + union { + struct event_hdl_cb_data_server_state state; + struct event_hdl_cb_data_server common; + } cb_data; + int requeued; + + /* prepare common server event data */ + _srv_event_hdl_prepare(&cb_data.common, s, 0); if (type) - _srv_update_status_adm(s, cause); + requeued = _srv_update_status_adm(s, cause); else - _srv_update_status_op(s, cause); + requeued = _srv_update_status_op(s, cause); /* explicitly commit state changes (even if it was already applied implicitly * by some lb state change function), so we don't miss anything @@ -5767,6 +5820,11 @@ static void srv_update_status(struct server *s, int type, int cause) s->counters.down_trans++; } s->last_change = ns_to_sec(now_ns); + + /* publish the state change */ + _srv_event_hdl_prepare_state(&cb_data.state, + s, type, cause, srv_prev_state, requeued); + _srv_event_hdl_publish(EVENT_HDL_SUB_SERVER_STATE, cb_data.state, s); } /* check if backend stats must be updated due to the server state change */