Files
oopt-tai/tools/taish/lib/server.cpp
Wataru Ishida b70397c331 taish: add is_enum field in metadata
Signed-off-by: Wataru Ishida <wataru.ishid@gmail.com>
2020-06-26 21:57:31 -07:00

558 lines
19 KiB
C++

/**
* @file server.cpp
*
* @brief This module implements TAI gRPC 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 "taigrpc.hpp"
#include "taimetadata.h"
#include <sstream>
#include <chrono>
#include <functional>
#include "attribute.hpp"
using grpc::Status;
using grpc::StatusCode;
static const std::string _serialize_status(const tai_status_t status) {
tai_serialize_option_t option{ .human = true, .valueonly = true, .json = false};
size_t size = 64;
char buf[size];
auto ret = tai_serialize_status(buf, size, status, &option);
if ( ret < 0 ) {
return "unknown";
}
return std::string(buf, ret);
}
static void add_status(::grpc::ServerContext* context, tai_status_t status) {
context->AddTrailingMetadata("tai-status-code", std::to_string(status));
context->AddTrailingMetadata("tai-status-msg", _serialize_status(status));
}
::grpc::Status TAIServiceImpl::ListModule(::grpc::ServerContext* context, const taish::ListModuleRequest* request, ::grpc::ServerWriter< taish::ListModuleResponse>* writer) {
std::vector<tai_api_module_t> list;
auto ret = m_api->list_module(list);
if ( ret != TAI_STATUS_SUCCESS ) {
goto err;
}
for ( const auto& module : list ) {
auto res = taish::ListModuleResponse();
auto m = res.mutable_module();
m->set_location(module.location);
m->set_present(module.present);
m->set_oid(module.id);
if ( module.id != TAI_NULL_OBJECT_ID ) {
m->clear_hostifs();
for ( uint32_t i = 0; i < module.hostifs.size(); i++ ) {
auto hostif = m->add_hostifs();
hostif->set_index(i);
hostif->set_oid(module.hostifs[i]);
}
m->clear_netifs();
for ( uint32_t i = 0; i < module.netifs.size(); i++ ) {
auto netif = m->add_netifs();
netif->set_index(i);
netif->set_oid(module.netifs[i]);
}
}
writer->Write(res);
}
err:
add_status(context, ret);
return Status::OK;
}
static void usage(const tai_attr_metadata_t* meta, std::string* str) {
if ( meta->isenum && meta->enummetadata != nullptr ) {
auto m = meta->enummetadata;
*str = "[";
for ( uint32_t i = 0; i < m->valuescount; i++ ) {
*str += m->valuesshortnames[i];
if ( i < m->valuescount - 1 ) {
*str += "|";
}
}
*str += "]";
return;
}
char buf[32] = {0};
tai_serialize_option_t option = {true};
auto ret = tai_serialize_attr_value_type(buf, 32, meta->attrvaluetype, &option);
if ( ret < 0 ) {
*str = "<unknown>";
}
std::stringstream ss;
ss << "<" << buf << ">";
*str = ss.str();
}
static void convert_metadata(const tai_attr_metadata_t* const src, taish::AttributeMetadata* const dst) {
dst->set_attr_id(src->attrid);
dst->set_name(src->attridname);
dst->set_short_name(src->attridshortname);
dst->set_object_type(static_cast<taish::TAIObjectType>(src->objecttype));
auto u = dst->mutable_usage();
usage(src, u);
dst->set_is_readonly(src->isreadonly);
dst->set_is_mandatoryoncreate(src->ismandatoryoncreate);
dst->set_is_createonly(src->iscreateonly);
dst->set_is_createandset(src->iscreateandset);
dst->set_is_key(src->iskey);
dst->set_is_enum(src->isenum);
}
static tai_serialize_option_t convert_serialize_option(const taish::SerializeOption& src) {
tai_serialize_option_t dst;
dst.human = src.human();
dst.valueonly = src.value_only();
dst.json = src.json();
return dst;
}
::grpc::Status TAIServiceImpl::ListAttributeMetadata(::grpc::ServerContext* context, const taish::ListAttributeMetadataRequest* request, ::grpc::ServerWriter< taish::ListAttributeMetadataResponse>* writer) {
auto res = taish::ListAttributeMetadataResponse();
auto object_type = request->object_type();
auto info = tai_metadata_all_object_type_infos[object_type];
if ( info == nullptr ) {
for ( uint32_t i = 0; i < tai_metadata_attr_sorted_by_id_name_count; i++ ) {
auto src = tai_metadata_attr_sorted_by_id_name[i];
auto dst = res.mutable_metadata();
convert_metadata(src, dst);
writer->Write(res);
}
return Status::OK;
}
for ( uint32_t i = 0; i < info->attrmetadatalength; i++ ) {
auto src = info->attrmetadata[i];
auto dst = res.mutable_metadata();
convert_metadata(src, dst);
writer->Write(res);
}
return Status::OK;
}
::grpc::Status TAIServiceImpl::GetAttributeMetadata(::grpc::ServerContext* context, const taish::GetAttributeMetadataRequest* request, taish::GetAttributeMetadataResponse* response) {
auto object_type = request->object_type();
int32_t attr_id = 0;
if ( request->attr_name() != "" ) {
auto attr_name = request->attr_name();
int ret;
auto option = convert_serialize_option(request->serialize_option());
switch (object_type) {
case taish::MODULE:
ret = tai_deserialize_module_attr(attr_name.c_str(), &attr_id, &option);
break;
case taish::NETIF:
ret = tai_deserialize_network_interface_attr(attr_name.c_str(), &attr_id, &option);
break;
case taish::HOSTIF:
ret = tai_deserialize_host_interface_attr(attr_name.c_str(), &attr_id, &option);
break;
default:
ret = TAI_STATUS_NOT_SUPPORTED;
}
if ( ret < 0 ) {
add_status(context, ret);
return Status::OK;
}
} else {
attr_id = request->attr_id();
}
auto meta = tai_metadata_get_attr_metadata(static_cast<tai_object_type_t>(object_type), attr_id);
if ( meta == nullptr ) {
return Status(StatusCode::NOT_FOUND, "not found metadata");
}
auto res = response->mutable_metadata();
convert_metadata(meta, res);
add_status(context, TAI_STATUS_SUCCESS);
return Status::OK;
}
::grpc::Status TAIServiceImpl::GetAttribute(::grpc::ServerContext* context, const taish::GetAttributeRequest* request, taish::GetAttributeResponse* response) {
auto oid = request->oid();
taish::Attribute a = request->attribute();
taish::Attribute* res;
auto id = a.attr_id();
auto value = a.value();
auto type = tai_object_type_query(oid);
auto meta = tai_metadata_get_attr_metadata(type, id);
auto option = convert_serialize_option(request->serialize_option());
auto getter = [&](tai_attribute_t* attr) -> tai_status_t {
if ( value.size() > 0 ) {
auto ret = tai_deserialize_attribute_value(value.c_str(), meta, &attr->value, &option);
if ( ret < 0 ) {
return ret;
}
}
switch (type) {
case TAI_OBJECT_TYPE_MODULE:
return m_api->module_api->get_module_attribute(oid, attr);
case TAI_OBJECT_TYPE_NETWORKIF:
return m_api->netif_api->get_network_interface_attribute(oid, attr);
case TAI_OBJECT_TYPE_HOSTIF:
return m_api->hostif_api->get_host_interface_attribute(oid, attr);
default:
return TAI_STATUS_NOT_SUPPORTED;
}
};
auto ret = TAI_STATUS_SUCCESS;
try {
auto attr = std::make_unique<tai::Attribute>(meta, getter);
auto v = attr->to_string(&option);
res = response->mutable_attribute();
res->set_attr_id(id);
res->set_value(v);
} catch (tai::Exception& e) {
ret = e.err();
}
add_status(context, ret);
return Status::OK;
}
::grpc::Status TAIServiceImpl::SetAttribute(::grpc::ServerContext* context, const taish::SetAttributeRequest* request, taish::SetAttributeResponse* response) {
auto oid = request->oid();
auto a = request->attribute();
auto id = a.attr_id();
auto v = a.value();
auto type = tai_object_type_query(oid);
auto meta = tai_metadata_get_attr_metadata(type, id);
auto option = convert_serialize_option(request->serialize_option());
auto ret = TAI_STATUS_SUCCESS;
try {
auto attr = std::make_unique<tai::Attribute>(meta, v, &option);
switch (type) {
case TAI_OBJECT_TYPE_MODULE:
ret = m_api->module_api->set_module_attribute(oid, attr->raw());
break;
case TAI_OBJECT_TYPE_NETWORKIF:
ret = m_api->netif_api->set_network_interface_attribute(oid, attr->raw());
break;
case TAI_OBJECT_TYPE_HOSTIF:
ret = m_api->hostif_api->set_host_interface_attribute(oid, attr->raw());
break;
default:
ret = TAI_STATUS_NOT_SUPPORTED;
}
} catch (tai::Exception& e) {
ret = e.err();
}
add_status(context, ret);
return Status::OK;
}
::grpc::Status TAIServiceImpl::ClearAttribute(::grpc::ServerContext* context, const taish::ClearAttributeRequest* request, taish::ClearAttributeResponse* response) {
auto oid = request->oid();
auto id = request->attr_id();
auto type = tai_object_type_query(oid);
tai_status_t ret;
switch (type) {
case TAI_OBJECT_TYPE_HOSTIF:
ret = m_api->hostif_api->clear_host_interface_attribute(oid, id);
break;
default:
ret = TAI_STATUS_FAILURE;
}
add_status(context, ret);
return Status::OK;
}
int TAINotifier::notify(const tai_notification_t& n) {
std::unique_lock<std::mutex> lk(mtx);
for ( auto& s : m ) {
auto v = s.second;
std::unique_lock<std::mutex> lk(v->mtx);
v->q.push(n);
v->cv.notify_one();
}
return 0;
}
void monitor_callback(void* context, tai_object_id_t oid, uint32_t attr_count, tai_attribute_t const * const attr_list) {
if ( context == nullptr ) {
return;
}
tai_notification_t notification;
notification.oid = oid;
auto type = tai_object_type_query(oid);
for ( uint32_t i = 0; i < attr_count; i++ ) {
auto meta = tai_metadata_get_attr_metadata(type, attr_list[i].id);
if ( meta == nullptr ) {
continue;
}
notification.attrs.emplace_back(std::make_shared<tai::Attribute>(meta, &attr_list[i]));
}
auto n = static_cast<TAINotifier*>(context);
n->notify(notification);
}
::grpc::Status TAIServiceImpl::Monitor(::grpc::ServerContext* context, const taish::MonitorRequest* request, ::grpc::ServerWriter< taish::MonitorResponse>* writer) {
auto oid = request->oid();
auto nid = request->notification_attr_id();
auto type = tai_object_type_query(oid);
tai_attribute_t attr = {0};
tai_status_t ret;
tai_subscription_t s;
std::shared_ptr<TAINotifier> notifier = nullptr;
auto key = std::pair<tai_object_id_t, tai_attr_id_t>(oid, nid);
auto option = convert_serialize_option(request->serialize_option());
auto meta = tai_metadata_get_attr_metadata(type, nid);
if ( meta == nullptr ) {
return Status(StatusCode::NOT_FOUND, "not found metadata");
}
if ( meta->attrvaluetype != TAI_ATTR_VALUE_TYPE_NOTIFICATION ) {
return Status(StatusCode::INVALID_ARGUMENT, "value type is not notification");
}
{
std::unique_lock<std::mutex> lk(m_mtx);
attr.id = nid;
switch (type) {
case TAI_OBJECT_TYPE_NETWORKIF:
ret = m_api->netif_api->get_network_interface_attribute(oid, &attr);
break;
case TAI_OBJECT_TYPE_HOSTIF:
ret = m_api->hostif_api->get_host_interface_attribute(oid, &attr);
break;
case TAI_OBJECT_TYPE_MODULE:
ret = m_api->module_api->get_module_attribute(oid, &attr);
break;
default:
ret = TAI_STATUS_NOT_SUPPORTED;
}
if ( ret != TAI_STATUS_SUCCESS ) {
add_status(context, ret);
return Status::OK;
}
notifier = get_notifier(oid, nid);
if ( attr.value.notification.notify == nullptr ) {
attr.value.notification.notify = monitor_callback;
attr.value.notification.context = notifier.get();
switch (type) {
case TAI_OBJECT_TYPE_NETWORKIF:
ret = m_api->netif_api->set_network_interface_attribute(oid, &attr);
break;
case TAI_OBJECT_TYPE_HOSTIF:
ret = m_api->hostif_api->set_host_interface_attribute(oid, &attr);
break;
case TAI_OBJECT_TYPE_MODULE:
ret = m_api->module_api->set_module_attribute(oid, &attr);
break;
default:
ret = TAI_STATUS_NOT_SUPPORTED;
}
if ( ret != TAI_STATUS_SUCCESS ) {
add_status(context, ret);
return Status::OK;
}
} else if ( attr.value.notification.notify != nullptr && notifier->size() == 0 ) {
return Status(StatusCode::UNKNOWN, "notify attribute is set by others");
}
if ( notifier->subscribe(writer, &s) < 0 ) {
return Status(StatusCode::UNKNOWN, "failed to subscribe");
}
}
{
std::unique_lock<std::mutex> lk(s.mtx);
while(true) {
std::chrono::seconds sec(1);
s.cv.wait_for(lk, sec, [&]{ return !s.q.empty(); });
if ( context->IsCancelled() ) {
break;
}
{
std::unique_lock<std::mutex> lk(m_mtx);
if ( m_notifiers.find(key) == m_notifiers.end() ) {
return Status(StatusCode::UNKNOWN, "object is removed");
}
}
if ( s.q.size() == 0 ) {
continue;
}
auto n = s.q.front();
s.q.pop();
taish::MonitorResponse res;
for ( auto e : n.attrs ) {
auto a = res.add_attrs();
a->set_attr_id(e->id());
a->set_value(e->to_string(&option));
}
if (!writer->Write(res)) {
break;
}
}
}
{
std::unique_lock<std::mutex> lk(m_mtx);
if ( notifier->size() == 1 ) {
attr.value.notification.notify = nullptr;
attr.value.notification.context = nullptr;
switch (type) {
case TAI_OBJECT_TYPE_NETWORKIF:
ret = m_api->netif_api->set_network_interface_attribute(oid, &attr);
break;
case TAI_OBJECT_TYPE_HOSTIF:
ret = m_api->hostif_api->set_host_interface_attribute(oid, &attr);
break;
case TAI_OBJECT_TYPE_MODULE:
ret = m_api->module_api->set_module_attribute(oid, &attr);
break;
default:
ret = TAI_STATUS_NOT_SUPPORTED;
}
if ( ret != TAI_STATUS_SUCCESS ) {
add_status(context, ret);
return Status::OK;
}
}
if ( notifier->desubscribe(writer) < 0 ) {
return Status(StatusCode::UNKNOWN, "failed to desubscribe");
}
if ( notifier->size() == 0 ) {
m_notifiers.erase(key);
}
}
return Status::OK;
}
::grpc::Status TAIServiceImpl::SetLogLevel(::grpc::ServerContext* context, const taish::SetLogLevelRequest* request, taish::SetLogLevelResponse* response) {
auto ret = tai_log_set(static_cast<tai_api_t>(request->api()), static_cast<tai_log_level_t>(request->level()), nullptr);
add_status(context, ret);
return Status::OK;
}
::grpc::Status TAIServiceImpl::Create(::grpc::ServerContext* context, const taish::CreateRequest* request, taish::CreateResponse* response) {
auto object_type = request->object_type();
auto mid = static_cast<tai_object_id_t>(request->module_id());
tai_object_type_t type;
std::function<tai_status_t(tai_object_id_t*, uint32_t, const tai_attribute_t*)> create;
switch (object_type) {
case taish::MODULE:
type = TAI_OBJECT_TYPE_MODULE;
create = m_api->module_api->create_module;
break;
case taish::NETIF:
type = TAI_OBJECT_TYPE_NETWORKIF;
create = std::bind(m_api->netif_api->create_network_interface, std::placeholders::_1, mid, std::placeholders::_2, std::placeholders::_3);
break;
case taish::HOSTIF:
type = TAI_OBJECT_TYPE_HOSTIF;
create = std::bind(m_api->hostif_api->create_host_interface, std::placeholders::_1, mid, std::placeholders::_2, std::placeholders::_3);
break;
default:
return Status(StatusCode::INVALID_ARGUMENT, "unsupported object type");
}
std::vector<tai::S_Attribute> attrs;
auto option = convert_serialize_option(request->serialize_option());
try {
for ( auto i = 0; i < request->attrs_size(); i++ ) {
auto a = request->attrs(i);
auto id = a.attr_id();
auto v = a.value();
auto meta = tai_metadata_get_attr_metadata(type, id);
auto attr = std::make_shared<tai::Attribute>(meta, v, &option);
attrs.emplace_back(attr);
}
} catch (tai::Exception& e) {
add_status(context, e.err());
return Status::OK;
}
tai_object_id_t oid;
std::vector<tai_attribute_t> list;
for ( auto& v : attrs ) {
list.emplace_back(*(v->raw()));
}
auto ret = create(&oid, list.size(), list.data());
if ( ret == TAI_STATUS_SUCCESS ) {
response->set_oid(oid);
m_api->object_update(type, oid, true);
}
add_status(context, ret);
return Status::OK;
}
::grpc::Status TAIServiceImpl::Remove(::grpc::ServerContext* context, const taish::RemoveRequest* request, taish::RemoveResponse* response) {
auto oid = request->oid();
auto type = tai_object_type_query(oid);
tai_status_t ret;
switch (type) {
case TAI_OBJECT_TYPE_MODULE:
ret = m_api->module_api->remove_module(oid);
break;
case TAI_OBJECT_TYPE_NETWORKIF:
ret = m_api->netif_api->remove_network_interface(oid);
break;
case TAI_OBJECT_TYPE_HOSTIF:
ret = m_api->hostif_api->remove_host_interface(oid);
break;
default:
ret = TAI_STATUS_NOT_SUPPORTED;
}
if ( ret == TAI_STATUS_SUCCESS ) {
{
std::unique_lock<std::mutex> lk(m_mtx);
auto it = m_notifiers.begin();
while ( it != m_notifiers.end() ) {
if ( it->first.first == oid ) {
it = m_notifiers.erase(it);
} else {
it++;
}
}
}
m_api->object_update(type, oid, false);
}
add_status(context, ret);
return Status::OK;
}