/** * @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 #include #include #include #include #include "unistd.h" #include #include #include #include #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> q; std::mutex m; class module; std::map g_modules; static int load_config(const json& config, std::vector& 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(l.size()), .list = const_cast(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(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 list; std::vector 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(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 netifs; std::map 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 g(m); q.push(std::pair(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 list; std::vector 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(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 list; std::vector 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(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 *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& l) { std::lock_guard 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(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 g(m); q.push(std::pair(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 -p -f -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 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 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; }