From fa184dfd795b03a121035d1435edc727ec4dcd99 Mon Sep 17 00:00:00 2001 From: generatedunixname89002005232357 Date: Tue, 22 Oct 2024 11:07:55 -0700 Subject: [PATCH] Revert D63971252 Summary: This diff reverts D63971252 removed by mistake, used in github Reviewed By: avasylev Differential Revision: D64762130 fbshipit-source-id: c9c78cf9f772cebae5f910ae6b9ffae2969c33ee --- example_grpc/GrpcSignalHandler.cpp | 52 ++++ example_grpc/GrpcSignalHandler.h | 57 ++++ example_grpc/KatranGrpcService.cpp | 445 +++++++++++++++++++++++++++++ example_grpc/KatranGrpcService.h | 151 ++++++++++ example_grpc/katran_server.cpp | 141 +++++++++ 5 files changed, 846 insertions(+) create mode 100644 example_grpc/GrpcSignalHandler.cpp create mode 100644 example_grpc/GrpcSignalHandler.h create mode 100644 example_grpc/KatranGrpcService.cpp create mode 100644 example_grpc/KatranGrpcService.h create mode 100644 example_grpc/katran_server.cpp diff --git a/example_grpc/GrpcSignalHandler.cpp b/example_grpc/GrpcSignalHandler.cpp new file mode 100644 index 000000000..cca3f09d7 --- /dev/null +++ b/example_grpc/GrpcSignalHandler.cpp @@ -0,0 +1,52 @@ +/* Copyright (C) 2018-present, Facebook, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "GrpcSignalHandler.h" + +#include + +namespace lb { +namespace katran { + +GrpcSignalHandler::GrpcSignalHandler( + std::shared_ptr evb, + grpc::Server* server, + int32_t delay) + : folly::AsyncSignalHandler(evb.get()), delay_(delay) { + server_ = server; + evb_ = evb; +}; + +void GrpcSignalHandler::signalReceived(int signum) noexcept { + if (shutdownScheduled_) { + LOG(INFO) << "Ignoring signal: " << signum << " as we already scheduled" + << " sighandler to run."; + return; + }; + LOG(INFO) << "Signal: " << signum << ", stopping service in " << delay_ + << " milliseconds."; + evb_->runInEventBaseThread([this]() { + evb_->runAfterDelay( + [this]() { + LOG(INFO) << "Stopping Katran!"; + server_->Shutdown(); + }, + delay_); + }); + shutdownScheduled_ = true; +}; +} // namespace katran +} // namespace lb diff --git a/example_grpc/GrpcSignalHandler.h b/example_grpc/GrpcSignalHandler.h new file mode 100644 index 000000000..9e24e62ff --- /dev/null +++ b/example_grpc/GrpcSignalHandler.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2018-present, Facebook, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#include + +#include +#include + +namespace folly { +class EventBase; +} + +namespace lb { +namespace katran { + +/** + * class which implements sighandler for katran's grpc server + */ +class GrpcSignalHandler : public folly::AsyncSignalHandler { + public: + /** + * @param EventBase* evb event base thread + * @param grpc::Server* server katran's grpc server + * @param int32_t delay in ms between recving signal and stopping katran + */ + GrpcSignalHandler( + std::shared_ptr evb, + grpc::Server* server, + int32_t delay); + ~GrpcSignalHandler() override {} + + void signalReceived(int signum) noexcept override; + + private: + grpc::Server* server_; + std::shared_ptr evb_; + int32_t delay_; + bool shutdownScheduled_{false}; +}; + +} // namespace katran +} // namespace lb diff --git a/example_grpc/KatranGrpcService.cpp b/example_grpc/KatranGrpcService.cpp new file mode 100644 index 000000000..a17cbdc56 --- /dev/null +++ b/example_grpc/KatranGrpcService.cpp @@ -0,0 +1,445 @@ +/* Copyright (C) 2018-present, Facebook, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "KatranGrpcService.h" + +#include + +#include "katran/lib/MacHelpers.h" + +using grpc::Server; +using grpc::ServerContext; +using grpc::Status; + +namespace lb { +namespace katran { + +using Guard = std::lock_guard; + +// translation helpers + +::katran::VipKey translateVipObject(const Vip& vip) { + ::katran::VipKey vk; + vk.address = vip.address(); + vk.port = vip.port(); + vk.proto = vip.protocol(); + return vk; +} + +::katran::NewReal translateRealObject(const Real& real) { + ::katran::NewReal nr; + nr.address = real.address(); + nr.weight = real.weight(); + nr.flags = real.flags(); + return nr; +} + +::katran::QuicReal translateQuicRealObject(const QuicReal& real) { + ::katran::QuicReal qr; + qr.address = real.address(); + qr.id = real.id(); + return qr; +} + +Status returnStatus(bool result) { + if (result) { + return Status::OK; + } else { + return Status::CANCELLED; + } +} + +KatranGrpcService::KatranGrpcService(const ::katran::KatranConfig& config) + : lb_(config, + std::make_unique<::katran::BpfAdapter>(config.memlockUnlimited)), + hcForwarding_(config.enableHc) { + LOG(INFO) << "Starting Katran"; + lb_.loadBpfProgs(); + lb_.attachBpfProgs(); +} + +Status KatranGrpcService::changeMac( + ServerContext* context, + const Mac* request, + Bool* response) { + Guard lock(giant_); + auto mac = ::katran::convertMacToUint(request->mac()); + auto res = lb_.changeMac(mac); + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::getMac( + ServerContext* context, + const Empty* request, + Mac* response) { + Guard lock(giant_); + auto mac = lb_.getMac(); + response->set_mac(::katran::convertMacToString(mac)); + return Status::OK; +} + +Status KatranGrpcService::addVip( + ServerContext* context, + const VipMeta* request, + Bool* response) { + bool res; + auto vk = translateVipObject(request->vip()); + + try { + Guard lock(giant_); + res = lb_.addVip(vk, request->flags()); + } catch (const std::exception& e) { + LOG(INFO) << "Exception while adding new vip: " << e.what(); + res = false; + } + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::delVip( + ServerContext* context, + const Vip* request, + Bool* response) { + auto vk = translateVipObject(*request); + Guard lock(giant_); + auto res = lb_.delVip(vk); + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::getAllVips( + ServerContext* context, + const Empty* request, + Vips* response) { + Vip vip; + Guard lock(giant_); + auto vips = lb_.getAllVips(); + for (auto& v : vips) { + vip.set_address(v.address); + vip.set_port(v.port); + vip.set_protocol(v.proto); + auto rvip = response->add_vips(); + *rvip = vip; + } + + return Status::OK; +} + +Status KatranGrpcService::modifyVip( + ServerContext* context, + const VipMeta* request, + Bool* response) { + auto vk = translateVipObject(request->vip()); + Guard lock(giant_); + auto res = lb_.modifyVip(vk, request->flags(), request->setflag()); + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::modifyReal( + ServerContext* context, + const RealMeta* request, + Bool* response) { + Guard lock(giant_); + auto res = + lb_.modifyReal(request->address(), request->flags(), request->setflag()); + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::getVipFlags( + ServerContext* context, + const Vip* request, + Flags* response) { + int64_t flags = -1; + auto vk = translateVipObject(*request); + + try { + Guard lock(giant_); + flags = lb_.getVipFlags(vk); + } catch (const std::exception& e) { + LOG(INFO) << "Exception while getting flags for vip" << e.what(); + } + response->set_flags(flags); + return Status::OK; +} + +Status KatranGrpcService::addRealForVip( + ServerContext* context, + const realForVip* request, + Bool* response) { + bool res; + auto vk = translateVipObject(request->vip()); + auto nr = translateRealObject(request->real()); + try { + Guard lock(giant_); + res = lb_.addRealForVip(nr, vk); + } catch (const std::exception&) { + res = false; + } + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::delRealForVip( + ServerContext* context, + const realForVip* request, + Bool* response) { + auto vk = translateVipObject(request->vip()); + auto nr = translateRealObject(request->real()); + Guard lock(giant_); + auto res = lb_.delRealForVip(nr, vk); + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::modifyRealsForVip( + ServerContext* context, + const modifiedRealsForVip* request, + Bool* response) { + ::katran::ModifyAction a; + std::vector<::katran::NewReal> nreals; + bool res; + + switch (request->action()) { + case Action::ADD: + a = ::katran::ModifyAction::ADD; + break; + case Action::DEL: + a = ::katran::ModifyAction::DEL; + break; + default: + break; + } + + auto vk = translateVipObject(request->vip()); + for (int i = 0; i < request->real().reals_size(); i++) { + auto nr = translateRealObject(request->real().reals(i)); + nreals.push_back(nr); + } + + try { + Guard lock(giant_); + res = lb_.modifyRealsForVip(a, nreals, vk); + } catch (const std::exception& e) { + LOG(INFO) << "Exception while modifying vip: " << e.what(); + res = false; + } + + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::getRealsForVip( + ServerContext* context, + const Vip* request, + Reals* response) { + // + Real r; + std::vector<::katran::NewReal> reals; + auto vk = translateVipObject(*request); + try { + Guard lock(giant_); + reals = lb_.getRealsForVip(vk); + } catch (const std::exception& e) { + LOG(INFO) << "Exception while getting reals from vip: " << e.what(); + return Status::CANCELLED; + } + for (auto& real : reals) { + r.set_address(real.address); + r.set_weight(real.weight); + r.set_flags(real.flags); + auto rr = response->add_reals(); + *rr = r; + } + return Status::OK; +} + +Status KatranGrpcService::modifyQuicRealsMapping( + ServerContext* context, + const modifiedQuicReals* request, + Bool* response) { + ::katran::ModifyAction a; + std::vector<::katran::QuicReal> qreals; + bool res{true}; + switch (request->action()) { + case Action::ADD: + a = ::katran::ModifyAction::ADD; + break; + case Action::DEL: + a = ::katran::ModifyAction::DEL; + break; + default: + break; + } + for (int i = 0; i < request->reals().qreals_size(); i++) { + auto qr = translateQuicRealObject(request->reals().qreals(i)); + qreals.push_back(qr); + } + try { + Guard lock(giant_); + lb_.modifyQuicRealsMapping(a, qreals); + } catch (const std::exception& e) { + LOG(INFO) << "Exception while modifying quic real: " << e.what(); + res = false; + } + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::getQuicRealsMapping( + ServerContext* context, + const Empty* request, + QuicReals* response) { + QuicReal qr; + std::vector<::katran::QuicReal> qreals; + try { + Guard lock(giant_); + qreals = lb_.getQuicRealsMapping(); + } catch (const std::exception& e) { + LOG(INFO) << "Exception while getting reals from vip: " << e.what(); + return Status::CANCELLED; + } + for (auto& real : qreals) { + qr.set_address(real.address); + qr.set_id(real.id); + auto rqr = response->add_qreals(); + *rqr = qr; + } + + return Status::OK; +} + +Status KatranGrpcService::getStatsForVip( + ServerContext* context, + const Vip* request, + Stats* response) { + auto vk = translateVipObject(*request); + Guard lock(giant_); + auto stats = lb_.getStatsForVip(vk); + + response->set_v1(stats.v1); + response->set_v2(stats.v2); + + return Status::OK; +} + +Status KatranGrpcService::getLruStats( + ServerContext* context, + const Empty* request, + Stats* response) { + Guard lock(giant_); + auto stats = lb_.getLruStats(); + + response->set_v1(stats.v1); + response->set_v2(stats.v2); + + return Status::OK; +} + +Status KatranGrpcService::getLruMissStats( + ServerContext* context, + const Empty* request, + Stats* response) { + Guard lock(giant_); + auto stats = lb_.getLruMissStats(); + + response->set_v1(stats.v1); + response->set_v2(stats.v2); + + return Status::OK; +} + +Status KatranGrpcService::getLruFallbackStats( + ServerContext* context, + const Empty* request, + Stats* response) { + Guard lock(giant_); + auto stats = lb_.getLruFallbackStats(); + + response->set_v1(stats.v1); + response->set_v2(stats.v2); + + return Status::OK; +} + +Status KatranGrpcService::getIcmpTooBigStats( + ServerContext* context, + const Empty* request, + Stats* response) { + Guard lock(giant_); + auto stats = lb_.getIcmpTooBigStats(); + + response->set_v1(stats.v1); + response->set_v2(stats.v2); + + return Status::OK; +} + +Status KatranGrpcService::addHealthcheckerDst( + ServerContext* context, + const Healthcheck* request, + Bool* response) { + if (!hcForwarding_) { + response->set_success(false); + return Status::CANCELLED; + } + bool res; + try { + Guard lock(giant_); + res = lb_.addHealthcheckerDst(request->somark(), request->address()); + } catch (const std::exception& e) { + LOG(INFO) << "Exception while adding healthcheck: " << e.what(); + res = false; + } + + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::delHealthcheckerDst( + ServerContext* context, + const Somark* request, + Bool* response) { + if (!hcForwarding_) { + response->set_success(false); + return Status::CANCELLED; + } + Guard lock(giant_); + auto res = lb_.delHealthcheckerDst(request->somark()); + response->set_success(res); + return returnStatus(res); +} + +Status KatranGrpcService::getHealthcheckersDst( + ServerContext* context, + const Empty* request, + hcMap* response) { + if (!hcForwarding_) { + return Status::CANCELLED; + } + Guard lock(giant_); + auto hcs = lb_.getHealthcheckersDst(); + auto rhcs = response->mutable_healthchecks(); + for (auto& hc : hcs) { + (*rhcs)[hc.first] = hc.second; + } + return Status::OK; +} + +} // namespace katran +} // namespace lb diff --git a/example_grpc/KatranGrpcService.h b/example_grpc/KatranGrpcService.h new file mode 100644 index 000000000..a011ce9cc --- /dev/null +++ b/example_grpc/KatranGrpcService.h @@ -0,0 +1,151 @@ +/* Copyright (C) 2018-present, Facebook, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once +#include +#include +#include +#include + +#include +#include "katran.grpc.pb.h" +#include "katran/lib/KatranLb.h" + +using grpc::Server; +using grpc::ServerContext; +using grpc::Status; + +namespace lb { +namespace katran { + +class KatranGrpcService final : public KatranService::Service { + public: + KatranGrpcService() = delete; + + explicit KatranGrpcService(const ::katran::KatranConfig& config); + + Status changeMac(ServerContext* context, const Mac* request, Bool* response) + override; + + Status getMac(ServerContext* context, const Empty* request, Mac* response) + override; + + Status addVip(ServerContext* context, const VipMeta* request, Bool* response) + override; + + Status delVip(ServerContext* context, const Vip* request, Bool* response) + override; + + Status getAllVips( + ServerContext* context, + const Empty* request, + Vips* response) override; + + Status modifyVip( + ServerContext* context, + const VipMeta* request, + Bool* response) override; + + Status modifyReal( + ServerContext* context, + const RealMeta* request, + Bool* response) override; + + Status getVipFlags( + ServerContext* context, + const Vip* request, + Flags* response) override; + + Status addRealForVip( + ServerContext* context, + const realForVip* request, + Bool* response) override; + + Status delRealForVip( + ServerContext* context, + const realForVip* request, + Bool* response) override; + + Status modifyRealsForVip( + ServerContext* context, + const modifiedRealsForVip* request, + Bool* response) override; + + Status getRealsForVip( + ServerContext* context, + const Vip* request, + Reals* response) override; + + Status modifyQuicRealsMapping( + ServerContext* context, + const modifiedQuicReals* request, + Bool* response) override; + + Status getQuicRealsMapping( + ServerContext* context, + const Empty* request, + QuicReals* response) override; + + Status getStatsForVip( + ServerContext* context, + const Vip* request, + Stats* response) override; + + Status getLruStats( + ServerContext* context, + const Empty* request, + Stats* response) override; + + Status getLruMissStats( + ServerContext* context, + const Empty* request, + Stats* response) override; + + Status getLruFallbackStats( + ServerContext* context, + const Empty* request, + Stats* response) override; + + Status getIcmpTooBigStats( + ServerContext* context, + const Empty* request, + Stats* response) override; + + Status addHealthcheckerDst( + ServerContext* context, + const Healthcheck* request, + Bool* response) override; + + Status delHealthcheckerDst( + ServerContext* context, + const Somark* request, + Bool* response) override; + + Status getHealthcheckersDst( + ServerContext* context, + const Empty* request, + hcMap* response) override; + + private: + ::katran::KatranLb lb_; + + std::mutex giant_; + + bool hcForwarding_; +}; + +} // namespace katran +} // namespace lb diff --git a/example_grpc/katran_server.cpp b/example_grpc/katran_server.cpp new file mode 100644 index 000000000..8077a4de0 --- /dev/null +++ b/example_grpc/katran_server.cpp @@ -0,0 +1,141 @@ +/* Copyright (C) 2018-present, Facebook, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "GrpcSignalHandler.h" +#include "KatranGrpcService.h" +#include "katran/lib/MacHelpers.h" + +using grpc::Server; +using grpc::ServerBuilder; + +DEFINE_string(server, "0.0.0.0:50051", "Service server:port"); +DEFINE_string(intf, "eth0", "main interface"); +DEFINE_string(hc_intf, "", "interface for healthchecking"); +DEFINE_string(ipip_intf, "ipip0", "ipip (v4) encap interface"); +DEFINE_string(ipip6_intf, "ipip60", "ip(6)ip6 (v6) encap interface"); +DEFINE_string(balancer_prog, "./balancer.bpf.o", "path to balancer bpf prog"); +DEFINE_string( + healthchecker_prog, + "./healthchecking_ipip.o", + "path to healthchecking bpf prog"); +DEFINE_string( + default_mac, + "00:00:00:00:00:01", + "mac address of default router. must be in fomrat: xx:xx:xx:xx:xx:xx"); +DEFINE_int32(priority, 2307, "tc's priority for bpf progs"); +DEFINE_string( + map_path, + "", + "path to pinned map from root xdp prog." + " default path forces to work in standalone mode"); +DEFINE_int32(prog_pos, 2, "katran's position inside root xdp array"); +DEFINE_bool(hc_forwarding, true, "turn on forwarding path for healthchecks"); +DEFINE_int32(shutdown_delay, 10000, "shutdown delay in milliseconds"); +DEFINE_int64(lru_size, 8000000, "size of LRU table"); +DEFINE_string(forwarding_cores, "", "comma separed list of forwarding cores"); +DEFINE_string( + numa_nodes, + "", + "comma separed list of numa nodes to forwarding cores mapping"); + +// routine which parses comma separated string of numbers +// (e.g. "1,2,3,4,10,11,12,13") to vector of int32_t +// will throw on failure. +std::vector parseIntLine(const std::string& line) { + std::vector nums; + if (!line.empty()) { + std::vector splitedLine; + folly::split(",", line, splitedLine); + for (const auto& num_str : splitedLine) { + auto num = folly::to(num_str); + nums.push_back(num); + } + } + return nums; +} + +void RunServer( + katran::KatranConfig& config, + int32_t delay, + std::shared_ptr evb) { + std::string server_address(FLAGS_server); + lb::katran::KatranGrpcService service(config); + + ServerBuilder builder; + // Listen on the given address without any authentication mechanism. + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); + // Register "service" as the instance through which we'll communicate with + // clients. In this case it corresponds to an *synchronous* service. + builder.RegisterService(&service); + // Finally assemble the server. + std::unique_ptr server(builder.BuildAndStart()); + std::cout << "Server listening on " << server_address << std::endl; + lb::katran::GrpcSignalHandler grpcSigHandler(evb, server.get(), delay); + grpcSigHandler.registerSignalHandler(SIGINT); + grpcSigHandler.registerSignalHandler(SIGTERM); + // Wait for the server to shutdown. Note that some other thread must be + // responsible for shutting down the server for this call to ever return. + server->Wait(); +} + +int main(int argc, char** argv) { + folly::init(&argc, &argv); + FLAGS_logtostderr = 1; + + auto forwardingCores = parseIntLine(FLAGS_forwarding_cores); + VLOG(2) << "size of forwarding cores vector is " << forwardingCores.size(); + auto numaNodes = parseIntLine(FLAGS_numa_nodes); + VLOG(2) << "size of numa nodes vector is " << numaNodes.size(); + + katran::KatranConfig config = { + .mainInterface = FLAGS_intf, + .v4TunInterface = FLAGS_ipip_intf, + .v6TunInterface = FLAGS_ipip6_intf, + .balancerProgPath = FLAGS_balancer_prog, + .healthcheckingProgPath = FLAGS_healthchecker_prog, + .defaultMac = katran::convertMacToUint(FLAGS_default_mac), + .priority = static_cast(FLAGS_priority), + .rootMapPath = FLAGS_map_path, + .rootMapPos = static_cast(FLAGS_prog_pos), + .enableHc = FLAGS_hc_forwarding, + }; + config.LruSize = static_cast(FLAGS_lru_size); + config.forwardingCores = forwardingCores; + config.numaNodes = numaNodes; + config.hcInterface = FLAGS_hc_intf; + config.hashFunction = katran::HashFunction::MaglevV2; + + auto evb = std::make_shared(); + std::thread t1([evb]() { evb->loopForever(); }); + t1.detach(); + + RunServer(config, FLAGS_shutdown_delay, evb); + return 0; +}