openconfig / reference

This repository contains reference implementations, specifications and tooling related to OpenConfig-based network management.
Apache License 2.0
155 stars 88 forks source link

Clarification: Any easy way to converge generated Go structs (from different tools) ? #110

Open gsindigi opened 5 years ago

gsindigi commented 5 years ago

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 with proto_generator and converted to Go struct using protoc's compiler plugin protoc-gen-go.

3 Combination of goyang & protoc

This too is a two step process, YANG schema is converted to an intermediate proto file with goyang and converted to Go struct using protoc's compiler plugin protoc-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.

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 on ygot.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.GoStructs.

Do you see any easy way to converge generated Go structs across different forms and have them ready as ygot.GoStructs for subsequent gNMI related handling? And there will be other design alternatives to overcome such.

Appreciate any inputs/clarifications on this front. Thanks.

robshakir commented 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:

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.

gsindigi commented 5 years ago

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.

robshakir commented 5 years ago

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:

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.

gsindigi commented 5 years ago

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 .

robshakir commented 5 years ago

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.

gsindigi commented 5 years ago

-- 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.

robshakir commented 5 years ago

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.