diff --git a/meta/Makefile b/meta/Makefile index 870752cf4..95519f04f 100644 --- a/meta/Makefile +++ b/meta/Makefile @@ -129,6 +129,11 @@ saimetadatatest.c saimetadata.c saimetadata.h: xml $(XMLDEPS) parse.pl $(CONSTHE RPC_MODULES=$(shell find rpc -type f -name "*.pm") +sai.proto: xml $(XMLDEPS) gensaiprotorpc.pl templates/*.tt $(RPC_MODULES) + perl -Irpc gensaiprotorpc.pl $(GEN_SAIRPC_OPTS) + +protorpc: sai.proto + sai.thrift sai_rpc_server.cpp sai_adapter.py: xml $(XMLDEPS) gensairpc.pl templates/*.tt $(RPC_MODULES) perl -Irpc gensairpc.pl $(GEN_SAIRPC_OPTS) @@ -180,18 +185,18 @@ generated/gen-cpp/sai_rpc_server.skeleton: sai.thrift $(RPC_OBJ) sai_rpc_server.skeleton: generated/gen-cpp/sai_rpc_server.skeleton -sai_rpc_frontend: rpc sai_rpc_frontend.cpp sai_rpc_frontend.main.cpp sai_rpc_server.cpp libsaimetadata.so libsai.so +sai_rpc_frontend: rpc protorpc sai_rpc_frontend.cpp sai_rpc_frontend.main.cpp sai_rpc_server.cpp libsaimetadata.so libsai.so $(CXX) $(CFLAGS) -std=c++11 \ generated/gen-cpp/sai_rpc.o generated/gen-cpp/sai_types.o generated/gen-cpp/sai_constants.o \ sai_rpc_frontend.main.cpp sai_rpc_frontend.cpp \ libsaimetadata.so libsai.so -lthrift -lpthread -I generated/gen-cpp -o sai_rpc_frontend +.PHONY: clean rpc protorpc -.PHONY: clean rpc clean: rm -f *.o *~ .*~ *.tmp .*.swp .*.swo *.bak sai*.gv sai*.svg *.o.symbols doxygen*.db *.so rm -f saimetadata.h saimetadatasize.h saimetadata.c saimetadatatest.c saiswig.i rm -f saisanitycheck saimetadatatest saiserializetest saidepgraphgen sai_rpc_frontend - rm -f sai.thrift sai_rpc_server.cpp sai_adapter.py + rm -f sai.thrift sai_rpc_server.cpp sai_adapter.py sai.proto rm -f *.gcda *.gcno *.gcov - rm -rf xml html dist temp generated + rm -rf xml html dist temp generated generated-protobuf diff --git a/meta/gensaiprotorpc.README b/meta/gensaiprotorpc.README new file mode 100644 index 000000000..cd85a438c --- /dev/null +++ b/meta/gensaiprotorpc.README @@ -0,0 +1,38 @@ +NAME + gensaiprotorpc.pl - generate protobuf interface of SAI + +DESCRIPTION + This script generates GRPC interface of SAI function. It + should be used after SAI interface update. + + For more details see the development documentation (rpc/README.md). + +USAGE + In order to run, just call: + + ./gensaiprotorpc.pl + + Before committing it is a good idea to clean before generation: + + ./gensaiprotorpc.pl --clean-meta + + In order to generate debug files and avoid cleanup, use --dbg or --dump + flags. Use --experimental to generate extensions and experimental code. + + For more information, use --help: + + ./gensaiprotorpc.pl --help + +DEPENDENCIES + Ubuntu packages: + ``` + [sudo] apt-get install -y \ + libtemplate-perl \ + libconst-fast-perl \ + libmoosex-aliases-perl \ + libnamespace-autoclean-perl \ + libgetopt-long-descriptive-perl \ + doxygen \ + graphviz \ + aspell-en +``` diff --git a/meta/gensaiprotorpc.pl b/meta/gensaiprotorpc.pl new file mode 100755 index 000000000..5fb9e8a5b --- /dev/null +++ b/meta/gensaiprotorpc.pl @@ -0,0 +1,529 @@ +#!/usr/bin/env perl +# +# Copyright (c) 2021 Microsoft Open Technologies, Inc. +# +# 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 +# +# THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT +# LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS +# FOR A PARTICULAR PURPOSE, MERCHANTABILITY OR NON-INFRINGEMENT. +# +# See the Apache Version 2.0 License for specific language governing +# permissions and limitations under the License. +# +# Microsoft would like to thank the following companies for their review and +# assistance with these files: Intel Corporation, Mellanox Technologies Ltd, +# Dell Products, L.P., Facebook, Inc., Marvell International Ltd. +# +# @file gensaiprotorpc.pl +# +# @brief This module generates RPC interface of SAI for PTF +# + +use strict; +use warnings; +use diagnostics; + +use 5.020; + +use File::Spec::Functions qw(catdir catfile rel2abs); +use File::Basename qw(dirname); +use English qw(-no_match_vars); +use Getopt::Long::Descriptive; +use File::Path qw(rmtree); +use Term::ANSIColor; +use Cwd qw(getcwd); +use Data::Dumper; +use Const::Fast; +use File::Copy; +use Template; +use Carp; +use Tie::File; + +use lib catdir( dirname( rel2abs(__FILE__) ), 'rpc' ); + +use Utils::Format; +use Utils; + +use SAI::Function::Argument; +use SAI::Struct::Member; +use SAI::Function; +use SAI::Typedef; +use SAI::Struct; +use SAI::Attrs; +use SAI::Stats; +use SAI::Type; + +# Avoid warnings related to given/when +no if $] >= 5.018, warnings => 'experimental::smartmatch'; + +# Consts +# TODO: move some of them to common package (e.g. PREFIX or RETVAL) so that they can be shared +const my $PREFIX => 'sai_protobuf_'; +const my $GEN_DIR => 'generated-protobuf'; +const my $COUNTER => -1; +const my $RETVAL => 0; + +# Keep the dbg data sorted, to make it comparable +$Data::Dumper::Sortkeys = 1; + +my $run_dir = getcwd; +my $script_dir = dirname( rel2abs($PROGRAM_NAME) ); +my $script_path = rel2abs(__FILE__); +my $gen_dir = catdir( $script_dir, $GEN_DIR ); +my $templates_dir = + catdir( $ENV{PAR_TEMP} ? "$ENV{PAR_TEMP}/inc" : $script_dir, 'templates' ); + +# Parameters +my $sai_dir = catdir( dirname($script_dir) ); + +#<<< +( my $args, my $usage ) = describe_options( + "$PROGRAM_NAME %o", + [ 'dbg', 'Print debug information in generated files, implies --dump', { default => 0 } ], + [ 'experimental|e', 'Generate also experimental files', { default => 0 } ], + [ 'dist', 'Create the standalone \'gensairpc\' script (experimental)', { default => 0 } ], + [ 'dump|d', 'Dump all data to the file', { default => 0 } ], + [ 'clean-meta|c', 'Perform clean meta before generation', { default => 0 } ], + [ 'verbose|v', 'Print more details', { default => 0 } ], + [ 'mandatory-attrs', 'Make mandatory attributes obligatory in sai_adapter.py', { default => 0 } ], + [ 'dev-utils:s', 'Generate additional development utils within the generated code. Additional options: [=log,zero]', { default => 0 } ], + [ 'adapter_logger', 'Enable the logger in sai_adapter, it will log all the method invocation.', { default => 0} ], + [ 'attr-header', 'Generate additional header of attributes definitions (including object types)', { default => 0 } ], + [ 'help|h', 'Print this help', { shortcircuit => 1 } ], +); +#>>> + +if ( $args->help ) { + print $usage->text; + exit; +} + +if ( $args->dist ) { + Utils->dist($script_path); + exit; +} + +my $dbg = $args->dbg; +my $dump = $args->dump; +my $verbose = $args->verbose; +$dump = 1 if $dbg; +$verbose = 1 if $dump; +my $experimental = $args->experimental; +my $clean = $args->clean_meta; +my $mandatory_attrs = $args->mandatory_attrs; +my $dev_utils = ( $args->dev_utils ne q{} ? $args->dev_utils : 1 ); +my $adapter_logger = ( $args->adapter_logger ne q{} ? $args->adapter_logger : 1 ); +my $attr_header = $args->attr_header; + +# Configure SAI meta +my $sai_meta_dir = catdir( $sai_dir, 'meta' ); +my $sai_parse_path = catfile( $sai_meta_dir, 'parse.pl' ); + +-d $sai_dir or die "\"$sai_dir\" directory is invalid"; + +# Declare SAI meta global variables, its libraries need them +our $XMLDIR = catdir( $sai_meta_dir, 'xml' ); +our $INCLUDE_DIR = catdir( $sai_dir, 'inc' ); +our $EXPERIMENTAL_DIR = catdir( $sai_dir, 'experimental' ); + +# Include SAI meta libs. The intention is that SAI meta path +# can be changed by the parameter, so we cannot use 'use' +our @INC; +unshift @INC, $sai_meta_dir; + +require xmlutils; +xmlutils->import; + +require utils; +utils->import; + +if ($clean) { + say colored( "Removing \"$GEN_DIR\" directory...", 'bold blue' ); + rmtree $gen_dir; + say colored( 'Cleaning SAI meta...', 'bold blue' ); + system 'make clean -C ' . $sai_meta_dir; +} + +# Generate Doxygen xml files +say colored( 'Building SAI meta XML...', 'bold blue' ); +system 'make xml -C ' . $sai_meta_dir; + +say colored( 'Parsing...', 'bold blue' ); +my $data = get_definitions(); +my $vars = { + apis => $data->{apis}, + functions => $data->{functions}, + methods => $data->{methods}, + structs => $data->{structs}, + dbg => $dbg, + mandatory_attrs => $mandatory_attrs, + dev_utils => $dev_utils, + adapter_logger => $adapter_logger, + templates_dir => $templates_dir +}; + +mkdir $gen_dir; + +if ($dump) { + say colored( 'Generating sai_dbg.dump...', 'bold blue' ); + open my $dump_file, '>', catfile( $gen_dir, 'sai_dbg.dump' ) + or die 'Could not open the dump file'; + print {$dump_file} Dumper $data; + close $dump_file or die; +} + +my $template = Template->new( ABSOLUTE => 1 ); + +my $sai_proto_path = catfile($run_dir, 'sai.proto'); + +say colored( 'Generating sai.proto...', 'bold blue' ); +$template->process( catfile( $templates_dir, 'sai.protobuf.tt' ), + $vars, $sai_proto_path ) + or die $template->error(); + +# Tie the file to an array +tie my @lines, 'Tie::File', $sai_proto_path or die "Couldn't tie $sai_proto_path: $!"; + +# Replace 'sai_thrift_' with 'sai_protobuf_' in each line +foreach my $line (@lines) { + $line =~ s/sai_thrift_/sai_protobuf_/g; +} + +# Untie the file +untie @lines; + +say colored( 'Generating ProtoBuf files...', 'bold blue' ); +chdir $gen_dir; +system('protoc --cpp_out=. --proto_path=.. sai.proto') == 0 or die colored("Command failed: $!", "red"); +chdir $run_dir; + + +# Find the api name within the header related to the currenct XML file. +sub get_api_name +{ + # it may happen that metadata is already generated, and SAI directory is + # mounted in docker, then the location will be invalid inside docker, so + # let's use only filename and inc directory + + my $location = shift; + + $location =~ s!.+/!!; # leave only filename + + # The hash should be static - don't open and traverse the header + # if we already found the API name inside. + state %api_names = ( + "saimetadatalogger.h" => "common", + "saimetadatatypes.h" => "common", + "sai.h" => "common", + "saiobject.h" => "object", # for sai_object_key_entry_t + "saitypes.h" => "common" ); + + return $api_names{$location} if exists $api_names{$location}; + + my $file = "$sai_dir/inc/$location"; + + $file = "$sai_dir/experimental/$location" if $location =~ /experimental|extension/; + + open(H, '<', $file) or die "Failed to open: $file: $!"; + + my @lines = ; + + close H or croak; + + for my $line (@lines) + { + if ($line =~ /^typedef struct _sai_(\w+)_api_t/) + { + $api_names{$location} = $1; + + return $1; + } + } + + if ($location =~ /^sai\w*extensions.h$/) + { + $api_names{$location} = "common"; + + return "common"; + } + + die "File $file doesn't contain api struct!"; +} + +# The main function that parses all XML files and creates all +# types definitions. +sub get_definitions { + my %methods_table; + my %all_functions; + my %all_structs; + my %all_attrs; + my @all_enums; + my %apis; + my $i = 0; + + # Iterate over files + for ( GetSaiXmlFiles($XMLDIR) ) { + my $xml = ReadXml($_); + + # Iterate over definitions + for ( @{ $xml->{compounddef}[0]->{sectiondef} } ) { + if ( $_->{kind} eq 'typedef' or $_->{kind} eq 'enum' ) { + for ( @{ $_->{memberdef} } ) { + my $function; + my $typedef; + my $object; + my $struct; + my $attrs; + my $stats; + my $api; + + # Get API name + my $location = $_->{location}[0]->{file}; + unless ($experimental) { + next if $location =~ /(experimental|extension)/; + } + $api = get_api_name($location); + $apis{$api} = {} unless ref $apis{$api} eq 'HASH'; + + # Populate attribute list per object + next if get_object_types( $api, $apis{$api}, $_ ); + next if get_attributes( $apis{$api}, $_ ); + next if get_stats( $apis{$api}, $_ ); + + # Populate type definitions and function declarations + + next if get_typedef( $apis{$api}, $_, $api eq 'common' ); + + next + if get_struct( $apis{$api}, \%all_structs, + \%methods_table, $_ ); + + next + if $api ne 'common' + and + get_function( $apis{$api}, \%all_functions, $_, $api ); + + # Add enum name to the list + if ( $_->{kind} eq 'enum' ) { + my $enum_name = $_->{name}[0]; + $enum_name =~ s/^_//; + push @all_enums, $enum_name; + } + + } + } + } + } + + my $api_list = assign_attr_types( \%apis, \@all_enums ); + + return { + apis => $api_list, + attrs => \%all_attrs, + structs => \%all_structs, + functions => \%all_functions, + methods => \%methods_table + }; +} + +# To set or get attribute, the proper value struct field need to be used. +# Obtain the name of this field and assign it to the attribute. +# If the attribute is of enum type, then it is s32, otherwise the correct type +# should be found within sai_attribute_value_t fields. +sub get_attr_type { + my $attr = shift; + my $attr_types = shift; + my $all_enums = shift; + + my $type = $attr->type->protobuf_name; + + # First, check if we have enum + return 'int32' if ( $attr->type->name ~~ $all_enums ); + + # Try to compare types of attribute and attr value otherwise + for ( @{ $attr_types->members } ) { + return $_->protobuf_name if ( $type eq $_->type->protobuf_name ); + } + + carp colored( "Unknown type $type of attribute " . $attr->name, 'red' ); + return; +} + +# To set or get attribute, the proper value struct field need to be used. +# Obtain the name of this field and assign it to all attributes. +sub assign_attr_types { + my $apis = shift; + my $all_enums = shift; + + my $attr_types; + for ( @{ $apis->{common}->{structs} } ) { + $attr_types = $_ if $_->name eq 'sai_attribute_value_t'; + } + + croak unless $attr_types; + + for my $api ( values %{$apis} ) { + for my $object ( values %{ $api->{objects} } ) { + for my $attr ( $object->{attrs}->all ) { + $attr->typename( + get_attr_type( $attr, $attr_types, $all_enums ) ); + } + } + } + + return $apis; +} + +# Create and store the object type Enum +sub get_object_types { + my $api_name = shift; + my $api = shift; + my $enum = shift; + + return 0 + unless SAI::Enum->validate_xml_typedef($enum) + and $enum->{name}[0] =~ /object_type_t/; + my $object_types = SAI::Enum->new( xml_typedef => $enum ); + + if ($object_types) { + say "\"object_types\" added to \"$api_name\" hash" if $verbose; + $api->{object_types} = $object_types; + } + + return 1; +} + +# Create and store the Attrs object +sub get_attributes { + my $api = shift; + my $enum = shift; + + return 0 unless SAI::Attrs->validate_xml_typedef($enum); + my $attrs = SAI::Attrs->new( xml_typedef => $enum ); + + if ( $attrs and $attrs->object ) { + $api->{objects}->{ $attrs->object }->{attrs} = $attrs; + } + + return 1; +} + +# Create and store the Stats object +sub get_stats { + my $api = shift; + my $enum = shift; + + return 0 unless SAI::Stats->validate_xml_typedef($enum); + my $stats = SAI::Stats->new( xml_typedef => $enum ); + + if ( $stats and $stats->object ) { + $api->{objects}->{ $stats->object }->{stats} = $stats; + } + + return 1; +} + +# Create and store the Typedef object +sub get_typedef { + my $api = shift; + my $xml_typedef = shift; + my $raw = shift; + + # If $raw, the we can use raw thrift types only + $raw //= 0; + + return 0 unless SAI::Typedef->validate_xml_typedef($xml_typedef); + my $typedef = SAI::Typedef->new( + raw => $raw, + xml_typedef => $xml_typedef, + ); + + push @{ $api->{typedefs} }, $typedef; + + return 1; +} + +# In SAI RPC server we don't call SAI functions directly - pointers +# to them are stored in some structures, so we need to know their +# names. +sub get_method_names { + my $struct = shift; + + my $function; + my %methods; + + for my $method ( GetStructKeysInOrder($struct) ) { + my $type = + { SAI::Struct::Member->parse_xml_typedef( $struct->{$method} ) } + ->{type}; + if ( $type =~ /^(\w+)_fn$/ ) { $function = $1 } + else { next } + + $methods{$function} = $method; + } + + return \%methods; +} + +# Create and store the Struct object. +# The struct of API function pointers is an exception - just the its name. +sub get_struct { + my $api = shift; + my $all_structs = shift; + my $methods_table = shift; + my $xml_typedef = shift; + + my @members; + my $name; + + # Make sure we have a struct + return 0 unless SAI::Struct->validate_xml_typedef($xml_typedef); + + $name = { SAI::Struct->parse_xml_typedef($xml_typedef) }->{name}; + my %struct_def = ExtractStructInfo( $name, 'struct_' ); + + # If we have method table, then we don't create a structure - + # just take the method names + if ( $name =~ /_api_t$/ ) { + my $method_names = get_method_names( \%struct_def ); + %{$methods_table} = ( %{$methods_table}, %{$method_names} ); + return 1; + } + + push @members, SAI::Struct::Member->new( xml_typedef => $struct_def{$_} ) + for ( GetStructKeysInOrder( \%struct_def ) ); + + my $struct = SAI::Struct->new( + members => \@members, + xml_typedef => $xml_typedef, + ); + + push @{ $api->{structs} }, $struct; + $all_structs->{ $struct->protobuf_name } = $struct; + + return 1; +} + +# Create and store the Function object. +sub get_function { + my $api = shift; + my $all_functions = shift; + my $definition = shift; + my $api_name = shift; + + return 0 unless SAI::Function->validate_xml_typedef($definition); + my $function = SAI::Function->new( xml_typedef => $definition ); + + $function->api($api_name); + + push @{ $api->{functions} }, $function; + $all_functions->{ $function->name } = $function; + + return 1; +} + +__END__ diff --git a/meta/rpc/SAI/Function.pm b/meta/rpc/SAI/Function.pm index c3b25cb85..bdf48777f 100644 --- a/meta/rpc/SAI/Function.pm +++ b/meta/rpc/SAI/Function.pm @@ -57,7 +57,7 @@ has 'args' => ( has 'api' => ( is => 'rw', isa => 'Str' ); has 'dbg_info' => ( is => 'ro', isa => 'Str' ); -with 'SAI::RPC::ThriftName', 'SAI::Utils::XMLLoader', 'SAI::RPC::Function'; +with 'SAI::RPC::ThriftName', 'SAI::Utils::XMLLoader', 'SAI::RPC::Function', 'SAI::RPC::ProtoBufName'; ################## # Public methods # @@ -163,12 +163,11 @@ sub _find_count_arg { } elsif ( # Previous variable is uint32 - $prev_arg->type->thrift_name =~ /uint32_t/ - + ($prev_arg->type->thrift_name =~ /uint32_t/) # or list of uint32 (and we have pointer to pointer) or ( $arg->type->ptr == 2 and $prev_arg->is_list - and $prev_arg->type->subtype->thrift_name =~ /uint32_t/ ) + and $prev_arg->type->subtype->thrift_name =~ /uint32_t/) ) { # Note, that counter must be directly previous argument. diff --git a/meta/rpc/SAI/RPC/ProtoBufName.pm b/meta/rpc/SAI/RPC/ProtoBufName.pm new file mode 100644 index 000000000..798008871 --- /dev/null +++ b/meta/rpc/SAI/RPC/ProtoBufName.pm @@ -0,0 +1,59 @@ +# Copyright 2021-present Intel Corporation. +# +# 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. + +package SAI::RPC::ProtoBufName; + +use Const::Fast; + +use Moose::Role; + +const my $PREFIX => 'sai_protobuf_'; + +requires 'name'; + +########### +# Methods # +########### + +# Get the 'gpb' name equivalent +sub protobuf_name { + my $self = shift; + my $raw_name = shift; + my $name = shift; + + $name //= $self->name; + my $has_prefix = 0; + + if ( $name =~ /sai_(\w+)/ ) { + $name = $1; + $has_prefix = 1; + } + + # Remove keywords + # We replace them with subwords of type, otherwise we have no good replacement + my @words = + $self->can('type') + ? split /[>_]/, $self->type->name + : qw{array accessor}; + $name = $words[-1] . $name if $name eq 'set' or $name eq 'get'; + $name = $words[-2] . $name if $name eq 'list'; + + # Remove c++ keywords + $name = $words[-1] . $name if $name eq 'inline'; + + $raw_name = 0 if $has_prefix; + return ( $raw_name ? q{} : $PREFIX ) . $name; +} + +1; diff --git a/meta/rpc/SAI/RPC/ProtoBufName/Type.pm b/meta/rpc/SAI/RPC/ProtoBufName/Type.pm new file mode 100644 index 000000000..0da781848 --- /dev/null +++ b/meta/rpc/SAI/RPC/ProtoBufName/Type.pm @@ -0,0 +1,71 @@ +# Copyright 2021-present Intel Corporation. +# +# 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. + +package SAI::RPC::ProtoBufName::Type; + +use Moose::Role; + +requires 'is_attr_list'; + +with 'SAI::RPC::ProtoBufName'; + +########### +# Methods # +########### + +# Get the 'gpb' name equivalent +around protobuf_name => sub { + my $orig = shift; + my $self = shift; + my $raw_name = shift; + my $name = shift; + + # Surround the type with list<> + if ( $self->is_attr_list ) { + return 'sai_protobuf_attribute_list_t'; + } + else { + return 'repeated ' . $self->subtype->protobuf_name + if $self->is_list + and $self->subtype->name !~ /(?:void|char)/; + } + $name //= $self->name; + + # If we have a char array, then replace it with a string + $name = 'string' + if ( $name =~ /void/ and ( $self->is_list or $self->ptr ) ) + or $name =~ /char/; + + # Handle special types + $name = 'int32_t' if $name =~ /^enum(\s|$)/; + + # If $raw then convert the type to protobuf type + $name = "int32" if $raw_name and $name =~ /(u)?int32_t/; + $name = "int32" if $raw_name and $name =~ /(u)?int8_t/; + $name = "int32" if $raw_name and $name =~ /(u)?int16_t/; + $name = "int64" if $raw_name and $name =~ /(u)?int64_t/; + + # Replace any pointer to funtion with the single pointer type + $name = 'pointer_t' if ( $name =~ /_fn$/ ); + $name = 'int64' if $raw_name and ( $name =~ /size_t$/ or $self->ptr ); + + $raw_name = 1 if $name =~ /(?:string|bool|void)/; + + # Call the original function (add prefix etc) + $name = $self->$orig( @_, $raw_name, $name ); + + return $name; +}; + +1; diff --git a/meta/rpc/SAI/RPC/ProtoBufName/Variable.pm b/meta/rpc/SAI/RPC/ProtoBufName/Variable.pm new file mode 100644 index 000000000..47a82f7b7 --- /dev/null +++ b/meta/rpc/SAI/RPC/ProtoBufName/Variable.pm @@ -0,0 +1,34 @@ +# Copyright 2021-present Intel Corporation. +# +# 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. + +package SAI::RPC::ProtoBufName::Variable; + +use Moose::Role; + +with 'SAI::RPC::ProtoBufName'; + +########### +# Methods # +########### + +around protobuf_name => sub { + my $orig = shift; + my $self = shift; + + # Call the original function, disable prefix + return $self->$orig( @_, 1 ); + +}; + +1; diff --git a/meta/rpc/SAI/Struct.pm b/meta/rpc/SAI/Struct.pm index 1281ef062..25cd29174 100644 --- a/meta/rpc/SAI/Struct.pm +++ b/meta/rpc/SAI/Struct.pm @@ -22,7 +22,7 @@ use Moose; has 'name' => ( is => 'ro', required => 1 ); has 'members' => ( is => 'ro' ); -with 'SAI::RPC::ThriftName', 'SAI::Utils::XMLLoader'; +with 'SAI::RPC::ThriftName', 'SAI::RPC::ProtoBufName', 'SAI::Utils::XMLLoader'; ########### # Methods # diff --git a/meta/rpc/SAI/Typedef.pm b/meta/rpc/SAI/Typedef.pm index 149c502a9..35875e14f 100644 --- a/meta/rpc/SAI/Typedef.pm +++ b/meta/rpc/SAI/Typedef.pm @@ -62,6 +62,24 @@ sub thrift_def { return $name; } +sub protobuf_def { + my $self = shift; + + my $name = $self->type->protobuf_name( $self->raw ); + + # The special case - replace definition (like i64) + # with string - only for ip/mac addresses + $name = 'string' if $self->name =~ /(ip\d+|mac)_t$/; + + croak colored( + 'Circular type dependency ' + . $name . ' -> ' + . $self->type->protobuf_name . "\n", + 'bold red' + ) if $self->protobuf_name eq $name; + + return $name; +} ################ # TT coditions # ################ diff --git a/meta/rpc/SAI/Variable.pm b/meta/rpc/SAI/Variable.pm index 486d10e27..a143e837b 100644 --- a/meta/rpc/SAI/Variable.pm +++ b/meta/rpc/SAI/Variable.pm @@ -34,12 +34,14 @@ has 'type' => ( is_attr_list => 'is_attr_list', typename => 'name', thrift_typename => 'thrift_name', + protobuf_typename => 'protobuf_name', convert_to_list => 'convert_to_list', convert_to_attr_list => 'convert_to_attr_list' }, ); with 'SAI::RPC::ThriftName::Variable'; +with 'SAI::RPC::ProtoBufName::Variable'; ########### # Methods # diff --git a/meta/templates/sai.protobuf.tt b/meta/templates/sai.protobuf.tt new file mode 100644 index 000000000..a89b6e435 --- /dev/null +++ b/meta/templates/sai.protobuf.tt @@ -0,0 +1,382 @@ +[% PROCESS "$templates_dir/sai_protobuf_utils.tt" -%] + +[%- ######################################################################## -%] + +[%- BLOCK typedef_declaration -%] + [%- IF dbg -%] + // [% type.type.name %] [% type.name %][% IF type.raw %] (raw)[% END %]; + + [%- END -%] +message [% type.protobuf_name %] { + [% type.protobuf_def %] value = 1; +} + +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_api_typedefs -%] + [%- FOREACH type IN apis.$api.typedefs %] + [%- PROCESS typedef_declaration -%] + [%- END -%] +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_enums -%] +// SAI ENUMS +[%- SET generated_enums = {} %] +[% FOREACH api IN apis.keys.sort %] + [% FOREACH function IN apis.$api.functions %] + [%- IF apis.$api.objects.${function.object}.attrs.name %] + [%- SET enum_name = "protobuf" _ apis.$api.objects.${function.object}.attrs.name %] + [%- UNLESS generated_enums.$enum_name %] + +enum [%- enum_name %] { + [%- SET is_enum_first = "YES" %] + [% FOREACH elem IN apis.$api.objects.${function.object}.attrs.elements %] + [%- FOREACH attr IN elem %] + [%- IF attr.value && is_enum_first == 'YES' %] + [%- SET is_enum_first = "NO" -%] + [%- attr.value -%] = 0; + [% END %] + [%- IF attr.name && attr.value %] +// [%- attr.name %] = [%- attr.value %]; + [%- IF not loop.last %];[%- END %] + [%- END %] + [%- END %] + [% END %]; +} + [%- generated_enums.$enum_name = 1 -%] + [%- END -%] + [% END %] + [%- END -%] +[%- END -%] +// SAI ENUMS END +[%- END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_typedefs -%] +// common types +message sai_protobuf_int { + int32 value = 1; +} + + + [%- PROCESS define_api_typedefs api = 'common' -%] + + [%- FOREACH api IN apis.keys.sort -%] + [%- IF apis.$api.typedefs.size and api != 'common' -%] + +// [% api %] API types + + [%- PROCESS define_api_typedefs -%] + [%- END -%] + [%- END -%] +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- # TODO: This is workaround, it should be removed -%] +[%- BLOCK define_object_structs -%] +// object API structures +// warning: this struct is invalid and manually defined - do not use +message sai_protobuf_object_key_entry_t { + int64 object_id = 1; + int64 fdb_entry = 2; + int64 neighbor_entry = 3; + int64 route_entry = 4; + int64 mcast_fdb_entry = 5; + int64 l2mc_entry = 6; + int64 ipmc_entry = 7; + int64 inseg_entry = 8; + int64 nat_entry = 9; +} + +message sai_protobuf_object_key_t { + sai_protobuf_object_key_entry_t key = 1; +} + +message sai_protobuf_attr_capability_t { + bool create_implemented = 1; + bool boolset_implemented = 2; + bool get_implemented = 3; +} + +message sai_protobuf_query_attribute_enum_values_capability_msg_arg { + sai_protobuf_object_type_t object_type = 1; + sai_protobuf_attr_id_t attr_id = 2; + int32 caps_count = 3; +} + +message sai_protobuf_object_type_get_availability_msg_arg { + sai_protobuf_object_type_t object_type = 1; + sai_protobuf_attr_id_t attr_id = 2; + int32 attr_type = 3; +} + +message sai_protobuf_string_response { + string value = 1; +} + +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_api_structs -%] + [%- FOREACH struct IN apis.$api.structs %] + [%- PROCESS struct_declaration %] + + [%- END %] +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK struct_declaration -%] +message [% struct.protobuf_name %] { + [%- id = 1; FOREACH member IN struct.members %] + [%- # IF dbg %] + // [% member.type.name %] [% member.name %]; + [%- # END -%] + + [%- # TODO: The condition is a workaround, it should be removed and only 'ELSE' part should be kept -%] + [%- IF struct.protobuf_name == 'sai_protobuf_attribute_value_t' AND id == 1 %] + oneof sai_protobuf_attribute_value { + [%- END %] + + [%- IF struct.protobuf_name == 'sai_protobuf_attr_condition_t' AND member.type.protobuf_name == 'sai_protobuf_attribute_value_t' AND member.name == 'condition' -%] + int64 [% member.protobuf_name %] = [% id; id = id + 1 %]; + [%- ELSE -%] + [% member.type.protobuf_name %] [% member.protobuf_name %] = [% id; id = id + 1 %]; + [%- END -%] + [%- END %] + [%- IF struct.protobuf_name == 'sai_protobuf_attribute_value_t' %] + } + [%- END %] +} +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_structs -%] +[% # TODO: This is workaround, it should be removed -%] +[%- PROCESS define_object_structs %] +[% # TODO: This is end of workaround, it should be removed -%] + +// common types + + [%- PROCESS define_api_structs api = 'common' -%] + + [%- FOREACH api IN apis.keys.sort -%] + [%- # TODO: Comparison to 'object' is a part of workaround, it should be removed -%] + [%- IF apis.$api.structs.size and api != 'common' and api != 'object' -%] +// [% api %] API structures + + [%- PROCESS define_api_structs %] + [%- END -%] + [%- END %] + + [%- PROCESS define_attribute_list -%] + [%- PROCESS define_stats_ptr_list -%] +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_exceptions -%] + +// error handling +message sai_protobuf_exception { + sai_protobuf_status_t status = 1; +} +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_empty_response -%] + +// error handling +message sai_protobuf_response { + bool success = 1; +} +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_stats_ptr_list -%] +// Stats attribute list +message sai_protobuf_u64_list_t { + // uint32_t count; + sai_protobuf_uint32_t count = 1; + // PTR=>int64_t list; + repeated sai_protobuf_uint64_t u64 = 2; +} +message sai_protobuf_status_list_t { + // uint32_t count; + sai_protobuf_uint32_t count = 1; + // PTR=>int32_t list; + repeated sai_protobuf_status_t status = 2; +} +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_attribute_list -%] +// common attribute list +message sai_protobuf_attribute_list_t { + sai_protobuf_int32_t attr_count = 1; + repeated sai_protobuf_attribute_t attr_list = 2; +} +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK function_debug_info -%] + [%- # IF dbg -%] + + // [% function.dbg_info %] + + [%- IF function.object -%] + [%- IF function.operation == 'create' -%] + // [% function.object %] mandatory attrs: + // [%- FOREACH attr IN apis.$api.objects.${function.object}.attrs.mandatory -%] [%- attr.name %], [% END %] + [%- ELSIF function.operation == 'stats' -%] + // [% function.object %] stats: + // [%- FOREACH stat IN apis.$api.objects.${function.object}.stats.all -%] [%- stat.simple_name -%], [%- END -%] + [%- ELSE -%] + // [% function.object %] attrs: + // [% FOREACH attr IN apis.$api.objects.${function.object}.attrs.${function.operation} %] + // [% attr.name %], [% END %] + [%- END %] + + [%- END -%] + [%- # END -%] + // [% RAGHA %] +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK function_declaration -%] + rpc [% function.protobuf_name %]([% function.protobuf_name %]_msg_args) + [%- IF function.rpc_return.type.protobuf_name == 'void' -%] + returns (sai_protobuf_response); + + [%- ELSIF function.rpc_return.type.protobuf_name == 'string' -%] + returns (sai_protobuf_string_response); + + [%- ELSIF function.rpc_return.type.protobuf_name == 'repeated sai_protobuf_uint64_t' || function.rpc_return.type.protobuf_name == 'repeated sai_protobuf_uint64_t ' -%] + returns (sai_protobuf_u64_list_t); + [%- ELSIF function.rpc_return.type.protobuf_name == 'repeated sai_protobuf_uint32_t' || function.rpc_return.type.protobuf_name == 'repeated sai_protobuf_uint32_t ' -%] + returns (sai_protobuf_u32_list_t); + [%- ELSIF function.rpc_return.type.protobuf_name == 'repeated sai_protobuf_status_t' -%] + returns (sai_protobuf_status_list_t); + + [%- ELSE -%] + returns ([% function.rpc_return.type.protobuf_name %]); + [% END -%] + +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_api_functions -%] + [%- FOREACH function IN apis.$api.functions -%] + [%- PROCESS function_debug_info -%] + [%- IF api -%] + // [% api %] API + [%- END %] + + [%- PROCESS function_declaration -%] + [%- END -%] +[% END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_functions -%] + [%- IF apis.common.functions.size -%] + + // common functions + [%- PROCESS define_api_functions api = 'common' -%] + [%- END -%] + + [%- FOREACH api IN apis.keys.sort -%] + [%- IF apis.$api.functions.size and api != 'common' %] + // Start of [% api %] API + [%- PROCESS define_api_functions api = api -%] + [%- END -%] + + [%- END -%] +[% END -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_message_sai_args -%] +[%- FOREACH api IN apis.keys.sort -%] +[%- FOREACH function IN apis.$api.functions -%] +message [% function.protobuf_name %]_msg_args { + optional sai_protobuf_int32_t tid = 1; + [%- id = 2; FOREACH rpcarg IN function.args %] + [% rpcarg.type.protobuf_name %] [% rpcarg.name %] = [%- id; id = id + 1 %]; + [%- END %] +} +[% END -%] +[% END -%] +[%- END -%] + +[%- # The body of the file: -%] +// AUTOGENERATED FILE! DO NOT EDIT + +syntax = "proto3"; + +//package my_package; + + +[% PROCESS define_typedefs %] + +[% PROCESS define_enums %] + +[% PROCESS define_structs %] + +[% PROCESS define_exceptions %] + +[% PROCESS define_empty_response %] + +[% PROCESS define_message_sai_args %] + +service sai_rpc { + + [%- PROCESS define_functions %] + [%- PROCESS define_utils_functions %] + [%- new_template -%] +} + diff --git a/meta/templates/sai_protobuf_utils.tt b/meta/templates/sai_protobuf_utils.tt new file mode 100644 index 000000000..f8c9fdf65 --- /dev/null +++ b/meta/templates/sai_protobuf_utils.tt @@ -0,0 +1,26 @@ +[%- ######################################################################## -%] + +[%- BLOCK define_objects_api -%] + // sai objects API + rpc sai_protobuf_query_attribute_enum_values_capability(sai_protobuf_query_attribute_enum_values_capability_msg_arg) returns (sai_protobuf_attribute_list_t); + rpc sai_protobuf_object_type_get_availability(sai_protobuf_object_type_get_availability_msg_arg) returns (sai_protobuf_uint64_t); + rpc sai_protobuf_switch_id_query(sai_protobuf_object_id_t) returns (sai_protobuf_object_id_t); + rpc sai_protobuf_object_type_query(sai_protobuf_object_id_t) returns (sai_protobuf_object_type_t); + rpc sai_protobuf_api_uninitialize(sai_protobuf_object_id_t) returns (sai_protobuf_status_t); + +[%- END -%] + +[%- ######################################################################## -%] + +[%- ######################################################################## -%] + +[%- BLOCK define_utils_functions -%] + + // SAI utils + + + [%- PROCESS define_objects_api -%] + +[%- END -%] + +[%- ######################################################################## -%]