mirror of
https://github.com/Telecominfraproject/oopt-tai.git
synced 2026-01-02 19:55:04 +00:00
537 lines
16 KiB
C++
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;
|
|
}
|