Open gsindigi opened 5 years ago
Hi Ganesh,
Thanks for the question.
The common thread to link different representations of YANG models together is the schema and datatree paths that are used for a particular element. We annotate both the Go files, and the generated protobufs with the paths within the schema, and runtime libraries can then subsequently determine what the data tree paths are by appending the keys for elements to this. So, generically, I think the answer is that we should be able to take <path, value>
tuples and deserialise these into any form of generated entity with reflects a YANG schema.
gNMI's serialisation defines a common type structure that is used for exchange between systems, so the use of the <path, value>
tuples using gNMI's TypedValue
message would then mean that we have a way to unify types across the different generated code -- such that we can optimise for the best type for each different code artifact's users.
Let's say that we made this <path, value>
tuple a gnmi_proto.Update
- which can store both the path (as gnmi_proto.Path
) and value (as suggested above as gnmi_proto.TypedValue
). This has another advantage insofar that it is useful library code to generate gnmi_proto.Notification
from generated structures for telemetry use-cases, and deserialising gnmi_proto.Update
into a data tree is required for the gNMI Set
implementations.
This approach now allows you to arbitrarily map between any two representations of a YANG schema that has had code generated for it. It also maintains the advantage that there is no need for consistency between the different code generation libraries -- each language can choose the most idiomatic approach for itself.
For Go, ygot
already implements much of the required functionality here for the ygot.GoStruct
interface:
ygot.TogNMINotifications
converts a ygot.GoStruct
to []*gnmipb.Notification
.ytypes.SetNode
can set field in a ygot.GoStruct
based on a gnmipb.Path
and gnmipb.TypedValue
.For protobuf, we also designed and started implementation on similar infrastructure -- it needs mapping between a particular protobuf message or field and the corresponding schemapath. The design document for this is accessible here (Google account required). The initial protoc-gen-go plugin to implement the output of the annotations can be found in the protoyangplugin
directory in ygot. Similar plugins within the protoc framework are required per-language (similarly to the code generation initially required, and the runtime libraries).
Please let me know if this isn't clear -- hopefully the design doc answers most of the initial queries.
r.
Rob, thanks for the clarifications and the design doc as well. Shall go through it and come back for any further clarifications.
The intention behind having this convergence is to minimize or eliminate (if possible) between different systems. In an ideal scenario, the protobuf based GoStruct (either proto_generator or goyang based protoc generated ones) can be directly used for gNMI serializations as if they were ygot.GoStruct
. If not, are there any generic ways of such conversion either by having GoYang AST YANG-SchemaTree or by means of reflection? Want to leverage the ygot
support to large extent and avoid re-inventing where its possible.
goyang's Protobuf generation is incomplete, and we've actually removed the code that was used there. It was a demonstration program, rather than something that should be relied upon. The only real supported way to go from YANG->protobuf is the proto_generator
binary.
Our intention with the toolchain is (for Go) to keep the ygot.GoStruct
types as the primary way that developers work with a YANG schema in Go. This means that we don't need to duplicate all the library support that we have (ytypes.Validate
, ygot.Diff
, etc.). To support protobuf encoding (e.g., for Stratum, or telemetry), then the implementation plan is to:
protoc-gen-go
structs to gnmi_proto.Notification
.gnmi_proto.Notification
to protoc-gen-go
generated structs.I think that it would help for me to understand what you're actually trying to accomplish -- all deserialisation/serialisation in ygot
is implemented for generic schemas using reflection.
Thanks again for your clarifications.
I think that it would help for me to understand what you're actually trying to accomplish
Recently came across Stratum and was trying to see how gNMI operations could be supported and some of design alternatives on config/state datastores, should it be a centralized/shared or not. If need to have a persistent datastore (for config), whether protobuf can be used for storage ? etc. I do not know much about Stratum, so I may not know whether it has answers for these. Or if gNMI alone need to be supported (without Stratum model in picture), how the config data can be persisted , so that Get/Set & Subscribe can be implemented with minimal inter-conversions in a polyglot technology environment.
-- all deserialisation/serialisation in ygot is implemented for generic schemas using reflection. Yes, I'd seen SerDes in ygot , which is reflection based and pretty neat as well.
On Wed, May 22, 2019 at 11:15 PM Rob Shakir notifications@github.com wrote:
goyang's Protobuf generation is incomplete, and we've actually removed the code that was used there. It was a demonstration program, rather than something that should be relied upon. The only real supported way to go from YANG->protobuf is the proto_generator binary.
Our intention with the toolchain is (for Go) to keep the ygot.GoStruct types as the primary way that developers work with a YANG schema in Go. This means that we don't need to duplicate all the library support that we have (ytypes.Validate, ygot.Diff, etc.). To support protobuf encoding (e.g., for Stratum, or telemetry), then the implementation plan is to:
- Implement serialisation of generated protoc-gen-go structs to gnmi_proto.Notification.
- Implement deserialisation of gnmi_proto.Notification to protoc-gen-go generated structs.
I think that it would help for me to understand what you're actually trying to accomplish -- all deserialisation/serialisation in ygot is implemented for generic schemas using reflection.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/openconfig/reference/issues/110?email_source=notifications&email_token=AHLHCPJK7Q6K224LIDZM3YLPWWBEXA5CNFSM4HOKFV22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODV7ZNSQ#issuecomment-494900938, or mute the thread https://github.com/notifications/unsubscribe-auth/AHLHCPKOTRVFHR4KIW3VM33PWWBEXANCNFSM4HOKFV2Q .
There are a few approaches as to how to persist configuration. Generally, as you've noticed, this needs some serialisation to store the config input. The fakes that we have developed generally persist the configuration as JSON, since that's the primary interchange format that is being used with devices.
Stratum aims to use proto as this format, but the internal implementations are not in Go -- thus, there's been less focus on this ygot.GoStruct
to protobuf conversion. If one were building an implementation that does use Go, then the above implementation is likely the best option -- since we currently don't have significant tooling to generate C++ data structures from the YANG models. The proto conversion is POC'd to some extent, but needs further work. We always welcome contributions in this space.
-- since we currently don't have significant tooling to generate C++ data structures from the YANG models
I wonder what Stratum do suggest/propose for datastore as far as config persistence is concerned. With increasing scale, it may not be ideal to have a file-based mechanism. Tried to gather information on this front, but could not. Is it left to individual services' choice or any guidelines set forth ?
At this moment, I do not have an idea on what it would take to support such conversions and contribute. Based on your prior experience, if you have any information on this front, please share. Would help to decide either ways.
The Stratum open source project can choose its own implementation -- which may include persisting the YANG schema, but does not necessarily need to include this (for example, it can persist some other backend format which can then be translated back to the original OpenConfig schema input). This implementation is entirely a choice for that project, and the associated tooling around OpenConfig makes no assertions as to the format choices. To some extent it rather depends on the scale that one expects to need to support, and the approach for querying the data.
The conversion design is roughly described in the design documentation -- there's some work to implement these for sure, I don't think it's a huge project given that the majority of the design seems pretty much there.
With the current set of tools/support (YGOT & GoYang), there are 3 different ways to generate the Go Structs for a given YANG schema.
1 YGOT-Generator
This generates the related Go artifacts directly and can readily be used for gNMI related support. Also, its possible to un-/marshal from/to RFC7951 compliant JSON. All such functions are included either in the
ygot
or the generated structs.2 Combination of proto_generator & protoc
This is a two step process, YANG schema is converted to an intermediate
proto
file withproto_generator
and converted to Go struct usingprotoc
's compiler pluginprotoc-gen-go
.3 Combination of goyang & protoc
This too is a two step process, YANG schema is converted to an intermediate
proto
file withgoyang
and converted to Go struct usingprotoc
's compiler pluginprotoc-gen-go
.All above 3 ways are great and work as expected in isolation. However, when these are used in combination e.g., 1 & 2, 1 & 3 or 2 & 3, there is no easy way to convert from one form to other due to different reasons though they are all referring to same YANG schema.
Listed below are some of such, which I came across.
difference in name generation e.g., use '_', '-' in paths, field names etc.
This is translated as
difference in enum handling & encoding values e.g.,
difference in data-types used e.g.,
The possibility of inter-conversion is very much required in a polyglot technologies/development scenarios. Imagine, the device software is in C++ (for legacy reasons) which supports all OpenConfig modules information e.g.,
state
information and intended to provide gNMI support by leveraging YGOT support available. One easy option to begin with would be choosing to combine above conversion mechanisms 1 & 2 or 1 & 3, so that C++ can make use protoc generatec C++ Class artefacts and Go implementation can depend onygot.GoStruct
alone (to leverage YGOT support for gNMI to large extent). Now, with differences enlisted above, such a design choice may not work easily and would need identification & intermittent conversions of such differences (as and when they are uncovered?) at multiple places. This forces not to use Protobuf's efficient on-the-wire encoding. Instead, if JSON is used to convert from one form to other, then it too will run into some non-matching fields issues and thus either errors out or skips.I understand, these tools have evovled independently and at different phases. And it may not be right to expect a convergence.
I am not sure, how easy or difficult a task it would be to come up with a conversion mechanism for Go-Struct (option 2/3 above) suitable for
ygot.GoStruct
s.Do you see any easy way to converge generated Go structs across different forms and have them ready as
ygot.GoStruct
s for subsequent gNMI related handling? And there will be other design alternatives to overcome such.Appreciate any inputs/clarifications on this front. Thanks.