Files
oopt-tai/tools/taish/server/main.cpp
Wataru Ishida e3e3a0fc4c use -Wall -Werror for build
Signed-off-by: Wataru Ishida <wataru.ishid@gmail.com>
2022-02-12 23:43:18 +09:00

537 lines
16 KiB
C++

/**
* @file main.cpp
*
* @brief This module implements taish server
*
* @copyright Copyright (c) 2018 Nippon Telegraph and Telephone Corporation
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <thread>
#include <queue>
#include <mutex>
#include <iostream>
#include <sys/eventfd.h>
#include "unistd.h"
#include <cstdlib>
#include <sstream>
#include <fstream>
#include <cstdarg>
#include "json.hpp"
#include "taigrpc.hpp"
#include "taimetadata.h"
#include "logger.hpp"
#include "attribute.hpp"
using grpc::ServerBuilder;
using grpc::ServerContext;
using json = nlohmann::json;
static const std::string TAI_RPC_DEFAULT_IP = "0.0.0.0";
static const uint16_t TAI_RPC_DEFAULT_PORT = 50051;
tai_api_method_table_t g_api;
int event_fd;
std::queue<std::pair<bool, std::string>> q;
std::mutex m;
class module;
std::map<std::string, module*> g_modules;
static int load_config(const json& config, std::vector<tai::S_Attribute>& list, tai_object_type_t t, const std::string& l) {
int32_t attr_id;
tai_serialize_option_t option{true, true, true};
if ( !config.is_object() ) {
return -1;
}
if ( config.find("attrs") == config.end() ) {
return 0;
}
tai_char_list_t location{.count = static_cast<uint32_t>(l.size()), .list = const_cast<char*>(l.c_str())};
tai_metadata_key_t key{.type = t, .location = location};
for ( auto& a: config["attrs"].items() ) {
int ret;
option.json = false;
if ( g_api.meta_api != nullptr && g_api.meta_api->get_object_info != nullptr ) {
auto info = g_api.meta_api->get_object_info(&key);
if ( info == nullptr ) {
throw std::runtime_error("failed to get object info");
}
ret = tai_deserialize_enum(a.key().c_str(), info->enummetadata, &attr_id, &option);
} else {
switch ( t ) {
case TAI_OBJECT_TYPE_MODULE:
ret = tai_deserialize_module_attr(a.key().c_str(), &attr_id, &option);
break;
case TAI_OBJECT_TYPE_HOSTIF:
ret = tai_deserialize_host_interface_attr(a.key().c_str(), &attr_id, &option);
break;
case TAI_OBJECT_TYPE_NETWORKIF:
ret = tai_deserialize_network_interface_attr(a.key().c_str(), &attr_id, &option);
break;
default:
throw std::runtime_error("unsupported object type");
}
}
if ( ret < 0 ) {
std::stringstream ss;
ss << "failed to deserialize attribute name: " << a.key();
throw std::runtime_error(ss.str());
}
auto meta = get_metadata(g_api.meta_api, &key, attr_id);
if ( meta == nullptr ) {
std::stringstream ss;
ss << "failed to get metadata for " << a.key();
throw std::runtime_error(ss.str());
}
auto value = a.value().dump();
option.json = true;
list.push_back(std::make_shared<tai::Attribute>(meta, value, &option));
}
return 0;
}
class module {
public:
module(std::string location, const json& config, bool auto_creation) : m_id(0), m_location(location) {
std::vector<tai::S_Attribute> list;
std::vector<tai_attribute_t> raw_list;
tai_attribute_t attr;
if (!auto_creation) {
return;
}
attr.id = TAI_MODULE_ATTR_LOCATION;
attr.value.charlist.count = location.size();
attr.value.charlist.list = (char*)location.c_str();
auto meta = tai_metadata_get_attr_metadata(TAI_OBJECT_TYPE_MODULE, attr.id);
if ( meta == nullptr ) {
throw std::runtime_error("failed to get metadata for location attribute");
}
list.push_back(std::make_shared<tai::Attribute>(meta, &attr));
load_config(config, list, TAI_OBJECT_TYPE_MODULE, location);
for ( auto& a : list ) {
raw_list.push_back(*a->raw());
}
auto status = g_api.module_api->create_module(&m_id, raw_list.size(), raw_list.data());
if ( status != TAI_STATUS_SUCCESS ) {
std::cout << "failed to create module whose location is " << location << ", err: " << status << std::endl;
return;
}
std::cout << "created module id: 0x" << std::hex << m_id << std::endl;
raw_list.clear();
attr.id = TAI_MODULE_ATTR_NUM_HOST_INTERFACES;
raw_list.push_back(attr);
attr.id = TAI_MODULE_ATTR_NUM_NETWORK_INTERFACES;
raw_list.push_back(attr);
status = g_api.module_api->get_module_attributes(m_id, raw_list.size(), raw_list.data());
if ( status != TAI_STATUS_SUCCESS ) {
throw std::runtime_error("faile to get attribute");
}
std::cout << "num hostif: " << raw_list[0].value.u32 << std::endl;
std::cout << "num netif: " << raw_list[1].value.u32 << std::endl;
create_hostif(raw_list[0].value.u32, config, location);
create_netif(raw_list[1].value.u32, config, location);
}
const std::string& location() {
return m_location;
}
tai_object_id_t id() {
return m_id;
}
void set_id(tai_object_id_t id) {
m_id = id;
return;
}
std::map<int, tai_object_id_t> netifs;
std::map<int, tai_object_id_t> hostifs;
bool present;
private:
tai_object_id_t m_id;
std::string m_location;
int create_hostif(uint32_t num, const json& config, const std::string& location);
int create_netif(uint32_t num, const json& config, const std::string& location);
};
void module_presence(bool present, char* location) {
uint64_t v = 1;
std::lock_guard<std::mutex> g(m);
q.push(std::pair<bool, std::string>(present, std::string(location)));
v = write(event_fd, &v, sizeof(uint64_t));
}
int module::create_hostif(uint32_t num, const json& config, const std::string& location) {
auto c = config.find("hostif");
for ( uint32_t i = 0; i < num; i++ ) {
tai_object_id_t id;
std::vector<tai::S_Attribute> list;
std::vector<tai_attribute_t> raw_list;
tai_attribute_t attr;
attr.id = TAI_HOST_INTERFACE_ATTR_INDEX;
attr.value.u32 = i;
auto meta = tai_metadata_get_attr_metadata(TAI_OBJECT_TYPE_HOSTIF, attr.id);
if ( meta == nullptr ) {
throw std::runtime_error("failed to get metadata for index attribute");
}
list.push_back(std::make_shared<tai::Attribute>(meta, &attr));
if ( c != config.end() && c->is_object() ) {
std::stringstream ss;
ss << i;
auto cc = c->find(ss.str());
if ( cc != c->end() ) {
load_config(*cc, list, TAI_OBJECT_TYPE_HOSTIF, location);
}
}
for ( auto& a : list ) {
raw_list.push_back(*a->raw());
}
auto status = g_api.hostif_api->create_host_interface(&id, m_id, raw_list.size(), raw_list.data());
if ( status != TAI_STATUS_SUCCESS ) {
throw std::runtime_error("failed to create host interface");
}
std::cout << "hostif: 0x" << std::hex << id << std::endl;
hostifs[i] = id;
}
return 0;
}
int module::create_netif(uint32_t num, const json& config, const std::string& location) {
auto c = config.find("netif");
for ( uint32_t i = 0; i < num; i++ ) {
tai_object_id_t id;
std::vector<tai::S_Attribute> list;
std::vector<tai_attribute_t> raw_list;
tai_attribute_t attr;
attr.id = TAI_NETWORK_INTERFACE_ATTR_INDEX;
attr.value.u32 = i;
auto meta = tai_metadata_get_attr_metadata(TAI_OBJECT_TYPE_NETWORKIF, attr.id);
if ( meta == nullptr ) {
throw std::runtime_error("failed to get metadata for index attribute");
}
list.push_back(std::make_shared<tai::Attribute>(meta, &attr));
if ( c != config.end() && c->is_object() ) {
std::stringstream ss;
ss << i;
auto cc = c->find(ss.str());
if ( cc != c->end() ) {
load_config(*cc, list, TAI_OBJECT_TYPE_NETWORKIF, location);
}
}
for ( auto& a : list ) {
raw_list.push_back(*a->raw());
}
auto status = g_api.netif_api->create_network_interface(&id, m_id, raw_list.size(), raw_list.data());
if ( status != TAI_STATUS_SUCCESS ) {
throw std::runtime_error("failed to create network interface");
}
std::cout << "netif: 0x" << std::hex << id << std::endl;
netifs[i] = id;
}
return 0;
}
void grpc_thread(std::string addr) {
TAIServiceImpl service(&g_api);
ServerBuilder builder;
builder.AddListeningPort(grpc::string(addr), grpc::InsecureServerCredentials());
builder.RegisterService(&service);
auto server = builder.BuildAndStart();
std::cout << "Server listening on " << addr << std::endl;
server->Wait();
}
int start_grpc_server(std::string addr) {
std::thread th(grpc_thread, addr);
th.detach();
return 0;
}
// when type == module or is_create == false, index value is meaningless
void object_update(tai_object_type_t type, tai_object_id_t oid, int index, bool is_create) {
if ( type == TAI_OBJECT_TYPE_MODULE ) {
if ( is_create ) {
tai_attribute_t attr;
char l[32];
attr.id = TAI_MODULE_ATTR_LOCATION;
attr.value.charlist.list = l;
attr.value.charlist.count = 32;
if ( g_api.module_api->get_module_attribute(oid, &attr) != TAI_STATUS_SUCCESS ) {
return;
}
std::string loc(l, attr.value.charlist.count);
auto m = g_modules[loc];
if ( m == nullptr ) {
return;
}
m->set_id(oid);
} else {
for ( auto& m : g_modules ) {
if ( m.second->id() == oid ) {
m.second->set_id(TAI_NULL_OBJECT_ID);
break;
}
}
}
return;
}
auto mid = tai_module_id_query(oid);
std::map<int, tai_object_id_t> *v;
for (auto& m : g_modules) {
if (is_create && m.second->id() != mid) {
continue;
}
if (type == TAI_OBJECT_TYPE_HOSTIF) {
v = &m.second->hostifs;
} else if (type == TAI_OBJECT_TYPE_NETWORKIF) {
v = &m.second->netifs;
}
if (is_create) {
v->emplace(index, oid);
break;
} else {
index = -1;
for (auto it = v->begin(); it != v->end(); it++) {
if (it->second == oid) {
index = it->first;
break;
}
}
TAI_INFO("removing object: %lx, index: %d", oid, index);
if (index >= 0) {
v->erase(index);
break;
}
}
}
}
tai_status_t list_module(std::vector<tai_api_module_t>& l) {
std::lock_guard<std::mutex> g(m);
for ( auto v : g_modules ) {
tai_api_module_t list;
list.location = v.first;
auto m = v.second;
list.present = m->present;
list.id = m->id();
list.hostifs = m->hostifs;
list.netifs = m->netifs;
l.emplace_back(list);
}
return TAI_STATUS_SUCCESS;
}
static const std::string to_string(tai_log_level_t level) {
switch (level) {
case TAI_LOG_LEVEL_DEBUG:
return "DEBUG";
case TAI_LOG_LEVEL_INFO:
return "INFO";
case TAI_LOG_LEVEL_NOTICE:
return "NOTICE";
case TAI_LOG_LEVEL_WARN:
return "WARN";
case TAI_LOG_LEVEL_ERROR:
return "ERROR";
case TAI_LOG_LEVEL_CRITICAL:
return "CRITICAL";
default:
return std::to_string(static_cast<int>(level));
}
}
static void log_cb(tai_log_level_t lvl, const char *file, int line, const char *function, const char *format, ...) {
std::cout << to_string(lvl) << " [" << function << "@" << std::dec << line << "] ";
std::va_list va;
va_start(va, format);
std::vprintf(format, va);
va_end(va);
std::cout << std::endl;
}
void signal_handler(int sig) {
uint64_t v = 1;
std::lock_guard<std::mutex> g(m);
q.push(std::pair<bool, std::string>(false, std::string("shutdown")));
v = write(event_fd, &v, sizeof(uint64_t));
}
int main(int argc, char *argv[]) {
auto ip = TAI_RPC_DEFAULT_IP;
auto port = TAI_RPC_DEFAULT_PORT;
std::string config_file;
int c, ret = -1;
tai_log_level_t level = TAI_LOG_LEVEL_INFO;
auto auto_creation = true;
json config;
std::stringstream ss;
if ( signal(SIGINT, signal_handler) == SIG_ERR ) {
std::cerr << "failed to register signal handler" << std::endl;
return 1;
}
if ( signal(SIGTERM, signal_handler) == SIG_ERR ) {
std::cerr << "failed to register signal handler" << std::endl;
return 1;
}
while ((c = getopt (argc, argv, "i:p:f:vn")) != -1) {
switch (c) {
case 'i':
ip = std::string(optarg);
break;
case 'p':
port = atoi(optarg);
break;
case 'f':
config_file = std::string(optarg);
break;
case 'v':
level = TAI_LOG_LEVEL_DEBUG;
break;
case 'n':
auto_creation = false;
break;
default:
std::cerr << "usage: " << argv[0] << "-i <IP address> -p <Port number> -f <Config file> -v -n" << std::endl;
return 1;
}
}
tai_service_method_table_t services = {0};
services.module_presence = module_presence;
event_fd = eventfd(0, 0);
auto status = tai_api_initialize(0, &services);
if ( status != TAI_STATUS_SUCCESS ) {
std::cout << "failed to initialize" << std::endl;
goto exit;
}
status = tai_log_set(TAI_API_UNSPECIFIED, level, log_cb);
if ( status != TAI_STATUS_SUCCESS ) {
std::cout << "failed to set log level" << std::endl;
goto exit;
}
status = tai_api_query(TAI_API_MODULE, (void **)(&g_api.module_api));
if ( status != TAI_STATUS_SUCCESS ) {
std::cout << "failed to query MODULE API" << std::endl;
goto exit;
}
status = tai_api_query(TAI_API_NETWORKIF, (void **)(&g_api.netif_api));
if ( status != TAI_STATUS_SUCCESS ) {
std::cout << "failed to query NETWORKIF API" << std::endl;
goto exit;
}
status = tai_api_query(TAI_API_HOSTIF, (void **)(&g_api.hostif_api));
if ( status != TAI_STATUS_SUCCESS ) {
std::cout << "failed to query HOSTIF API" << std::endl;
goto exit;
}
status = tai_api_query(TAI_API_META, (void **)(&g_api.meta_api));
if ( status != TAI_STATUS_SUCCESS ) {
std::cout << "failed to query META API: " << status << std::endl;
}
g_api.list_module = list_module;
g_api.object_update = object_update;
ss << ip << ":" << port;
start_grpc_server(ss.str());
if ( config_file != "" ) {
std::ifstream ifs(config_file);
if ( !ifs ) {
std::cout << "failed to open config file: " << config_file << std::endl;
goto exit;
}
std::istreambuf_iterator<char> it(ifs), last;
std::string c(it, last);
config = json::parse(c);
if ( !config.is_object() ) {
std::cout << "invalid configuration. config is not object" << std::endl;
goto exit;
}
}
while (true) {
uint64_t v;
v = read(event_fd, &v, sizeof(uint64_t));
{
std::lock_guard<std::mutex> g(m);
while ( !q.empty() ) {
auto p = q.front();
auto loc = p.second;
if ( loc == "shutdown" ) {
ret = 0;
goto exit;
}
std::cout << "present: " << p.first << ", loc: " << loc << std::endl;
if ( g_modules.find(loc) == g_modules.end() ) {
auto m = new module(loc, config[loc], auto_creation);
g_modules[loc] = m;
}
g_modules[loc]->present = p.first;
q.pop();
}
}
}
exit:
tai_api_uninitialize();
return ret;
}