| /** @file |
| |
| Implementation of Host Proxy routing |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| #include "HostStatus.h" |
| #include "ProcessManager.h" |
| |
| static RecRawStatBlock *host_status_rsb = nullptr; |
| |
| inline void |
| getStatName(std::string &stat_name, const char *name) |
| { |
| stat_name = stat_prefix + name; |
| } |
| |
| static void * |
| mgmt_host_status_up_callback(void *x, char *data, int len) |
| { |
| MgmtInt op; |
| MgmtMarshallString name; |
| MgmtMarshallInt down_time; |
| MgmtMarshallString reason_str; |
| std::string stat_name; |
| char buf[1024] = {0}; |
| static const MgmtMarshallType fields[] = {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}; |
| Debug("host_statuses", "%s:%s:%d - data: %s, len: %d\n", __FILE__, __func__, __LINE__, data, len); |
| |
| if (mgmt_message_parse(data, len, fields, countof(fields), &op, &name, &reason_str, &down_time) == -1) { |
| Error("Plugin message - RPC parsing error - message discarded."); |
| } |
| Debug("host_statuses", "op: %ld, name: %s, down_time: %d, reason_str: %s", static_cast<long>(op), name, |
| static_cast<int>(down_time), reason_str); |
| |
| unsigned int reason = Reason::getReason(reason_str); |
| |
| getStatName(stat_name, name); |
| if (data != nullptr) { |
| Debug("host_statuses", "marking up server %s", data); |
| HostStatus &hs = HostStatus::instance(); |
| if (hs.getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) { |
| hs.createHostStat(name); |
| } |
| hs.setHostStatus(name, HostStatus_t::HOST_STATUS_UP, down_time, reason); |
| } |
| return nullptr; |
| } |
| |
| static void * |
| mgmt_host_status_down_callback(void *x, char *data, int len) |
| { |
| MgmtInt op; |
| MgmtMarshallString name; |
| MgmtMarshallInt down_time; |
| MgmtMarshallString reason_str; |
| std::string stat_name; |
| char buf[1024] = {0}; |
| static const MgmtMarshallType fields[] = {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}; |
| Debug("host_statuses", "%s:%s:%d - data: %s, len: %d\n", __FILE__, __func__, __LINE__, data, len); |
| |
| if (mgmt_message_parse(data, len, fields, countof(fields), &op, &name, &reason_str, &down_time) == -1) { |
| Error("Plugin message - RPC parsing error - message discarded."); |
| } |
| Debug("host_statuses", "op: %ld, name: %s, down_time: %d, reason_str: %s", static_cast<long>(op), name, |
| static_cast<int>(down_time), reason_str); |
| |
| unsigned int reason = Reason::getReason(reason_str); |
| |
| if (data != nullptr) { |
| Debug("host_statuses", "marking down server %s", name); |
| HostStatus &hs = HostStatus::instance(); |
| if (hs.getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) { |
| hs.createHostStat(name); |
| } |
| hs.setHostStatus(name, HostStatus_t::HOST_STATUS_DOWN, down_time, reason); |
| } |
| return nullptr; |
| } |
| |
| HostStatRec::HostStatRec() |
| : status(HOST_STATUS_UP), |
| reasons(0), |
| active_marked_down(0), |
| local_marked_down(0), |
| manual_marked_down(0), |
| self_detect_marked_down(0), |
| active_down_time(0), |
| local_down_time(0), |
| manual_down_time(0){}; |
| |
| HostStatRec::HostStatRec(std::string str) |
| { |
| std::vector<std::string> v1; |
| std::stringstream ss1(str); |
| |
| reasons = 0; |
| |
| // parse the csv strings from the stat record value string. |
| while (ss1.good()) { |
| char b1[64]; |
| ss1.getline(b1, 64, ','); |
| v1.push_back(b1); |
| } |
| |
| // v1 contains 5 strings. |
| ink_assert(v1.size() == 5); |
| |
| // set the status and reasons fields. |
| for (unsigned int i = 0; i < v1.size(); i++) { |
| if (i == 0) { // set the status field |
| if (v1.at(i).compare("HOST_STATUS_UP") == 0) { |
| status = HOST_STATUS_UP; |
| } else if (v1.at(i).compare("HOST_STATUS_DOWN") == 0) { |
| status = HOST_STATUS_DOWN; |
| } |
| } else { // parse and set remaining reason fields. |
| std::vector<std::string> v2; |
| v2.clear(); |
| std::stringstream ss2(v1.at(i)); |
| while (ss2.good()) { |
| char b2[64]; |
| ss2.getline(b2, 64, ':'); |
| v2.push_back(b2); |
| } |
| // v2 contains 4 strings. |
| ink_assert(v2.size() == 3 || v2.size() == 4); |
| |
| if (v2.at(0).compare("ACTIVE") == 0) { |
| if (v2.at(1).compare("DOWN") == 0) { |
| reasons |= Reason::ACTIVE; |
| } else if (reasons & Reason::ACTIVE) { |
| reasons ^= Reason::ACTIVE; |
| } |
| active_marked_down = atoi(v2.at(2).c_str()); |
| active_down_time = atoi(v2.at(3).c_str()); |
| } |
| if (v2.at(0).compare("LOCAL") == 0) { |
| if (v2.at(1).compare("DOWN") == 0) { |
| reasons |= Reason::LOCAL; |
| } else if (reasons & Reason::LOCAL) { |
| reasons ^= Reason::LOCAL; |
| } |
| local_marked_down = atoi(v2.at(2).c_str()); |
| local_down_time = atoi(v2.at(3).c_str()); |
| } |
| if (v2.at(0).compare("MANUAL") == 0) { |
| if (v2.at(1).compare("DOWN") == 0) { |
| reasons |= Reason::MANUAL; |
| } else if (reasons & Reason::MANUAL) { |
| reasons ^= Reason::MANUAL; |
| } |
| manual_marked_down = atoi(v2.at(2).c_str()); |
| manual_down_time = atoi(v2.at(3).c_str()); |
| } |
| if (v2.at(0).compare("SELF_DETECT") == 0) { |
| if (v2.at(1).compare("DOWN") == 0) { |
| reasons |= Reason::SELF_DETECT; |
| } else if (reasons & Reason::SELF_DETECT) { |
| reasons ^= Reason::SELF_DETECT; |
| } |
| self_detect_marked_down = atoi(v2.at(2).c_str()); |
| } |
| } |
| } |
| } |
| |
| static void |
| handle_record_read(const RecRecord *rec, void *edata) |
| { |
| HostStatus &hs = HostStatus::instance(); |
| std::string hostname; |
| |
| if (rec) { |
| Debug("host_statuses", "name: %s", rec->name); |
| |
| // parse the hostname from the stat name |
| char *s = const_cast<char *>(rec->name); |
| // 1st move the pointer past the stat prefix. |
| s += stat_prefix.length(); |
| hostname = s; |
| hs.createHostStat(hostname.c_str(), rec->data.rec_string); |
| HostStatRec h(rec->data.rec_string); |
| hs.loadRecord(hostname, h); |
| } |
| } |
| |
| HostStatus::HostStatus() |
| { |
| ink_rwlock_init(&host_status_rwlock); |
| pmgmt->registerMgmtCallback(MGMT_EVENT_HOST_STATUS_UP, &mgmt_host_status_up_callback); |
| pmgmt->registerMgmtCallback(MGMT_EVENT_HOST_STATUS_DOWN, &mgmt_host_status_down_callback); |
| host_status_rsb = RecAllocateRawStatBlock((int)TS_MAX_API_STATS); |
| } |
| |
| HostStatus::~HostStatus() |
| { |
| for (auto &&it : hosts_statuses) { |
| ats_free(it.second); |
| } |
| // release the read and writer locks. |
| ink_rwlock_destroy(&host_status_rwlock); |
| } |
| |
| void |
| HostStatus::loadHostStatusFromStats() |
| { |
| if (RecLookupMatchingRecords(RECT_PROCESS, stat_prefix.c_str(), handle_record_read, nullptr) != REC_ERR_OKAY) { |
| Error("[HostStatus] - While loading HostStatus stats, there was an Error reading HostStatus stats."); |
| } |
| } |
| |
| void |
| HostStatus::loadRecord(std::string &name, HostStatRec &h) |
| { |
| HostStatRec *host_stat = nullptr; |
| Debug("host_statuses", "loading host status record for %s", name.c_str()); |
| ink_rwlock_wrlock(&host_status_rwlock); |
| { |
| if (auto it = hosts_statuses.find(name.c_str()); it != hosts_statuses.end()) { |
| host_stat = it->second; |
| } else { |
| host_stat = static_cast<HostStatRec *>(ats_malloc(sizeof(HostStatRec))); |
| *host_stat = h; |
| hosts_statuses.emplace(name, host_stat); |
| } |
| } |
| ink_rwlock_unlock(&host_status_rwlock); |
| |
| *host_stat = h; |
| } |
| |
| void |
| HostStatus::setHostStatus(const char *name, HostStatus_t status, const unsigned int down_time, const unsigned int reason) |
| { |
| std::string stat_name; |
| char buf[1024] = {0}; |
| |
| getStatName(stat_name, name); |
| |
| if (getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) { |
| createHostStat(name); |
| } |
| |
| int result = getHostStat(stat_name, buf, 1024); |
| |
| // update / insert status. |
| // using the hash table pointer to store the HostStatus_t value. |
| HostStatRec *host_stat = nullptr; |
| ink_rwlock_wrlock(&host_status_rwlock); |
| { |
| if (auto it = hosts_statuses.find(name); it != hosts_statuses.end()) { |
| host_stat = it->second; |
| } else { |
| host_stat = static_cast<HostStatRec *>(ats_malloc(sizeof(HostStatRec))); |
| bzero(host_stat, sizeof(HostStatRec)); |
| hosts_statuses.emplace(name, host_stat); |
| } |
| if (reason & Reason::ACTIVE) { |
| Debug("host_statuses", "for host %s set status: %s, Reason:ACTIVE", name, HostStatusNames[status]); |
| if (status == HostStatus_t::HOST_STATUS_DOWN) { |
| host_stat->active_marked_down = time(0); |
| host_stat->active_down_time = down_time; |
| host_stat->reasons |= Reason::ACTIVE; |
| } else { |
| host_stat->active_marked_down = 0; |
| host_stat->active_down_time = 0; |
| if (host_stat->reasons & Reason::ACTIVE) { |
| host_stat->reasons ^= Reason::ACTIVE; |
| } |
| } |
| } |
| if (reason & Reason::LOCAL) { |
| Debug("host_statuses", "for host %s set status: %s, Reason:LOCAL", name, HostStatusNames[status]); |
| if (status == HostStatus_t::HOST_STATUS_DOWN) { |
| host_stat->local_marked_down = time(0); |
| host_stat->local_down_time = down_time; |
| host_stat->reasons |= Reason::LOCAL; |
| } else { |
| host_stat->local_marked_down = 0; |
| host_stat->local_down_time = 0; |
| if (host_stat->reasons & Reason::LOCAL) { |
| host_stat->reasons ^= Reason::LOCAL; |
| } |
| } |
| } |
| if (reason & Reason::MANUAL) { |
| Debug("host_statuses", "for host %s set status: %s, Reason:MANUAL", name, HostStatusNames[status]); |
| if (status == HostStatus_t::HOST_STATUS_DOWN) { |
| host_stat->manual_marked_down = time(0); |
| host_stat->manual_down_time = down_time; |
| host_stat->reasons |= Reason::MANUAL; |
| } else { |
| host_stat->manual_marked_down = 0; |
| host_stat->manual_down_time = 0; |
| if (host_stat->reasons & Reason::MANUAL) { |
| host_stat->reasons ^= Reason::MANUAL; |
| } |
| } |
| } |
| if (reason & Reason::SELF_DETECT) { |
| Debug("host_statuses", "for host %s set status: %s, Reason:SELF_DETECT", name, HostStatusNames[status]); |
| if (status == HostStatus_t::HOST_STATUS_DOWN) { |
| host_stat->self_detect_marked_down = time(0); |
| host_stat->reasons |= Reason::SELF_DETECT; |
| } else { |
| host_stat->self_detect_marked_down = 0; |
| if (host_stat->reasons & Reason::SELF_DETECT) { |
| host_stat->reasons ^= Reason::SELF_DETECT; |
| } |
| } |
| } |
| if (status == HostStatus_t::HOST_STATUS_UP) { |
| if (host_stat->reasons == 0) { |
| host_stat->status = HostStatus_t::HOST_STATUS_UP; |
| } |
| Debug("host_statuses", "reasons: %d, status: %s", host_stat->reasons, HostStatusNames[host_stat->status]); |
| } else { |
| host_stat->status = status; |
| Debug("host_statuses", "reasons: %d, status: %s", host_stat->reasons, HostStatusNames[host_stat->status]); |
| } |
| } |
| ink_rwlock_unlock(&host_status_rwlock); |
| |
| // update the stats |
| if (result == REC_ERR_OKAY) { |
| std::stringstream status_rec; |
| status_rec << *host_stat; |
| RecSetRecordString(stat_name.c_str(), const_cast<char *>(status_rec.str().c_str()), REC_SOURCE_EXPLICIT, true, false); |
| if (status == HostStatus_t::HOST_STATUS_UP) { |
| Debug("host_statuses", "set status up for name: %s, status: %d, stat_name: %s", name, status, stat_name.c_str()); |
| } else { |
| Debug("host_statuses", "set status down for name: %s, status: %d, stat_name: %s", name, status, stat_name.c_str()); |
| } |
| } |
| Debug("host_statuses", "name: %s, status: %d", name, status); |
| |
| // log it. |
| if (status == HostStatus_t::HOST_STATUS_DOWN) { |
| Note("Host %s has been marked down, down_time: %d - %s.", name, down_time, down_time == 0 ? "indefinitely." : "seconds."); |
| } else { |
| Note("Host %s has been marked up.", name); |
| } |
| } |
| |
| HostStatRec * |
| HostStatus::getHostStatus(const char *name) |
| { |
| HostStatRec *_status = nullptr; |
| time_t now = time(0); |
| bool lookup = false; |
| |
| // the hash table value pointer has the HostStatus_t value. |
| ink_rwlock_rdlock(&host_status_rwlock); |
| { |
| auto it = hosts_statuses.find(name); |
| lookup = it != hosts_statuses.end(); |
| if (lookup) { |
| _status = it->second; |
| } |
| } |
| ink_rwlock_unlock(&host_status_rwlock); |
| |
| // if the host was marked down and it's down_time has elapsed, mark it up. |
| if (lookup && _status->status == HostStatus_t::HOST_STATUS_DOWN) { |
| unsigned int reasons = _status->reasons; |
| if ((_status->reasons & Reason::ACTIVE) && _status->active_down_time > 0) { |
| if ((_status->active_down_time + _status->active_marked_down) < now) { |
| Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now, |
| _status->active_down_time, _status->active_marked_down, Reason::ACTIVE_REASON); |
| setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::ACTIVE); |
| reasons ^= Reason::ACTIVE; |
| } |
| } |
| if ((_status->reasons & Reason::LOCAL) && _status->local_down_time > 0) { |
| if ((_status->local_down_time + _status->local_marked_down) < now) { |
| Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now, |
| _status->local_down_time, _status->local_marked_down, Reason::LOCAL_REASON); |
| setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::LOCAL); |
| reasons ^= Reason::LOCAL; |
| } |
| } |
| if ((_status->reasons & Reason::MANUAL) && _status->manual_down_time > 0) { |
| if ((_status->manual_down_time + _status->manual_marked_down) < now) { |
| Debug("host_statuses", "name: %s, now: %ld, down_time: %d, marked_down: %ld, reason: %s", name, now, |
| _status->manual_down_time, _status->manual_marked_down, Reason::MANUAL_REASON); |
| setHostStatus(name, HostStatus_t::HOST_STATUS_UP, 0, Reason::MANUAL); |
| reasons ^= Reason::MANUAL; |
| } |
| } |
| _status->reasons = reasons; |
| } |
| |
| return _status; |
| } |
| |
| void |
| HostStatus::createHostStat(const char *name, const char *data) |
| { |
| char buf[1024] = {0}; |
| HostStatRec r; |
| |
| std::string stat_name; |
| std::stringstream status_rec; |
| if (data != nullptr) { |
| HostStatRec h(data); |
| r = h; |
| } |
| status_rec << r; |
| getStatName(stat_name, name); |
| |
| if (getHostStat(stat_name, buf, 1024) == REC_ERR_FAIL) { |
| RecRegisterStatString(RECT_PROCESS, stat_name.c_str(), const_cast<char *>(status_rec.str().c_str()), RECP_PERSISTENT); |
| Debug("host_statuses", "stat name: %s, data: %s", stat_name.c_str(), status_rec.str().c_str()); |
| } |
| } |
| |
| int |
| HostStatus::getHostStat(std::string &stat_name, char *buf, unsigned int buf_len) |
| { |
| return RecGetRecordString(stat_name.c_str(), buf, buf_len, true); |
| } |