diff --git a/CMakeLists.txt b/CMakeLists.txt index a7e056f..fa96d55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,16 @@ get_filename_component(juniper_gnmi_proto_path "${juniper_gnmi_proto}" PATH) get_filename_component(juniper_gnmi_ext_proto "proto/Juniper/juniper_gnmi_ext.proto" ABSOLUTE) get_filename_component(juniper_gnmi_ext_proto_path "${juniper_gnmi_ext_proto}" PATH) +# Proto file gRPC dial-out / gnmi / gnmi extensions - Nokia +get_filename_component(nokia_gnmi_dialout_proto "proto/Nokia/nokia_dialout.proto" ABSOLUTE) +get_filename_component(nokia_gnmi_dialout_proto_path "${nokia_gnmi_dialout_proto}" PATH) + +get_filename_component(nokia_gnmi_proto "proto/Nokia/nokia_gnmi.proto" ABSOLUTE) +get_filename_component(nokia_gnmi_proto_path "${nokia_gnmi_proto}" PATH) + +get_filename_component(nokia_gnmi_ext_proto "proto/Nokia/nokia_gnmi_ext.proto" ABSOLUTE) +get_filename_component(nokia_gnmi_ext_proto_path "${nokia_gnmi_ext_proto}" PATH) + # Proto file gRPC dial-out - Huawei get_filename_component(huawei_dialout_proto "proto/Huawei/huawei_dialout.proto" ABSOLUTE) get_filename_component(huawei_dialout_proto_path "${huawei_dialout_proto}" PATH) @@ -193,6 +203,57 @@ add_custom_command( "${juniper_gnmi_dialout_proto}" ) +# Protobuf generated "gRPC dial-out" - Nokia +set(nokia_gnmi_ext_pb_cc + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_gnmi_ext.pb.cc") +set(nokia_gnmi_ext_pb_h + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_gnmi_ext.pb.h") +set(nokia_gnmi_pb_cc + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_gnmi.pb.cc") +set(nokia_gnmi_pb_h + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_gnmi.pb.h") +set(nokia_gnmi_pb_grpc_cc + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_gnmi.grpc.pb.cc") +set(nokia_gnmi_pb_grpc_h + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_gnmi.grpc.pb.h") +set(nokia_gnmi_dialout_pb_cc + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_dialout.pb.cc") +set(nokia_gnmi_dialout_pb_h + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_dialout.pb.h") +set(nokia_gnmi_dialout_pb_grpc_cc + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_dialout.grpc.pb.cc") +set(nokia_gnmi_dialout_pb_grpc_h + "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia/nokia_dialout.grpc.pb.h") +add_custom_command( + OUTPUT + "${nokia_gnmi_ext_pb_cc}" + "${nokia_gnmi_ext_pb_h}" + "${nokia_gnmi_pb_cc}" + "${nokia_gnmi_pb_h}" + "${nokia_gnmi_pb_grpc_cc}" + "${nokia_gnmi_pb_grpc_h}" + "${nokia_gnmi_dialout_pb_cc}" + "${nokia_gnmi_dialout_pb_h}" + "${nokia_gnmi_dialout_pb_grpc_cc}" + "${nokia_gnmi_dialout_pb_grpc_h}" + COMMAND + ${_PROTOBUF_PROTOC} + ARGS + -I "${nokia_gnmi_ext_proto_path}" + -I "${nokia_gnmi_proto_path}" + -I "${nokia_gnmi_dialout_proto_path}" + --cpp_out "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia" + "${nokia_gnmi_ext_proto}" + "${nokia_gnmi_proto}" + "${nokia_gnmi_dialout_proto}" + --grpc_out "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Nokia" + --plugin=protoc-gen-grpc="${_GRPC_CPP_PLUGIN_EXECUTABLE}" + DEPENDS + "${nokia_gnmi_ext_proto}" + "${nokia_gnmi_proto}" + "${nokia_gnmi_dialout_proto}" +) + # Protobuf generated "gRPC dial-out" - Huawei set(huawei_dialout_pb_cc "${CMAKE_CURRENT_SOURCE_DIR}/src/proto/Huawei/huawei_dialout.pb.cc") @@ -368,6 +429,10 @@ foreach(sourcefile ${APP_SOURCES}) ${juniper_telemetry_pb_cc} ${juniper_telemetry_header_pb_cc} ${juniper_telemetry_header_extension_pb_cc} + ${nokia_gnmi_pb_cc} + ${nokia_gnmi_ext_pb_cc} + ${nokia_gnmi_dialout_pb_cc} + ${nokia_gnmi_dialout_pb_grpc_cc} ${huawei_dialout_pb_cc} ${huawei_dialout_pb_grpc_cc} ${huawei_telemetry_pb_cc} diff --git a/doc/CONFIG-KEYS b/doc/CONFIG-KEYS index 54e04c1..551f7a9 100644 --- a/doc/CONFIG-KEYS +++ b/doc/CONFIG-KEYS @@ -23,6 +23,12 @@ DESC: Defining the network end-point receiving the gRPC data-stream from Ju value is set and if the configuration key is specified its value can't be empty. DEFAULT: none +KEY: ipv4_socket_nokia +DESC: Defining the network end-point receiving the gRPC data-stream from Nokia devices. The end-point information must include both the + IPv4 and port separated by colon, for example "192.18.0.2:10002" is considered to be a valid end-point. No default + value is set and if the configuration key is specified its value can't be empty. +DEFAULT: none + KEY: ipv4_socket_huawei DESC: Defining the network end-point receiving the gRPC data-stream from Huawei devices. The end-point information must include both the IPv4 and port separated by colon, for example "192.18.0.3:10003" is considered to be a valid end-point. No default @@ -41,6 +47,12 @@ DESC: This is ment mainly for debugging. With this configuration key you ca a single receiving session. The default value is set to "0" which means unlimited. DEFAULT: "0" +KEY: replies_nokia +VALUES: [value >= "0" and value <= "1000"] +DESC: This is ment mainly for debugging. With this configuration key you can rate limit the amount of Nokia's gRPC messages processed within + a single receiving session. The default value is set to "0" which means unlimited. +DEFAULT: "0" + KEY: replies_huawei VALUES: [value >= "0" and value <= "1000"] DESC: This is ment mainly for debugging. With this configuration key you can rate limit the amount of Huawei's gRPC messages processed within @@ -57,6 +69,11 @@ VALUES: [value >= "1" and value <= "5"] DESC: Defining the amount of running threads busy with processing incoming Juniper's gRPC messages. DEFAULT: "1" +KEY: nokia_workers +VALUES: [value >= "1" and value <= "5"] +DESC: Defining the amount of running threads busy with processing incoming Nokia's gRPC messages. +DEFAULT: "1" + KEY: huawei_workers VALUES: [value >= "1" and value <= "5"] DESC: Defining the amount of running threads busy with processing incoming Huawei's gRPC messages. diff --git a/proto/Juniper/juniper_dialout.proto b/proto/Juniper/juniper_dialout.proto index 4f85f2c..348529f 100644 --- a/proto/Juniper/juniper_dialout.proto +++ b/proto/Juniper/juniper_dialout.proto @@ -20,7 +20,7 @@ service Subscriber { https://github.com/openconfig/public/blob/master/release/models/telemetry/openconfig-telemetry.yang */ - rpc DialOutSubscriber(stream gnmi.SubscribeResponse) returns (stream gnmi.SubscribeRequest); + rpc DialOutSubscriber(stream juniper_gnmi.SubscribeResponse) returns (stream juniper_gnmi.SubscribeRequest); } diff --git a/proto/Juniper/juniper_gnmi.proto b/proto/Juniper/juniper_gnmi.proto index 35678da..6a744be 100644 --- a/proto/Juniper/juniper_gnmi.proto +++ b/proto/Juniper/juniper_gnmi.proto @@ -27,7 +27,7 @@ import "juniper_gnmi_ext.proto"; // // This document references the gNMI Specification which can be found at // http://github.com/openconfig/reference/blob/master/rpc/gnmi -package gnmi; +package juniper_gnmi; // Define a protobuf FileOption that defines the gNMI service version. extend google.protobuf.FileOptions { @@ -37,7 +37,7 @@ extend google.protobuf.FileOptions { // gNMI_service is the current version of the gNMI service, returned through // the Capabilities RPC. -option (gnmi_service) = "0.7.0"; +option (gnmi_service) = "0.8.0"; service gNMI { // Capabilities allows the client to retrieve the set of capabilities that @@ -211,7 +211,7 @@ message SubscribeRequest { } // Extension messages associated with the SubscribeRequest. See the // gNMI extension specification for further definition. - repeated gnmi_ext.Extension extension = 5; + repeated juniper_gnmi_ext.Extension extension = 5; } // Poll is sent within a SubscribeRequest to trigger the device to @@ -238,7 +238,7 @@ message SubscribeResponse { } // Extension messages associated with the SubscribeResponse. See the // gNMI extension specification for further definition. - repeated gnmi_ext.Extension extension = 5; + repeated juniper_gnmi_ext.Extension extension = 5; } // SubscriptionList is used within a Subscribe message to specify the list of @@ -341,7 +341,7 @@ message SetRequest { repeated Update update = 4; // Updates specifying elements to updated. // Extension messages associated with the SetRequest. See the // gNMI extension specification for further definition. - repeated gnmi_ext.Extension extension = 5; + repeated juniper_gnmi_ext.Extension extension = 5; } // SetResponse is the response to a SetRequest, sent from the target to the @@ -360,7 +360,7 @@ message SetResponse { int64 timestamp = 4; // Timestamp of transaction (ns since epoch). // Extension messages associated with the SetResponse. See the // gNMI extension specification for further definition. - repeated gnmi_ext.Extension extension = 5; + repeated juniper_gnmi_ext.Extension extension = 5; } // UpdateResult is used within the SetResponse message to communicate the @@ -408,7 +408,7 @@ message GetRequest { repeated ModelData use_models = 6; // The schema models to be used. // Extension messages associated with the GetRequest. See the // gNMI extension specification for further definition. - repeated gnmi_ext.Extension extension = 7; + repeated juniper_gnmi_ext.Extension extension = 7; } // GetResponse is used by the target to respond to a GetRequest from a client. @@ -420,7 +420,7 @@ message GetResponse { Error error = 2 [deprecated=true]; // Errors that occurred in the Get. // Extension messages associated with the GetResponse. See the // gNMI extension specification for further definition. - repeated gnmi_ext.Extension extension = 3; + repeated juniper_gnmi_ext.Extension extension = 3; } // CapabilityRequest is sent by the client in the Capabilities RPC to request @@ -429,7 +429,7 @@ message GetResponse { message CapabilityRequest { // Extension messages associated with the CapabilityRequest. See the // gNMI extension specification for further definition. - repeated gnmi_ext.Extension extension = 1; + repeated juniper_gnmi_ext.Extension extension = 1; } // CapabilityResponse is used by the target to report its capabilities to the @@ -441,7 +441,7 @@ message CapabilityResponse { string gNMI_version = 3; // Supported gNMI version. // Extension messages associated with the CapabilityResponse. See the // gNMI extension specification for further definition. - repeated gnmi_ext.Extension extension = 4; + repeated juniper_gnmi_ext.Extension extension = 4; } // ModelData is used to describe a set of schema modules. It can be used in a diff --git a/proto/Juniper/juniper_gnmi_ext.proto b/proto/Juniper/juniper_gnmi_ext.proto index 502456f..baaa02f 100644 --- a/proto/Juniper/juniper_gnmi_ext.proto +++ b/proto/Juniper/juniper_gnmi_ext.proto @@ -19,7 +19,7 @@ syntax = "proto3"; // included with the request and response messages of gNMI RPCs. A set of // well-known extensions are defined within this file, along with a registry for // extensions defined outside of this package. -package gnmi_ext; +package juniper_gnmi_ext; // The Extension message contains a single gNMI extension. message Extension { diff --git a/proto/Makefile.am b/proto/Makefile.am index f53e2b5..276fc69 100644 --- a/proto/Makefile.am +++ b/proto/Makefile.am @@ -19,13 +19,14 @@ all: echo "Autogenerated files up-to-date..."; \ fi -_all: dirs grpc_cisco grpc_huawei grpc_juniper openconfig +_all: dirs grpc_cisco grpc_huawei grpc_juniper grpc_nokia openconfig echo "$(MD5SUM_HASH)" > .proto_mark dirs: mkdir -p ${top_builddir}/src/proto mkdir -p ${top_builddir}/src/proto/Cisco mkdir -p ${top_builddir}/src/proto/Huawei mkdir -p ${top_builddir}/src/proto/Juniper + mkdir -p ${top_builddir}/src/proto/Nokia mkdir -p ${top_builddir}/src/proto/OpenConfig grpc_cisco: @@ -68,6 +69,21 @@ grpc_juniper: --cpp_out ${top_builddir}/src/proto/Juniper \ --grpc_out ${top_builddir}/src/proto/Juniper \ --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin" juniper_gnmi_ext.proto + +grpc_nokia: + $(PROTOC) -I ${top_srcdir}/proto/Nokia/ \ + --cpp_out ${top_builddir}/src/proto/Nokia \ + --grpc_out ${top_builddir}/src/proto/Nokia \ + --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin" nokia_dialout.proto + $(PROTOC) -I ${top_srcdir}/proto/Nokia/ \ + --cpp_out ${top_builddir}/src/proto/Nokia \ + --grpc_out ${top_builddir}/src/proto/Nokia \ + --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin" nokia_gnmi.proto + $(PROTOC) -I ${top_srcdir}/proto/Nokia/ \ + --cpp_out ${top_builddir}/src/proto/Nokia \ + --grpc_out ${top_builddir}/src/proto/Nokia \ + --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin" nokia_gnmi_ext.proto + openconfig: $(PROTOC) -I ${top_srcdir}/proto/OpenConfig/ \ --cpp_out ${top_builddir}/src/proto/OpenConfig \ diff --git a/proto/Nokia/nokia_dialout.proto b/proto/Nokia/nokia_dialout.proto new file mode 100644 index 0000000..c59111b --- /dev/null +++ b/proto/Nokia/nokia_dialout.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +import "nokia_gnmi.proto"; + +package Nokia.SROS; + +service DialoutTelemetry { + rpc Publish(stream nokia_gnmi.SubscribeResponse) returns (stream PublishResponse); +} +message PublishResponse {} \ No newline at end of file diff --git a/proto/Nokia/nokia_gnmi.proto b/proto/Nokia/nokia_gnmi.proto new file mode 100644 index 0000000..517687c --- /dev/null +++ b/proto/Nokia/nokia_gnmi.proto @@ -0,0 +1,495 @@ +// +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +syntax = "proto3"; + +import "google/protobuf/any.proto"; +import "google/protobuf/descriptor.proto"; +import public "nokia_gnmi_ext.proto"; +//import "github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto"; +//import "github.com/openconfig/gnmi/proto/gnmi_ext/gnmi_ext.proto"; +//import "gnmi_ext.proto"; + +// Package gNMI defines a service specification for the gRPC Network Management +// Interface. This interface is defined to be a standard interface via which +// a network management system ("client") can subscribe to state values, +// retrieve snapshots of state information, and manipulate the state of a data +// tree supported by a device ("target"). +// +// This document references the gNMI Specification which can be found at +// http://github.com/openconfig/reference/blob/master/rpc/gnmi +package nokia_gnmi; + + +// gNMI_service is the current version of the gNMI service, returned through +// the Capabilities RPC. + +option go_package = "github.com/openconfig/gnmi/proto/gnmi"; +option java_multiple_files = true; +option java_outer_classname = "GnmiProto"; +option java_package = "com.github.gnmi.proto"; + + +service gNMI { + // Capabilities allows the client to retrieve the set of capabilities that + // is supported by the target. This allows the target to validate the + // service version that is implemented and retrieve the set of models that + // the target supports. The models can then be specified in subsequent RPCs + // to restrict the set of data that is utilized. + // Reference: gNMI Specification Section 3.2 + rpc Capabilities(CapabilityRequest) returns (CapabilityResponse); + // Retrieve a snapshot of data from the target. A Get RPC requests that the + // target snapshots a subset of the data tree as specified by the paths + // included in the message and serializes this to be returned to the + // client using the specified encoding. + // Reference: gNMI Specification Section 3.3 + rpc Get(GetRequest) returns (GetResponse); + // Set allows the client to modify the state of data on the target. The + // paths to modified along with the new values that the client wishes + // to set the value to. + // Reference: gNMI Specification Section 3.4 + rpc Set(SetRequest) returns (SetResponse); + // Subscribe allows a client to request the target to send it values + // of particular paths within the data tree. These values may be streamed + // at a particular cadence (STREAM), sent one off on a long-lived channel + // (POLL), or sent as a one-off retrieval (ONCE). + // Reference: gNMI Specification Section 3.5 + rpc Subscribe(stream SubscribeRequest) returns (stream SubscribeResponse); +} + +// Notification is a re-usable message that is used to encode data from the +// target to the client. A Notification carries two types of changes to the data +// tree: +// - Deleted values (delete) - a set of paths that have been removed from the +// data tree. +// - Updated values (update) - a set of path-value pairs indicating the path +// whose value has changed in the data tree. +// Reference: gNMI Specification Section 2.1 +message Notification { + int64 timestamp = 1; // Timestamp in nanoseconds since Epoch. + Path prefix = 2; // Prefix used for paths in the message. + // An alias for the path specified in the prefix field. + // Reference: gNMI Specification Section 2.4.2 + string alias = 3; + repeated Update update = 4; // Data elements that have changed values. + repeated Path delete = 5; // Data elements that have been deleted. + // This notification contains a set of paths that are always updated together + // referenced by a globally unique prefix. + bool atomic = 6; +} + +// Update is a re-usable message that is used to store a particular Path, +// Value pair. +// Reference: gNMI Specification Section 2.1 +message Update { + Path path = 1; // The path (key) for the update. + Value value = 2 [deprecated=true]; // The value (value) for the update. + TypedValue val = 3; // The explicitly typed update value. + uint32 duplicates = 4; // Number of coalesced duplicates. +} + +// TypedValue is used to encode a value being sent between the client and +// target (originated by either entity). +message TypedValue { + // One of the fields within the val oneof is populated with the value + // of the update. The type of the value being included in the Update + // determines which field should be populated. In the case that the + // encoding is a particular form of the base protobuf type, a specific + // field is used to store the value (e.g., json_val). + oneof value { + string string_val = 1; // String value. + int64 int_val = 2; // Integer value. + uint64 uint_val = 3; // Unsigned integer value. + bool bool_val = 4; // Bool value. + bytes bytes_val = 5; // Arbitrary byte sequence value. + float float_val = 6 [deprecated=true]; // Deprecated - use double_val. + double double_val = 14; // Floating point value. + Decimal64 decimal_val = 7 [deprecated=true]; // Deprecated - use double_val. + ScalarArray leaflist_val = 8; // Mixed type scalar array value. + google.protobuf.Any any_val = 9; // protobuf.Any encoded bytes. + bytes json_val = 10; // JSON-encoded text. + bytes json_ietf_val = 11; // JSON-encoded text per RFC7951. + string ascii_val = 12; // Arbitrary ASCII text. + // Protobuf binary encoded bytes. The message type is not included. + // See the specification at + // github.com/openconfig/reference/blob/master/rpc/gnmi/protobuf-vals.md + // for a complete specification. [Experimental] + bytes proto_bytes = 13; + } +} + +// Path encodes a data tree path as a series of repeated strings, with +// each element of the path representing a data tree node name and the +// associated attributes. +// Reference: gNMI Specification Section 2.2.2. +message Path { + // Elements of the path are no longer encoded as a string, but rather within + // the elem field as a PathElem message. + repeated string element = 1 [deprecated=true]; + string origin = 2; // Label to disambiguate path. + repeated PathElem elem = 3; // Elements of the path. + string target = 4; // The name of the target + // (Sec. 2.2.2.1) +} + +// PathElem encodes an element of a gNMI path, along with any attributes (keys) +// that may be associated with it. +// Reference: gNMI Specification Section 2.2.2. +message PathElem { + string name = 1; // The name of the element in the path. + map key = 2; // Map of key (attribute) name to value. +} + +// Value encodes a data tree node's value - along with the way in which +// the value is encoded. This message is deprecated by gNMI 0.3.0. +// Reference: gNMI Specification Section 2.2.3. +message Value { + option deprecated = true; + bytes value = 1; // Value of the variable being transmitted. + Encoding type = 2; // Encoding used for the value field. +} + +// Encoding defines the value encoding formats that are supported by the gNMI +// protocol. These encodings are used by both the client (when sending Set +// messages to modify the state of the target) and the target when serializing +// data to be returned to the client (in both Subscribe and Get RPCs). +// Reference: gNMI Specification Section 2.3 +enum Encoding { + JSON = 0; // JSON encoded text. + BYTES = 1; // Arbitrarily encoded bytes. + PROTO = 2; // Encoded according to scalar values of TypedValue. + ASCII = 3; // ASCII text of an out-of-band agreed format. + JSON_IETF = 4; // JSON encoded text as per RFC7951. +} + +// Error message previously utilised to return errors to the client. Deprecated +// in favour of using the google.golang.org/genproto/googleapis/rpc/status +// message in the RPC response. +// Reference: gNMI Specification Section 2.5 +message Error { + option deprecated = true; + uint32 code = 1; // Canonical gRPC error code. + string message = 2; // Human readable error. + google.protobuf.Any data = 3; // Optional additional information. +} + +// Decimal64 is used to encode a fixed precision decimal number. The value +// is expressed as a set of digits with the precision specifying the +// number of digits following the decimal point in the digit set. +message Decimal64 { + int64 digits = 1; // Set of digits. + uint32 precision = 2; // Number of digits following the decimal point. +} + +// ScalarArray is used to encode a mixed-type array of values. +message ScalarArray { + // The set of elements within the array. Each TypedValue message should + // specify only elements that have a field identifier of 1-7 (i.e., the + // values are scalar values). + repeated TypedValue element = 1; +} + +// SubscribeRequest is the message sent by the client to the target when +// initiating a subscription to a set of paths within the data tree. The +// request field must be populated and the initial message must specify a +// SubscriptionList to initiate a subscription. The message is subsequently +// used to define aliases or trigger polled data to be sent by the target. +// Reference: gNMI Specification Section 3.5.1.1 +message SubscribeRequest { + oneof request { + SubscriptionList subscribe = 1; // Specify the paths within a subscription. + Poll poll = 3; // Trigger a polled update. + AliasList aliases = 4; // Aliases to be created. + } + // Extension messages associated with the SubscribeRequest. See the + // gNMI extension specification for further definition. + repeated nokia_gnmi_ext.Extension extension = 5; +} + +// Poll is sent within a SubscribeRequest to trigger the device to +// send telemetry updates for the paths that are associated with the +// subscription. +// Reference: gNMI Specification Section Section 3.5.1.4 +message Poll { +} + +// SubscribeResponse is the message used by the target within a Subscribe RPC. +// The target includes a Notification message which is used to transmit values +// of the path(s) that are associated with the subscription. The same message +// is to indicate that the target has sent all data values once (is +// synchronized). +// Reference: gNMI Specification Section 3.5.1.4 +message SubscribeResponse { + oneof response { + Notification update = 1; // Changed or sampled value for a path. + // Indicate target has sent all values associated with the subscription + // at least once. + bool sync_response = 3; + // Deprecated in favour of google.golang.org/genproto/googleapis/rpc/status + Error error = 4 [deprecated=true]; + } + // Extension messages associated with the SubscribeResponse. See the + // gNMI extension specification for further definition. + repeated nokia_gnmi_ext.Extension extension = 5; +} + +// SubscriptionList is used within a Subscribe message to specify the list of +// paths that the client wishes to subscribe to. The message consists of a +// list of (possibly prefixed) paths, and options that relate to the +// subscription. +// Reference: gNMI Specification Section 3.5.1.2 +message SubscriptionList { + Path prefix = 1; // Prefix used for paths. + repeated Subscription subscription = 2; // Set of subscriptions to create. + // Whether target defined aliases are allowed within the subscription. + bool use_aliases = 3; + QOSMarking qos = 4; // DSCP marking to be used. + // Mode of the subscription. + enum Mode { + STREAM = 0; // Values streamed by the target (Sec. 3.5.1.5.2). + ONCE = 1; // Values sent once-off by the target (Sec. 3.5.1.5.1). + POLL = 2; // Values sent in response to a poll request (Sec. 3.5.1.5.3). + } + Mode mode = 5; + // Whether elements of the schema that are marked as eligible for aggregation + // should be aggregated or not. + bool allow_aggregation = 6; + // The set of schemas that define the elements of the data tree that should + // be sent by the target. + repeated ModelData use_models = 7; + // The encoding that the target should use within the Notifications generated + // corresponding to the SubscriptionList. + Encoding encoding = 8; + // An optional field to specify that only updates to current state should be + // sent to a client. If set, the initial state is not sent to the client but + // rather only the sync message followed by any subsequent updates to the + // current state. For ONCE and POLL modes, this causes the server to send only + // the sync message (Sec. 3.5.2.3). + bool updates_only = 9; +} + +// Subscription is a single request within a SubscriptionList. The path +// specified is interpreted (along with the prefix) as the elements of the data +// tree that the client is subscribing to. The mode determines how the target +// should trigger updates to be sent. +// Reference: gNMI Specification Section 3.5.1.3 +message Subscription { + Path path = 1; // The data tree path. + SubscriptionMode mode = 2; // Subscription mode to be used. + uint64 sample_interval = 3; // ns between samples in SAMPLE mode. + // Indicates whether values that have not changed should be sent in a SAMPLE + // subscription. + bool suppress_redundant = 4; + // Specifies the maximum allowable silent period in nanoseconds when + // suppress_redundant is in use. The target should send a value at least once + // in the period specified. + uint64 heartbeat_interval = 5; +} + +// SubscriptionMode is the mode of the subscription, specifying how the +// target must return values in a subscription. +// Reference: gNMI Specification Section 3.5.1.3 +enum SubscriptionMode { + TARGET_DEFINED = 0; // The target selects the relevant mode for each element. + ON_CHANGE = 1; // The target sends an update on element value change. + SAMPLE = 2; // The target samples values according to the interval. +} + +// QOSMarking specifies the DSCP value to be set on transmitted telemetry +// updates from the target. +// Reference: gNMI Specification Section 3.5.1.2 +message QOSMarking { + uint32 marking = 1; +} + +// Alias specifies a data tree path, and an associated string which defines an +// alias which is to be used for this path in the context of the RPC. The alias +// is specified as a string which is prefixed with "#" to disambiguate it from +// data tree element paths. +// Reference: gNMI Specification Section 2.4.2 +message Alias { + Path path = 1; // The path to be aliased. + string alias = 2; // The alias value, a string prefixed by "#". +} + +// AliasList specifies a list of aliases. It is used in a SubscribeRequest for +// a client to create a set of aliases that the target is to utilize. +// Reference: gNMI Specification Section 3.5.1.6 +message AliasList { + repeated Alias alias = 1; // The set of aliases to be created. +} + +// SetRequest is sent from a client to the target to update values in the data +// tree. Paths are either deleted by the client, or modified by means of being +// updated, or replaced. Where a replace is used, unspecified values are +// considered to be replaced, whereas when update is used the changes are +// considered to be incremental. The set of changes that are specified within +// a single SetRequest are considered to be a transaction. +// Reference: gNMI Specification Section 3.4.1 +message SetRequest { + Path prefix = 1; // Prefix used for paths in the message. + repeated Path delete = 2; // Paths to be deleted from the data tree. + repeated Update replace = 3; // Updates specifying elements to be replaced. + repeated Update update = 4; // Updates specifying elements to updated. + // Extension messages associated with the SetRequest. See the + // gNMI extension specification for further definition. + repeated nokia_gnmi_ext.Extension extension = 5; +} + +// SetResponse is the response to a SetRequest, sent from the target to the +// client. It reports the result of the modifications to the data tree that were +// specified by the client. Errors for this RPC should be reported using the +// https://github.com/googleapis/googleapis/blob/master/google/rpc/status.proto +// message in the RPC return. The gnmi.Error message can be used to add additional +// details where required. +// Reference: gNMI Specification Section 3.4.2 +message SetResponse { + Path prefix = 1; // Prefix used for paths. + // A set of responses specifying the result of the operations specified in + // the SetRequest. + repeated UpdateResult response = 2; + Error message = 3 [deprecated=true]; // The overall status of the transaction. + int64 timestamp = 4; // Timestamp of transaction (ns since epoch). + // Extension messages associated with the SetResponse. See the + // gNMI extension specification for further definition. + repeated nokia_gnmi_ext.Extension extension = 5; +} + +// UpdateResult is used within the SetResponse message to communicate the +// result of an operation specified within a SetRequest message. +// Reference: gNMI Specification Section 3.4.2 +message UpdateResult { + // The operation that was associated with the Path specified. + enum Operation { + INVALID = 0; + DELETE = 1; // The result relates to a delete of Path. + REPLACE = 2; // The result relates to a replace of Path. + UPDATE = 3; // The result relates to an update of Path. + } + // Deprecated timestamp for the UpdateResult, this field has been + // replaced by the timestamp within the SetResponse message, since + // all mutations effected by a set should be applied as a single + // transaction. + int64 timestamp = 1 [deprecated=true]; + Path path = 2; // Path associated with the update. + Error message = 3 [deprecated=true]; // Status of the update operation. + Operation op = 4; // Update operation type. +} + +// GetRequest is sent when a client initiates a Get RPC. It is used to specify +// the set of data elements for which the target should return a snapshot of +// data. The use_models field specifies the set of schema modules that are to +// be used by the target - where use_models is not specified then the target +// must use all schema models that it has. +// Reference: gNMI Specification Section 3.3.1 +message GetRequest { + Path prefix = 1; // Prefix used for paths. + repeated Path path = 2; // Paths requested by the client. + // Type of elements within the data tree. + enum DataType { + ALL = 0; // All data elements. + CONFIG = 1; // Config (rw) only elements. + STATE = 2; // State (ro) only elements. + // Data elements marked in the schema as operational. This refers to data + // elements whose value relates to the state of processes or interactions + // running on the device. + OPERATIONAL = 3; + } + DataType type = 3; // The type of data being requested. + Encoding encoding = 5; // Encoding to be used. + repeated ModelData use_models = 6; // The schema models to be used. + // Extension messages associated with the GetRequest. See the + // gNMI extension specification for further definition. + repeated nokia_gnmi_ext.Extension extension = 7; +} + +// GetResponse is used by the target to respond to a GetRequest from a client. +// The set of Notifications corresponds to the data values that are requested +// by the client in the GetRequest. +// Reference: gNMI Specification Section 3.3.2 +message GetResponse { + repeated Notification notification = 1; // Data values. + Error error = 2 [deprecated=true]; // Errors that occurred in the Get. + // Extension messages associated with the GetResponse. See the + // gNMI extension specification for further definition. + repeated nokia_gnmi_ext.Extension extension = 3; +} + +// CapabilityRequest is sent by the client in the Capabilities RPC to request +// that the target reports its capabilities. +// Reference: gNMI Specification Section 3.2.1 +message CapabilityRequest { + // Extension messages associated with the CapabilityRequest. See the + // gNMI extension specification for further definition. + repeated nokia_gnmi_ext.Extension extension = 1; +} + +// CapabilityResponse is used by the target to report its capabilities to the +// client within the Capabilities RPC. +// Reference: gNMI Specification Section 3.2.2 +message CapabilityResponse { + repeated ModelData supported_models = 1; // Supported schema models. + repeated Encoding supported_encodings = 2; // Supported encodings. + string gNMI_version = 3; // Supported gNMI version. + // Extension messages associated with the CapabilityResponse. See the + // gNMI extension specification for further definition. + repeated nokia_gnmi_ext.Extension extension = 4; +} + +// ModelData is used to describe a set of schema modules. It can be used in a +// CapabilityResponse where a target reports the set of modules that it +// supports, and within the SubscribeRequest and GetRequest messages to specify +// the set of models from which data tree elements should be reported. +// Reference: gNMI Specification Section 3.2.3 +message ModelData { + string name = 1; // Name of the model. + string organization = 2; // Organization publishing the model. + string version = 3; // Semantic version of the model. +} + +service gNMIDialOut { + // Publish allows the target to send telemetry updates (in the form of + // SubscribeResponse messages, which have the same semantics as in the + // gNMI Subscribe RPC, to a client. The client may optionally return the + // PublishResponse message in response to the dial-out connection from the + // target. In this case, the client may modify the set of subscriptions + // that are to be published by the target by: + // - Specifying a client_id within the PublishResponse message. In this + // case the target should match pre-configured subscriptions the specified + // client_id, and send data only for the paths associated with the + // specified client_id. + // - Specifying a SubscribeRequest message within the subscriptions field of + // the PublishResponse message. This message has the same semantics as + // in the Subscribe gNMI RPC. + // In the case that the client specifies neither option, a default set of + // subscriptions (which should be configurable on the target) should be + // published to the client (collector). + // + // The configuration of subscriptions associated with the publish RPC may + // be through the OpenConfig telemetry configuration and operational state + // model: + // https://github.com/openconfig/public/blob/master/release/models/telemetry/openconfig-telemetry.yang + rpc Publish(stream SubscribeResponse) returns (stream PublishResponse); +} + +// PublishResponse is the message sent within the Publish RPC of the gNMI +// dial-out service by the client (collector) to the target. It is used to +// modify the set of paths that are to be sent by the target to the collector. +message PublishResponse { + oneof request { + string client_id = 1; // A string identifying the client to the target. + SubscribeRequest subscriptions = 2; // Optional specification of the subscriptions. + } +} diff --git a/proto/Nokia/nokia_gnmi_ext.proto b/proto/Nokia/nokia_gnmi_ext.proto new file mode 100644 index 0000000..1677304 --- /dev/null +++ b/proto/Nokia/nokia_gnmi_ext.proto @@ -0,0 +1,91 @@ +// +// Copyright 2018 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +syntax = "proto3"; + +// Package gnmi_ext defines a set of extensions messages which can be optionally +// included with the request and response messages of gNMI RPCs. A set of +// well-known extensions are defined within this file, along with a registry for +// extensions defined outside of this package. +package nokia_gnmi_ext; + +option go_package = "github.com/openconfig/gnmi/proto/gnmi_ext"; + +// The Extension message contains a single gNMI extension. +message Extension { + oneof ext { + RegisteredExtension registered_ext = 1; // A registered extension. + // Well known extensions. + MasterArbitration master_arbitration = 2; // Master arbitration extension. + History history = 3; // History extension. + } +} + +// The RegisteredExtension message defines an extension which is defined outside +// of this file. +message RegisteredExtension { + ExtensionID id = 1; // The unique ID assigned to this extension. + bytes msg = 2; // The binary-marshalled protobuf extension payload. +} + +// RegisteredExtension is an enumeration acting as a registry for extensions +// defined by external sources. +enum ExtensionID { + EID_UNSET = 0; + // New extensions are to be defined within this enumeration - their definition + // MUST link to a reference describing their implementation. + + // An experimental extension that may be used during prototyping of a new + // extension. + EID_EXPERIMENTAL = 999; +} + +// MasterArbitration is used to select the master among multiple gNMI clients +// with the same Roles. The client with the largest election_id is honored as +// the master. +// The document about gNMI master arbitration can be found at +// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-master-arbitration.md +message MasterArbitration { + Role role = 1; + Uint128 election_id = 2; +} + +// Representation of unsigned 128-bit integer. +message Uint128 { + uint64 high = 1; + uint64 low = 2; +} + +// There can be one master for each role. The role is identified by its id. +message Role { + string id = 1; + // More fields can be added if needed, for example, to specify what paths the + // role can read/write. +} + +// The History extension allows clients to request historical data. Its +// spec can be found at +// https://github.com/openconfig/reference/blob/master/rpc/gnmi/gnmi-history.md +message History { + oneof request { + int64 snapshot_time = 1; // Nanoseconds since the epoch + TimeRange range = 2; + } +} + +message TimeRange { + int64 start = 1; // Nanoseconds since the epoch + int64 end = 2; // Nanoseconds since the epoch +} \ No newline at end of file diff --git a/src/bridge/grpc_collector_bridge.cc b/src/bridge/grpc_collector_bridge.cc index 8bdc9e6..0942214 100644 --- a/src/bridge/grpc_collector_bridge.cc +++ b/src/bridge/grpc_collector_bridge.cc @@ -16,13 +16,16 @@ extern "C" { main_cfg_parameters.at("iface"), main_cfg_parameters.at("ipv4_socket_cisco"), main_cfg_parameters.at("ipv4_socket_juniper"), + main_cfg_parameters.at("ipv4_socket_nokia"), main_cfg_parameters.at("ipv4_socket_huawei"), /*main_cfg_parameters.at("core_pid_folder"),*/ main_cfg_parameters.at("cisco_workers"), main_cfg_parameters.at("juniper_workers"), + main_cfg_parameters.at("nokia_workers"), main_cfg_parameters.at("huawei_workers"), main_cfg_parameters.at("replies_cisco"), main_cfg_parameters.at("replies_juniper"), + main_cfg_parameters.at("replies_nokia"), main_cfg_parameters.at("replies_huawei"), logs_cfg_parameters.at("syslog"), logs_cfg_parameters.at("syslog_facility"), @@ -53,6 +56,11 @@ extern "C" { opts->ipv4_socket_juniper = strndup(ipv4_socket_juniper, strlen(ipv4_socket_juniper)); + const char *ipv4_socket_nokia = + cfg_wrapper.get_ipv4_socket_nokia().c_str(); + opts->ipv4_socket_nokia = + strndup(ipv4_socket_nokia, strlen(ipv4_socket_nokia)); + const char *ipv4_socket_huawei = cfg_wrapper.get_ipv4_socket_huawei().c_str(); opts->ipv4_socket_huawei = @@ -69,6 +77,9 @@ extern "C" { const char *juniper_workers = cfg_wrapper.get_juniper_workers().c_str(); opts->juniper_workers = strndup(juniper_workers, strlen(juniper_workers)); + const char *nokia_workers = cfg_wrapper.get_nokia_workers().c_str(); + opts->nokia_workers = strndup(nokia_workers, strlen(nokia_workers)); + const char *huawei_workers = cfg_wrapper.get_huawei_workers().c_str(); opts->huawei_workers = strndup(huawei_workers, strlen(huawei_workers)); @@ -78,6 +89,9 @@ extern "C" { const char *replies_juniper = cfg_wrapper.get_replies_juniper().c_str(); opts->replies_juniper = strndup(replies_juniper, strlen(replies_juniper)); + const char *replies_nokia = cfg_wrapper.get_replies_nokia().c_str(); + opts->replies_nokia = strndup(replies_nokia, strlen(replies_nokia)); + const char *replies_huawei = cfg_wrapper.get_replies_huawei().c_str(); opts->replies_huawei = strndup(replies_huawei, strlen(replies_huawei)); @@ -162,13 +176,16 @@ extern "C" { free(opts->iface); free(opts->ipv4_socket_cisco); free(opts->ipv4_socket_juniper); + free(opts->ipv4_socket_nokia); free(opts->ipv4_socket_huawei); //free(opts->core_pid_folder); free(opts->cisco_workers); free(opts->juniper_workers); + free(opts->nokia_workers); free(opts->huawei_workers); free(opts->replies_cisco); free(opts->replies_juniper); + free(opts->replies_nokia); free(opts->replies_huawei); free(opts->syslog); free(opts->syslog_facility); @@ -213,9 +230,16 @@ extern "C" { const char *zmq_uri) { LoadOptions(cfg_path, zmq_uri); - + spdlog::get("multi-logger")->debug( + "ipv4 sockets found in config:\n ipv4_socket_cisco: {},\n ipv4_socket_juniper: {},\n ipv4_socket_nokia: {},\n ipv4_socket_huawei: {}\n", + main_cfg_parameters.at("ipv4_socket_cisco"), + main_cfg_parameters.at("ipv4_socket_juniper"), + main_cfg_parameters.at("ipv4_socket_nokia"), + main_cfg_parameters.at("ipv4_socket_huawei") + ); if (main_cfg_parameters.at("ipv4_socket_cisco").empty() == true && main_cfg_parameters.at("ipv4_socket_juniper").empty() == true && + main_cfg_parameters.at("ipv4_socket_nokia").empty() == true && main_cfg_parameters.at("ipv4_socket_huawei").empty() == true) { spdlog::get("multi-logger")-> error("[ipv4_socket_*] configuration issue: " @@ -227,6 +251,7 @@ extern "C" { // Use arrays to store the worker threads per vendor pthread_t cisco_workers[MAX_WORKERS] = {0}; pthread_t juniper_workers[MAX_WORKERS] = {0}; + pthread_t nokia_workers[MAX_WORKERS] = {0}; pthread_t huawei_workers[MAX_WORKERS] = {0}; // Cisco @@ -237,6 +262,10 @@ extern "C" { LoadThreads(juniper_workers, "ipv4_socket_juniper", "replies_juniper", "juniper_workers"); + // Nokia + LoadThreads(nokia_workers, "ipv4_socket_nokia", "replies_nokia", + "nokia_workers"); + // Huawei LoadThreads(huawei_workers, "ipv4_socket_huawei", "replies_huawei", "huawei_workers"); @@ -249,6 +278,10 @@ extern "C" { pthread_detach(juniper_workers[w]); } + for (size_t w = 0; w < MAX_WORKERS && nokia_workers[w] != 0; w++) { + pthread_detach(nokia_workers[w]); + } + for (size_t w = 0; w < MAX_WORKERS && huawei_workers[w] != 0; w++) { pthread_detach(huawei_workers[w]); } @@ -332,6 +365,13 @@ extern "C" { std::string juniper_srv_socket {ipv4_socket_juniper}; Srv juniper_mdt_dialout_collector; juniper_mdt_dialout_collector.JuniperBind(juniper_srv_socket); + } else if (strstr(ipv4_socket_str, "nokia") != NULL) { + std::string ipv4_socket_nokia = + main_cfg_parameters.at(ipv4_socket_str); + + std::string nokia_srv_socket {ipv4_socket_nokia}; + Srv nokia_mdt_dialout_collector; + nokia_mdt_dialout_collector.NokiaBind(nokia_srv_socket); } else if (strstr(ipv4_socket_str, "huawei") != NULL) { std::string ipv4_socket_huawei = main_cfg_parameters.at(ipv4_socket_str); diff --git a/src/bridge/grpc_collector_bridge.h b/src/bridge/grpc_collector_bridge.h index 7abae22..0c1b2b7 100644 --- a/src/bridge/grpc_collector_bridge.h +++ b/src/bridge/grpc_collector_bridge.h @@ -24,15 +24,18 @@ extern "C" { char *iface; char *ipv4_socket_cisco; char *ipv4_socket_juniper; + char *ipv4_socket_nokia; char *ipv4_socket_huawei; /* char *core_pid_folder; */ /* workers */ char *cisco_workers; char *juniper_workers; + char *nokia_workers; char *huawei_workers; /* replies */ char *replies_cisco; char *replies_juniper; + char *replies_nokia; char *replies_huawei; /* logging */ char *syslog; diff --git a/src/cfgWrapper/cfg_wrapper.cc b/src/cfgWrapper/cfg_wrapper.cc index 472ed13..384dc58 100644 --- a/src/cfgWrapper/cfg_wrapper.cc +++ b/src/cfgWrapper/cfg_wrapper.cc @@ -12,13 +12,16 @@ bool CfgWrapper::BuildCfgWrapper( const std::string &iface, const std::string &ipv4_socket_cisco, const std::string &ipv4_socket_juniper, + const std::string &ipv4_socket_nokia, const std::string &ipv4_socket_huawei, //const std::string &core_pid_folder const std::string &cisco_workers, const std::string &juniper_workers, + const std::string &nokia_workers, const std::string &huawei_workers, const std::string &replies_cisco, const std::string &replies_juniper, + const std::string &replies_nokia, const std::string &replies_huawei, // logging const std::string &syslog, @@ -36,13 +39,16 @@ bool CfgWrapper::BuildCfgWrapper( set_iface(iface); set_ipv4_socket_cisco(ipv4_socket_cisco); set_ipv4_socket_juniper(ipv4_socket_juniper); + set_ipv4_socket_nokia(ipv4_socket_nokia); set_ipv4_socket_huawei(ipv4_socket_huawei); //set_core_pid_folder(core_pid_folder); set_cisco_workers(cisco_workers); - set_juniper_workers(cisco_workers); + set_juniper_workers(juniper_workers); + set_nokia_workers(nokia_workers); set_huawei_workers(huawei_workers); set_replies_cisco(replies_cisco); set_replies_juniper(replies_juniper); + set_replies_nokia(replies_nokia); set_replies_huawei(replies_huawei); set_syslog(syslog); set_syslog_facility(syslog_facility); diff --git a/src/cfgWrapper/cfg_wrapper.h b/src/cfgWrapper/cfg_wrapper.h index 4f24e49..324e6a7 100644 --- a/src/cfgWrapper/cfg_wrapper.h +++ b/src/cfgWrapper/cfg_wrapper.h @@ -31,13 +31,16 @@ class CfgWrapper { const std::string &iface, const std::string &ipv4_socket_cisco, const std::string &ipv4_socket_juniper, + const std::string &ipv4_socket_nokia, const std::string &ipv4_socket_huawei, //const std::string &core_pid_folder const std::string &cisco_workers, const std::string &juniper_workers, + const std::string &nokia_workers, const std::string &huawei_workers, const std::string &replies_cisco, const std::string &replies_juniper, + const std::string &replies_nokia, const std::string &replies_huawei, // logging const std::string &syslog, @@ -65,6 +68,9 @@ class CfgWrapper { void set_ipv4_socket_juniper(const std::string &ipv4_socket_juniper) { this->ipv4_socket_juniper = ipv4_socket_juniper; }; + void set_ipv4_socket_nokia(const std::string &ipv4_socket_nokia) { + this->ipv4_socket_nokia = ipv4_socket_nokia; + }; void set_ipv4_socket_huawei(const std::string &ipv4_socket_huawei) { this->ipv4_socket_huawei = ipv4_socket_huawei; }; @@ -77,6 +83,9 @@ class CfgWrapper { void set_juniper_workers(const std::string &juniper_workers) { this->juniper_workers = juniper_workers; }; + void set_nokia_workers(const std::string &nokia_workers) { + this->nokia_workers = nokia_workers; + }; void set_huawei_workers(const std::string &huawei_workers) { this->huawei_workers = huawei_workers; }; @@ -86,6 +95,9 @@ class CfgWrapper { void set_replies_juniper(const std::string &replies_juniper) { this->replies_juniper = replies_juniper; }; + void set_replies_nokia(const std::string &replies_nokia) { + this->replies_nokia = replies_nokia; + }; void set_replies_huawei(const std::string &replies_huawei) { this->replies_huawei = replies_huawei; }; @@ -128,13 +140,16 @@ class CfgWrapper { std::string &get_ipv4_socket_cisco() { return this->ipv4_socket_cisco; }; std::string &get_ipv4_socket_juniper() { return this->ipv4_socket_juniper; }; + std::string &get_ipv4_socket_nokia() { return this->ipv4_socket_nokia; }; std::string &get_ipv4_socket_huawei() { return this->ipv4_socket_huawei; }; //std::string &get_core_pid_folder() { return this->core_pid_folder; }; std::string &get_cisco_workers() { return this->cisco_workers; }; std::string &get_juniper_workers() { return this->juniper_workers; }; + std::string &get_nokia_workers() { return this->nokia_workers; }; std::string &get_huawei_workers() { return this->huawei_workers; }; std::string &get_replies_cisco() { return this->replies_cisco; }; std::string &get_replies_juniper() { return this->replies_juniper; }; + std::string &get_replies_nokia() { return this->replies_nokia; }; std::string &get_replies_huawei() { return this->replies_huawei; }; std::string &get_syslog() { return this->syslog; }; std::string &get_syslog_facility() { return this->syslog_facility; }; @@ -155,13 +170,16 @@ class CfgWrapper { std::string iface; std::string ipv4_socket_cisco; std::string ipv4_socket_juniper; + std::string ipv4_socket_nokia; std::string ipv4_socket_huawei; //std::string core_pid_folder; std::string cisco_workers; std::string juniper_workers; + std::string nokia_workers; std::string huawei_workers; std::string replies_cisco; std::string replies_juniper; + std::string replies_nokia; std::string replies_huawei; // logging std::string syslog; diff --git a/src/core/mdt_dialout_core.cc b/src/core/mdt_dialout_core.cc index 8a05f2a..4875f7a 100644 --- a/src/core/mdt_dialout_core.cc +++ b/src/core/mdt_dialout_core.cc @@ -162,6 +162,23 @@ void Srv::JuniperBind(std::string juniper_srv_socket) Srv::JuniperFsmCtrl(); } +void Srv::NokiaBind(std::string nokia_srv_socket) +{ + grpc::ServerBuilder nokia_builder; + // --- Required for socket manipulation --- + std::unique_ptr + jsbo(new ServerBuilderOptionImpl()); + nokia_builder.SetOption(std::move(jsbo)); + // --- Required for socket manipulation --- + nokia_builder.RegisterService(&nokia_service_); + nokia_builder.AddListeningPort(nokia_srv_socket, + grpc::InsecureServerCredentials()); + nokia_cq_ = nokia_builder.AddCompletionQueue(); + nokia_server_ = nokia_builder.BuildAndStart(); + + Srv::NokiaFsmCtrl(); +} + void Srv::HuaweiBind(std::string huawei_srv_socket) { grpc::ServerBuilder huawei_builder; @@ -283,7 +300,7 @@ void Srv::JuniperFsmCtrl() zmq_pusher.~ZmqPush(); } else { spdlog::get("multi-logger")-> - error("[Srv::CiscoFsmCtrl()]: Unable to set the " + error("[Srv::JuniperFsmCtrl()]: Unable to set the " "delivery function"); std::abort(); } @@ -319,6 +336,74 @@ void Srv::JuniperFsmCtrl() } } +void Srv::NokiaFsmCtrl() +{ + auto tid = std::this_thread::get_id(); + std::stringstream stid; + stid << tid; + spdlog::get("multi-logger")->debug("Srv::NokiaFsmCtrl() - Thread-ID: {}", + stid.str()); + + const std::string ddm = main_cfg_parameters.at("data_delivery_method"); + + DataManipulation data_manipulation; + DataWrapper data_wrapper; + + // Kafka producer + KafkaDelivery kafka_delivery; + const kafka::Properties kproperties = kafka_delivery.get_properties(); + kafka::clients::KafkaProducer kafka_producer(kproperties); + + // Zmq pusher + ZmqPush zmq_pusher; + const std::string zmq_uri = zmq_pusher.get_zmq_transport_uri(); + zmq::socket_t zmq_sock(zmq_pusher.get_zmq_ctx(), + zmq::socket_type::push); + zmq_sock.connect(zmq_uri); + + if (ddm.compare("kafka") != 0) { + kafka_producer.~KafkaProducer(); + kafka_delivery.~KafkaDelivery(); + } else if (ddm.compare("zmq") != 0) { + zmq_sock.~socket_t(); + zmq_pusher.~ZmqPush(); + } else { + spdlog::get("multi-logger")-> + error("[Srv::NokiaFsmCtrl()]: Unable to set the " + "delivery function"); + std::abort(); + } + + std::unique_ptr nokia_sstream( + new Srv::NokiaStream(&nokia_service_, nokia_cq_.get())); + nokia_sstream->Start(label_map, data_manipulation, data_wrapper, + kafka_delivery, kafka_producer, zmq_pusher, zmq_sock, zmq_uri); + //int nokia_counter {0}; + void *nokia_tag {nullptr}; + bool nokia_ok {false}; + while (true) { + //std::cout << "Nokia: " << nokia_counter << "\n"; + GPR_ASSERT(nokia_cq_->Next(&nokia_tag, &nokia_ok)); + //GPR_ASSERT(nokia_ok); + if (nokia_ok == false) { + spdlog::get("multi-logger")-> + warn("[NokiaFsmCtrl][grpc::CompletionQueue] " + "unsuccessful event"); + continue; + } + static_cast(nokia_tag)->Srv::NokiaStream::Start( + label_map, data_manipulation, data_wrapper, kafka_delivery, + kafka_producer, zmq_pusher, zmq_sock, zmq_uri); + //nokia_counter++; + } + + if (ddm.compare("kafka") == 0) { + kafka_producer.close(); + } else if (ddm.compare("zmq") == 0) { + zmq_sock.close(); + } +} + void Srv::HuaweiFsmCtrl() { auto tid = std::this_thread::get_id(); @@ -354,7 +439,7 @@ void Srv::HuaweiFsmCtrl() zmq_pusher.~ZmqPush(); } else { spdlog::get("multi-logger")-> - error("[Srv::CiscoFsmCtrl()]: Unable to set the " + error("[Srv::HuaweiFsmCtrl()]: Unable to set the " "delivery function"); std::abort(); } @@ -418,6 +503,20 @@ Srv::JuniperStream::JuniperStream( spdlog::get("multi-logger")->debug("constructor: JuniperStream()"); } +Srv::NokiaStream::NokiaStream( + Nokia::SROS::DialoutTelemetry::AsyncService *nokia_service, + grpc::ServerCompletionQueue *nokia_cq) : + nokia_service_ {nokia_service}, + nokia_cq_ {nokia_cq}, + nokia_resp {&nokia_server_ctx}, + nokia_replies_sent {0}, + kNokiaMaxReplies + {std::stoi(main_cfg_parameters.at("replies_nokia"))}, + nokia_stream_status {START} +{ + spdlog::get("multi-logger")->debug("constructor: NokiaStream()"); +} + Srv::HuaweiStream::HuaweiStream( huawei_dialout::gRPCDataservice::AsyncService *huawei_service, grpc::ServerCompletionQueue *huawei_cq) : @@ -991,6 +1090,163 @@ void Srv::JuniperStream::Start( } } +void Srv::NokiaStream::Start( + std::unordered_map> &label_map, + DataManipulation &data_manipulation, + DataWrapper &data_wrapper, + KafkaDelivery &kafka_delivery, + kafka::clients::KafkaProducer &kafka_producer, + ZmqPush &zmq_pusher, + zmq::socket_t &zmq_sock, + const std::string &zmq_uri) +{ + const std::string ddm = main_cfg_parameters.at("data_delivery_method"); + + Srv::NokiaStream *nokia_sstream = + new Srv::NokiaStream(nokia_service_, nokia_cq_); + + // Initial stream_status set to START @constructor + if (nokia_stream_status == START) { + nokia_service_->RequestPublish( + &nokia_server_ctx, + &nokia_resp, + nokia_cq_, + nokia_cq_, + this); + nokia_stream_status = FLOW; + } else if (nokia_stream_status == FLOW) { + spdlog::get("multi-logger")->debug("[NokiaStream::Start()] " + "new Srv::NokiaStream() {}", nokia_server_ctx.peer()); + nokia_sstream->Start(label_map, data_manipulation, data_wrapper, + kafka_delivery, kafka_producer, zmq_pusher, zmq_sock, zmq_uri); + nokia_resp.Read(&nokia_stream, this); + nokia_stream_status = PROCESSING; + nokia_replies_sent++; + } else if (nokia_stream_status == PROCESSING) { + if (nokia_replies_sent == kNokiaMaxReplies) { + spdlog::get("multi-logger")->debug("[NokiaStream::Start()] " + "nokia_stream_status = END"); + nokia_stream_status = END; + nokia_resp.Finish(grpc::Status::OK, this); + if (nokia_sstream) { + delete nokia_sstream; + nokia_sstream = nullptr; + } + } else { + auto tid = std::this_thread::get_id(); + std::stringstream stid; + stid << tid; + spdlog::get("multi-logger")->debug( + "Srv::NokiaStream::Start() - Thread-ID: {}", + stid.str()); + // From the network + std::string stream_data_in; + // After meta-data + std::string stream_data_out_meta; + // After data enrichment + std::string stream_data_out; + std::string json_str_out; + const std::string _peer = nokia_server_ctx.peer(); + // select exclusively the IP addr/port from peer + int d1 = (_peer.find_first_of(":") + 1); + int d2 = _peer.find_last_of(":"); + const std::string peer_ip = _peer.substr(d1, (d2 - d1)); + const std::string peer_port = _peer.substr( + (d2 + 1), ((_peer.npos - 1) - (d2 + 1))); + + Json::Value root; + + // the key-word "this" is used as a unique TAG + nokia_resp.Read(&nokia_stream, this); + + if (data_manipulation.NokiaUpdate(nokia_stream, json_str_out, + root) == true) { + spdlog::get("multi-logger")-> + info("[NokiaStream::Start()] {} " + "NokiaExtension, parsing successful", peer_ip); + } else { + spdlog::get("multi-logger")-> + error("[NokiaStream::Start()] {} " + "NokiaExtension, parsing failure", peer_ip); + } + + stream_data_in = json_str_out; + + // Data enrichment with label (node_id/platform_id) + if (data_manipulation_cfg_parameters.at( + "enable_label_encode_as_map").compare("true") == 0 || + data_manipulation_cfg_parameters.at( + "enable_label_encode_as_map_ptm").compare("true") == 0) { + if (data_manipulation.MetaData( + stream_data_in, + peer_ip, + peer_port, + stream_data_out_meta) == true && + data_manipulation.AppendLabelMap( + label_map, + peer_ip, + stream_data_out_meta, + stream_data_out) == true) { + if (ddm.compare("kafka") == 0) { + kafka_delivery.AsyncKafkaProducer( + kafka_producer, + peer_ip, + stream_data_out); + } else if (ddm.compare("zmq") == 0) { + data_wrapper.BuildDataWrapper ( + "gRPC", + "json_string", + main_cfg_parameters.at("writer_id"), + peer_ip, + peer_port, + stream_data_in); + zmq_pusher.ZmqPusher( + data_wrapper, + zmq_sock, + zmq_pusher.get_zmq_transport_uri()); + } + } + } else { + if (data_manipulation.MetaData( + stream_data_in, + peer_ip, + peer_port, + stream_data_out_meta) == true) { + if (ddm.compare("kafka") == 0) { + kafka_delivery.AsyncKafkaProducer( + kafka_producer, + peer_ip, + stream_data_out_meta); + } else if (ddm.compare("zmq") == 0) { + data_wrapper.BuildDataWrapper ( + "gRPC", + "json_string", + main_cfg_parameters.at("writer_id"), + peer_ip, + peer_port, + stream_data_in); + zmq_pusher.ZmqPusher( + data_wrapper, + zmq_sock, + zmq_pusher.get_zmq_transport_uri()); + } + } + } + nokia_stream_status = PROCESSING; + nokia_replies_sent++; + if (nokia_sstream) { + delete nokia_sstream; + nokia_sstream = nullptr; + } + } + } else { + spdlog::get("multi-logger")->debug("[NokiaStream::Start()] " + "GPR_ASSERT(nokia_stream_status == END)"); + GPR_ASSERT(nokia_stream_status == END); + delete this; + } +} + void Srv::HuaweiStream::Start( std::unordered_map> &label_map, DataManipulation &data_manipulation, diff --git a/src/core/mdt_dialout_core.h b/src/core/mdt_dialout_core.h index e37c36c..4c02e3b 100644 --- a/src/core/mdt_dialout_core.h +++ b/src/core/mdt_dialout_core.h @@ -18,6 +18,8 @@ #include "proto/Huawei/huawei_dialout.grpc.pb.h" #include "proto/Juniper/juniper_dialout.grpc.pb.h" #include "proto/Juniper/juniper_gnmi.pb.h" +#include "proto/Nokia/nokia_dialout.grpc.pb.h" +#include "proto/Nokia/nokia_gnmi.pb.h" #include "../dataManipulation/data_manipulation.h" #include "../dataWrapper/data_wrapper.h" #include "../dataDelivery/kafka_delivery.h" @@ -58,28 +60,35 @@ class Srv final { spdlog::get("multi-logger")->debug("destructor: ~Srv()"); cisco_server_->grpc::ServerInterface::Shutdown(); juniper_server_->grpc::ServerInterface::Shutdown(); + nokia_server_->grpc::ServerInterface::Shutdown(); huawei_server_->grpc::ServerInterface::Shutdown(); cisco_cq_->grpc::ServerCompletionQueue::Shutdown(); juniper_cq_->grpc::ServerCompletionQueue::Shutdown(); + nokia_cq_->grpc::ServerCompletionQueue::Shutdown(); huawei_cq_->grpc::ServerCompletionQueue::Shutdown(); } Srv() { spdlog::get("multi-logger")->debug("constructor: Srv()"); }; void CiscoBind(std::string cisco_srv_socket); void JuniperBind(std::string juniper_srv_socket); + void NokiaBind(std::string nokia_srv_socket); void HuaweiBind(std::string huawei_srv_socket); private: mdt_dialout::gRPCMdtDialout::AsyncService cisco_service_; Subscriber::AsyncService juniper_service_; + Nokia::SROS::DialoutTelemetry::AsyncService nokia_service_; huawei_dialout::gRPCDataservice::AsyncService huawei_service_; std::unique_ptr cisco_cq_; std::unique_ptr juniper_cq_; + std::unique_ptr nokia_cq_; std::unique_ptr huawei_cq_; std::unique_ptr cisco_server_; std::unique_ptr juniper_server_; + std::unique_ptr nokia_server_; std::unique_ptr huawei_server_; void CiscoFsmCtrl(); void JuniperFsmCtrl(); + void NokiaFsmCtrl(); void HuaweiFsmCtrl(); class CiscoStream { @@ -138,15 +147,47 @@ class Srv final { Subscriber::AsyncService *juniper_service_; grpc::ServerCompletionQueue *juniper_cq_; grpc::ServerContext juniper_server_ctx; - gnmi::SubscribeResponse juniper_stream; - grpc::ServerAsyncReaderWriter juniper_resp; + juniper_gnmi::SubscribeResponse juniper_stream; + grpc::ServerAsyncReaderWriter juniper_resp; int juniper_replies_sent; const int kJuniperMaxReplies; enum StreamStatus { START, FLOW, PROCESSING, END }; StreamStatus juniper_stream_status; }; + class NokiaStream { + public: + ~NokiaStream() { + spdlog::get("multi-logger")-> + debug("destructor: ~NokiaStream()"); }; + NokiaStream( + Nokia::SROS::DialoutTelemetry::AsyncService *nokia_service, + grpc::ServerCompletionQueue *nokia_cq); + void Start( + std::unordered_map> + &label_map, + DataManipulation &data_manipulation, + DataWrapper &data_wrapper, + KafkaDelivery &kafka_delivery, + kafka::clients::KafkaProducer &kafka_producer, + ZmqPush &zmq_pusher, + zmq::socket_t &zmq_sock, + const std::string &zmq_uri + ); + private: + Nokia::SROS::DialoutTelemetry::AsyncService *nokia_service_; + grpc::ServerCompletionQueue *nokia_cq_; + grpc::ServerContext nokia_server_ctx; + nokia_gnmi::SubscribeResponse nokia_stream; + grpc::ServerAsyncReaderWriter nokia_resp; + int nokia_replies_sent; + const int kNokiaMaxReplies; + enum StreamStatus { START, FLOW, PROCESSING, END }; + StreamStatus nokia_stream_status; + }; + class HuaweiStream { public: ~HuaweiStream() { diff --git a/src/dataManipulation/data_manipulation.cc b/src/dataManipulation/data_manipulation.cc index ac892c8..976609f 100644 --- a/src/dataManipulation/data_manipulation.cc +++ b/src/dataManipulation/data_manipulation.cc @@ -214,7 +214,7 @@ Json::Value DataManipulation::CiscoGpbkvField2Json( // 1. Decode & Extract mata-data & Add to JSON-Obj (juniper_tlm_header_ext) // 2. From JSON-Obj to JSON-Str bool DataManipulation::JuniperExtension( - gnmi::SubscribeResponse &juniper_stream, + juniper_gnmi::SubscribeResponse &juniper_stream, GnmiJuniperTelemetryHeaderExtension &juniper_tlm_header_ext, Json::Value &root) { @@ -224,7 +224,7 @@ bool DataManipulation::JuniperExtension( for (const auto &ext : juniper_stream.extension()) { if (ext.has_registered_ext() && ext.registered_ext().id() == - gnmi_ext::ExtensionID::EID_JUNIPER_TELEMETRY_HEADER) { + juniper_gnmi_ext::ExtensionID::EID_JUNIPER_TELEMETRY_HEADER) { parsing_str = juniper_tlm_header_ext.ParseFromString( ext.registered_ext().msg()); @@ -253,7 +253,7 @@ bool DataManipulation::JuniperExtension( } // Generate the JSON-Str from the upadate msg -bool DataManipulation::JuniperUpdate(gnmi::SubscribeResponse &juniper_stream, +bool DataManipulation::JuniperUpdate(juniper_gnmi::SubscribeResponse &juniper_stream, std::string &json_str_out, Json::Value &root) { @@ -503,6 +503,134 @@ bool DataManipulation::JuniperUpdate(gnmi::SubscribeResponse &juniper_stream, return true; } + +// Generate the JSON-Str from the upadate msg +bool DataManipulation::NokiaUpdate(nokia_gnmi::SubscribeResponse &nokia_stream, + std::string &json_str_out, + Json::Value &root) +{ + // From the first update() generate the sensor_path + //SubscribeResponse + //---> bool sync_response = 3; + //---> Notification update = 1; + // ---> ( ) bool atomic = 6; + // ---> ( ) int64 timestamp = 1 + // ---> ( ) Path prefix = 2; + // ---> ( ) string origin = 2; + // ---> ( ) string target = 4; + // ---> (repeated) PathElem elem = 3; + // ---> string name = 1; + // ---> map key = 2; + + // sensor_path as JSON + if (nokia_stream.has_update()) { + const auto &nup = nokia_stream.update(); + std::uint64_t notification_timestamp = nup.timestamp(); + + // Log the full contents of the nup object using DebugString() if it's a protobuf object. + spdlog::get("multi-logger")->info("[NokiaDebug] Full nup object: {}", nup.DebugString()); + if (nokia_stream.extension_size() > 0) { + for (const auto& ext : nokia_stream.extension()) { + spdlog::get("multi-logger")->info("[NokiaDebug] Nokia extension object: {}", ext.DebugString()); + } + } else { + spdlog::get("multi-logger")->info("[NokiaDebug] No extensions found in the SubscribeResponse."); + } + int path_idx = 0; + Json::Value sensor_path(Json::arrayValue); + spdlog::get("multi-logger")->info("[NokiaDebug] Full nup.prefix() object: {}", nup.prefix().DebugString()); + + while (path_idx < nup.prefix().elem_size()) { + Json::Value path_element; + path_element["name"] = nup.prefix().elem().at(path_idx).name(); + + // handling paths with filters + if (nup.prefix().elem().at(path_idx).key_size() > 0) { + Json::Value filters; + for (const auto &[key, value] : + nup.prefix().elem().at(path_idx).key()) { + filters[key] = value; + } + path_element["filters"] = filters; + } + sensor_path.append(path_element); + path_idx++; + } + std::string collected_path; + + for (const auto& element : sensor_path) { + // Access the "name" field from the JSON object + if (element.isMember("name")) { + collected_path += "/" + element["name"].asString(); + } + // Check if "filters" exist and append them in square brackets. Uncomment to enable this logic. + /* + if (element.isMember("filters")) { + const Json::Value& filters = element["filters"]; + if (!filters.empty()) { + collected_path += "["; + // Assuming we can have multiple filters for one field in a sensor_path, we can remove the loop if that's not an existing scenario + bool first = true; + for (const auto& key : filters.getMemberNames()) { + if (!first) { + collected_path += ","; + } + collected_path += key + "=" + filters[key].asString(); + first = false; + } + collected_path += "]"; + } + }*/ + } + root["collected_path"] = collected_path; + root["sensor_path"] = sensor_path; + root["notification_timestamp"] = notification_timestamp; + + + std::string path; + Json::Value value; + for (const auto &_nup : nup.update()) { + //std::cout << "DebugString: " << _jup.path().Utf8DebugString() + // << "\n"; + int path_idx = 0; + path.clear(); + spdlog::get("multi-logger")->info("[NokiaDebug] Full nup.path() object: {}", _nup.path().DebugString()); + while (path_idx < _nup.path().elem_size()) { + //std::cout << _jup.path().elem().at(path_idx).name() + // << " ---> "; + path.append("/"); + path.append(_nup.path().elem().at(path_idx).name()); + path_idx++; + } + + value = _nup.val().json_ietf_val(); + //std::cout << value << "\n"; + root[path] = value; + } + } + + std::string raw_data; + google::protobuf::util::JsonPrintOptions opt; + opt.add_whitespace = false; + auto status = google::protobuf::util::MessageToJsonString(nokia_stream, &raw_data, opt); + if (!status.ok()) { + spdlog::get("multi-logger")->error("[NokiaDebug] Failed to convert protobuf to JSON: {}", status.ToString()); + } + // Log raw_data for debugging + spdlog::get("multi-logger")->debug("[NokiaDebug] raw_data: {}", raw_data); + // root["raw_data"] = raw_data; + + // Serialize the JSON value into a string + Json::StreamWriterBuilder builder_w; + builder_w["emitUTF8"] = true; + builder_w["indentation"] = ""; + const std::unique_ptr writer( + builder_w.newStreamWriter()); + json_str_out = Json::writeString(builder_w, root); + + return true; +} + // 1. Decode & Extract mata-data & Add to JSON-Obj (huawei_tlm) // 2. Decode & Extract payload (record = content_s) & Add to JSON-Obj (oc-if) // 3. From JSON-Obj to JSON-Str diff --git a/src/dataManipulation/data_manipulation.h b/src/dataManipulation/data_manipulation.h index a8d9e5b..1f80369 100644 --- a/src/dataManipulation/data_manipulation.h +++ b/src/dataManipulation/data_manipulation.h @@ -15,6 +15,7 @@ #include "proto/Juniper/juniper_gnmi.pb.h" #include "proto/Juniper/juniper_telemetry_header_extension.pb.h" #include "proto/Huawei/huawei_telemetry.pb.h" +#include "proto/Nokia/nokia_gnmi.pb.h" #include "proto/OpenConfig/openconfig_interfaces.pb.h" #include #include "../utils/logs_handler.h" @@ -46,10 +47,13 @@ class DataManipulation { std::string &json_str_out); Json::Value CiscoGpbkvField2Json( const cisco_telemetry::TelemetryField &field); - bool JuniperExtension(gnmi::SubscribeResponse &juniper_stream, + bool JuniperExtension(juniper_gnmi::SubscribeResponse &juniper_stream, GnmiJuniperTelemetryHeaderExtension &juniper_tlm_header_ext, Json::Value &root); - bool JuniperUpdate(gnmi::SubscribeResponse &juniper_stream, + bool JuniperUpdate(juniper_gnmi::SubscribeResponse &juniper_stream, + std::string &json_str_out, + Json::Value &root); + bool NokiaUpdate(nokia_gnmi::SubscribeResponse &nokia_stream, std::string &json_str_out, Json::Value &root); bool HuaweiGpbOpenconfigInterface( diff --git a/src/mdt_dialout_collector.cc b/src/mdt_dialout_collector.cc index b26e0a8..dcf805d 100644 --- a/src/mdt_dialout_collector.cc +++ b/src/mdt_dialout_collector.cc @@ -173,9 +173,16 @@ int main(int argc, char *argv[]) LoadLabelMapPreTagStyle(label_map, data_manipulation_cfg_parameters.at("label_map_ptm_path")); } - + spdlog::get("multi-logger")->debug( + "ipv4 sockets found in config:\n ipv4_socket_cisco: {},\n ipv4_socket_juniper: {},\n ipv4_socket_nokia: {},\n ipv4_socket_huawei: {}\n", + main_cfg_parameters.at("ipv4_socket_cisco"), + main_cfg_parameters.at("ipv4_socket_juniper"), + main_cfg_parameters.at("ipv4_socket_nokia"), + main_cfg_parameters.at("ipv4_socket_huawei") + ); if (main_cfg_parameters.at("ipv4_socket_cisco").empty() == true && main_cfg_parameters.at("ipv4_socket_juniper").empty() == true && + main_cfg_parameters.at("ipv4_socket_nokia").empty() == true && main_cfg_parameters.at("ipv4_socket_huawei").empty() == true) { spdlog::get("multi-logger")-> error("[ipv4_socket_*] configuration issue: " @@ -198,6 +205,7 @@ int main(int argc, char *argv[]) std::vector cisco_workers; std::vector juniper_workers; + std::vector nokia_workers; std::vector huawei_workers; // Cisco @@ -208,6 +216,10 @@ int main(int argc, char *argv[]) LoadThreads(juniper_workers, "ipv4_socket_juniper", "replies_juniper", "juniper_workers"); + // Nokia + LoadThreads(nokia_workers, "ipv4_socket_nokia", "replies_nokia", + "nokia_workers"); + // Huawei LoadThreads(huawei_workers, "ipv4_socket_huawei", "replies_huawei", "huawei_workers"); @@ -216,6 +228,7 @@ int main(int argc, char *argv[]) //std::cout << "CISCO_WORKERS: " << cisco_workers.size() << "\n"; //std::cout << "JUNIPER_WORKERS: " << juniper_workers.size() << "\n"; + //std::cout << "NOKIA_WORKERS: " << nokia_workers.size() << "\n"; //std::cout << "HUAWEI_WORKERS: " << huawei_workers.size() << "\n"; for (std::thread &w : cisco_workers) { @@ -230,6 +243,12 @@ int main(int argc, char *argv[]) } } + for (std::thread &w : nokia_workers) { + if(w.joinable()) { + w.join(); + } + } + for (std::thread &w : huawei_workers) { if(w.joinable()) { w.join(); @@ -271,6 +290,13 @@ void *VendorThread(const std::string &ipv4_socket_str) std::string juniper_srv_socket {ipv4_socket_juniper}; Srv juniper_mdt_dialout_collector; juniper_mdt_dialout_collector.JuniperBind(juniper_srv_socket); + } else if (ipv4_socket_str.find("nokia") != std::string::npos) { + std::string ipv4_socket_nokia = + main_cfg_parameters.at(ipv4_socket_str); + + std::string nokia_srv_socket {ipv4_socket_nokia}; + Srv nokia_mdt_dialout_collector; + nokia_mdt_dialout_collector.NokiaBind(nokia_srv_socket); } else if (ipv4_socket_str.find("huawei") != std::string::npos) { std::string ipv4_socket_huawei = main_cfg_parameters.at(ipv4_socket_str); diff --git a/src/proto/Makefile.am b/src/proto/Makefile.am index f2b01b9..5b25eb0 100644 --- a/src/proto/Makefile.am +++ b/src/proto/Makefile.am @@ -21,6 +21,12 @@ libgrpc_collector_proto_la_SOURCES = \ Juniper/juniper_telemetry_header_extension.grpc.pb.cc \ Juniper/juniper_gnmi.grpc.pb.cc \ Juniper/juniper_dialout.grpc.pb.cc \ + Nokia/nokia_dialout.pb.cc \ + Nokia/nokia_gnmi.pb.cc \ + Nokia/nokia_gnmi_ext.pb.cc \ + Nokia/nokia_gnmi_ext.grpc.pb.cc \ + Nokia/nokia_gnmi.grpc.pb.cc \ + Nokia/nokia_dialout.grpc.pb.cc \ OpenConfig/openconfig_interfaces.pb.cc \ OpenConfig/openconfig_interfaces.grpc.pb.cc diff --git a/src/proto/Nokia/README.md b/src/proto/Nokia/README.md new file mode 100644 index 0000000..3807f5c --- /dev/null +++ b/src/proto/Nokia/README.md @@ -0,0 +1 @@ +#### Nokia's protobuf & gRPC dialout helper classes \ No newline at end of file diff --git a/src/utils/cfg_handler.cc b/src/utils/cfg_handler.cc index 1956aaa..4ecf99b 100644 --- a/src/utils/cfg_handler.cc +++ b/src/utils/cfg_handler.cc @@ -365,6 +365,32 @@ bool MainCfgHandler::lookup_main_parameters(const std::string &cfg_path, params.insert({"ipv4_socket_juniper", ""}); } + std::string ipv4_socket_nokia_s; + bool ipv4_socket_nokia = main_params.exists("ipv4_socket_nokia"); + if (ipv4_socket_nokia == true) { + libconfig::Setting &ipv4_socket_nokia = + main_params.lookup("ipv4_socket_nokia"); + try { + ipv4_socket_nokia_s = ipv4_socket_nokia.c_str(); + if (ipv4_socket_nokia_s.empty() == false) { + params.insert({"ipv4_socket_nokia", ipv4_socket_nokia_s}); + } else { + spdlog::get("multi-logger")-> + error("[ipv4_socket_nokia] configuration " + "issue: [ {} ] is an invalid socket", + ipv4_socket_nokia_s); + return false; + } + } catch (const libconfig::SettingTypeException &ste) { + spdlog::get("multi-logger")-> + error("[ipv4_socket_nokia] configuration issue: " + "{}", ste.what()); + return false; + } + } else { + params.insert({"ipv4_socket_nokia", ""}); + } + std::string ipv4_socket_huawei_s; bool ipv4_socket_huawei = main_params.exists("ipv4_socket_huawei"); if (ipv4_socket_huawei == true) { @@ -445,6 +471,33 @@ bool MainCfgHandler::lookup_main_parameters(const std::string &cfg_path, } } + if (ipv4_socket_nokia_s.empty() == false) { + bool replies_nokia = main_params.exists("replies_nokia"); + if (replies_nokia == true) { + libconfig::Setting &replies_nokia = + main_params.lookup("replies_nokia"); + try { + std::string replies_nokia_s = replies_nokia; + if (replies_nokia_s.empty() == false) { + params.insert({"replies_nokia", replies_nokia_s}); + } else { + spdlog::get("multi-logger")-> + error("[replies_nokia] configuration " + "issue: [ {} ] is an invalid # of replies", + replies_nokia_s); + return false; + } + } catch (const libconfig::SettingTypeException &ste) { + spdlog::get("multi-logger")-> + error("[replies_nokia] configuration issue: " + "{}", ste.what()); + return false; + } + } else { + params.insert({"replies_nokia", "0"}); + } + } + if (ipv4_socket_huawei_s.empty() == false) { bool replies_huawei = main_params.exists("replies_huawei"); if (replies_huawei == true) { @@ -526,6 +579,33 @@ bool MainCfgHandler::lookup_main_parameters(const std::string &cfg_path, } } + if (ipv4_socket_nokia_s.empty() == false) { + bool nokia_workers = main_params.exists("nokia_workers"); + if (nokia_workers == true) { + libconfig::Setting &nokia_workers = + main_params.lookup("nokia_workers"); + try { + std::string nokia_workers_s = nokia_workers; + if (nokia_workers_s.empty() == false) { + params.insert({"nokia_workers", nokia_workers_s}); + } else { + spdlog::get("multi-logger")-> + error("[nokia_workers] configuration " + "issue: [ {} ] is an invalid # of replies", + nokia_workers_s); + return false; + } + } catch (const libconfig::SettingTypeException &ste) { + spdlog::get("multi-logger")-> + error("[nokia_workers] configuration issue: " + "{}", ste.what()); + return false; + } + } else { + params.insert({"nokia_workers", "1"}); + } + } + if (ipv4_socket_huawei_s.empty() == false) { bool huawei_workers = main_params.exists("huawei_workers"); if (huawei_workers == true) { diff --git a/src/utils/cfg_handler.h b/src/utils/cfg_handler.h index a69d893..bf07a6c 100644 --- a/src/utils/cfg_handler.h +++ b/src/utils/cfg_handler.h @@ -134,12 +134,15 @@ class MainCfgHandler { const std::string iface; const std::string ipv4_socket_cisco; const std::string ipv4_socket_juniper; + const std::string ipv4_socket_nokia; const std::string ipv4_socket_huawei; const std::string replies_cisco; const std::string replies_juniper; + const std::string replies_nokia; const std::string replies_huawei; const std::string cisco_workers; const std::string juniper_workers; + const std::string nokia_workers; const std::string huawei_workers; const std::string data_delivery_method; const std::string so_bindtodevice_check;