RSCG – DotnetYang
RSCG – DotnetYang
name | DotnetYang |
nuget | https://www.nuget.org/packages/DotnetYang/ |
link | https://github.com/westermo/DotnetYang |
author | Westermo Network Technologies |
Generating source code from YANG models
This is how you can use DotnetYang .
The code that you start with is
<project sdk="Microsoft.NET.Sdk"> <propertygroup> <outputtype>Exe</outputtype> <targetframework>net8.0</targetframework> <implicitusings>enable</implicitusings> <nullable>enable</nullable> </propertygroup> <itemgroup> <additionalfiles include="demo.yang"> </additionalfiles> <propertygroup> <emitcompilergeneratedfiles>true</emitcompilergeneratedfiles> <compilergeneratedfilesoutputpath>$(BaseIntermediateOutputPath)\GX</compilergeneratedfilesoutputpath> </propertygroup> <itemgroup> <packagereference version="0.3.0" include="dotnetYang"> </packagereference> </itemgroup>
The code that you will use is
Console.WriteLine("Yang file from https://info.support.huawei.com/info-finder/encyclopedia/en/YANG.html#content4!"); Some.Module.YangNode.DoSomethingInput input = new Some.Module.YangNode.DoSomethingInput { TheBigLeaf = 123 };
module some-module { yang-version 1.1; namespace "urn:dotnet:yang:andrei"; prefix sm; identity someIdentity; identity someOtherIdentity { base someIdentity; } rpc doSomething { input { leaf the-big-leaf { type uint32; default "4"; description "The value that is the input of the doSomething rpc"; } } output { leaf response { type identityref { base someIdentity; } default "someOtherIdentity"; description "The identity that is the output of the doSomething rpc"; } } } }
The code that is generated is
using System; using System.Xml; using YangSupport; namespace yangDemo; ///<summary> ///Configuration root object for yangDemo based on provided .yang modules ///</summary> public class Configuration { public Some.Module.YangNode? SomeModule { get; set; } public async Task WriteXMLAsync(XmlWriter writer) { await writer.WriteStartElementAsync(null,"root",null); if(SomeModule is not null) await SomeModule.WriteXMLAsync(writer); await writer.WriteEndElementAsync(); } public static async Task<configuration> ParseAsync(XmlReader reader) { Some.Module.YangNode? _SomeModule = default!; while(await reader.ReadAsync()) { switch(reader.NodeType) { case XmlNodeType.Element: switch(reader.Name) { case "some-module": _SomeModule = await Some.Module.YangNode.ParseAsync(reader); continue; case "rpc-error": throw await RpcException.ParseAsync(reader); default: throw new Exception($"Unexpected element '{reader.Name}' under 'root'"); } case XmlNodeType.EndElement when reader.Name == "root": return new Configuration{ SomeModule = _SomeModule, }; case XmlNodeType.Whitespace: break; default: throw new Exception($"Unexpected node type '{reader.NodeType}' : '{reader.Name}' under 'root'"); } } throw new Exception("Reached end-of-readability without ever returning from Configuration.ParseAsync"); } } public static class IYangServerExtensions { public static async Task Receive(this IYangServer server, global::System.IO.Stream input, global::System.IO.Stream output) { var initialPosition = output.Position; var initialLength = output.Length; string? id = null; using XmlReader reader = XmlReader.Create(input, SerializationHelper.GetStandardReaderSettings()); using XmlWriter writer = XmlWriter.Create(output, SerializationHelper.GetStandardWriterSettings()); try { await reader.ReadAsync(); switch(reader.Name) { case "rpc": id = reader.ParseMessageId(); await writer.WriteStartElementAsync(null, "rpc-reply", "urn:ietf:params:xml:ns:netconf:base:1.0"); await writer.WriteAttributeStringAsync(null, "message-id", null, id); await reader.ReadAsync(); switch(reader.Name) { case "action": await server.ReceiveAction(reader, writer); break; default: await server.ReceiveRPC(reader, writer); break; } await writer.WriteEndElementAsync(); await writer.FlushAsync(); break; case "notification": var eventTime = await reader.ParseEventTime(); await reader.ReadAsync(); await server.ReceiveNotification(reader, eventTime); break; } } catch(RpcException ex) { await writer.FlushAsync(); output.Position = initialPosition; output.SetLength(initialLength); await ex.SerializeAsync(output,id); } catch(Exception ex) { await writer.FlushAsync(); output.Position = initialPosition; output.SetLength(initialLength); await output.SerializeRegularExceptionAsync(ex,id); } } public static async Task ReceiveRPC(this IYangServer server, XmlReader reader, XmlWriter writer) { switch(reader.Name) { case "doSomething" when reader.NamespaceURI is "urn:dotnet:yang:andrei": { var input = await Some.Module.YangNode.DoSomethingInput.ParseAsync(reader); var task = server.OnDoSomething(input); var response = await task; await response.WriteXMLAsync(writer); } break; } } public static async Task ReceiveAction(this IYangServer server, XmlReader reader, XmlWriter writer) { await reader.ReadAsync(); switch(reader.Name) { } } public static async Task ReceiveNotification(this IYangServer server, XmlReader reader, DateTime eventTime) { switch(reader.Name) { } } }
using System; using System.Xml; using System.Text; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Xml.Linq; using System.Text.RegularExpressions; using YangSupport; namespace yangDemo { public partial interface IYangServer { Task<some.module.yangnode.dosomethingoutput> OnDoSomething(Some.Module.YangNode.DoSomethingInput input); } } namespace Some.Module{ public class YangNode { public const string ModuleName = "some-module"; public const string Revision = ""; public static string[] Features = []; //Yang Version 1.1 public const string Namespace = "urn:dotnet:yang:andrei"; public static string GetEncodedValue(SomeIdentityIdentity value) { switch(value) { case SomeIdentityIdentity.SomeIdentity: return "someIdentity"; case SomeIdentityIdentity.SomeOtherIdentity: return "someOtherIdentity"; default: return value.ToString(); } } public static string GetEncodedValue(SomeIdentityIdentity? value) => GetEncodedValue(value!.Value!); public static SomeIdentityIdentity GetSomeIdentityIdentityValue(string value) { switch(value) { case "someIdentity": return SomeIdentityIdentity.SomeIdentity; case "someOtherIdentity": return SomeIdentityIdentity.SomeOtherIdentity; default: throw new Exception($"{value} is not a valid value for SomeIdentityIdentity"); } } public enum SomeIdentityIdentity { SomeIdentity, SomeOtherIdentity } public static string GetEncodedValue(SomeOtherIdentityIdentity value) { switch(value) { case SomeOtherIdentityIdentity.SomeOtherIdentity: return "someOtherIdentity"; default: return value.ToString(); } } public static string GetEncodedValue(SomeOtherIdentityIdentity? value) => GetEncodedValue(value!.Value!); public static SomeOtherIdentityIdentity GetSomeOtherIdentityIdentityValue(string value) { switch(value) { case "someOtherIdentity": return SomeOtherIdentityIdentity.SomeOtherIdentity; default: throw new Exception($"{value} is not a valid value for SomeOtherIdentityIdentity"); } } public enum SomeOtherIdentityIdentity { SomeOtherIdentity } public static async Task<some.module.yangnode.dosomethingoutput> DoSomething(IChannel channel, int messageID, Some.Module.YangNode.DoSomethingInput input) { using XmlWriter writer = XmlWriter.Create(channel.WriteStream, SerializationHelper.GetStandardWriterSettings()); await writer.WriteStartElementAsync(null,"rpc","urn:ietf:params:xml:ns:netconf:base:1.0"); await writer.WriteAttributeStringAsync(null,"message-id",null,messageID.ToString()); await writer.WriteStartElementAsync("","doSomething","urn:dotnet:yang:andrei"); await input.WriteXMLAsync(writer); await writer.WriteEndElementAsync(); await writer.WriteEndElementAsync(); await writer.FlushAsync(); await channel.Send(); using XmlReader reader = XmlReader.Create(channel.ReadStream, SerializationHelper.GetStandardReaderSettings()); await reader.ReadAsync(); if(reader.NodeType != XmlNodeType.Element || reader.Name != "rpc-reply" || reader.NamespaceURI != "urn:ietf:params:xml:ns:netconf:base:1.0" || reader["message-id"] != messageID.ToString()) { throw new Exception($"Expected stream to start with a <rpc-reply> element with message id {messageID} & \"urn:ietf:params:xml:ns:netconf:base:1.0\" but got {reader.NodeType}: {reader.Name} in {reader.NamespaceURI}"); } var value = await DoSomethingOutput.ParseAsync(reader); return value; } public class DoSomethingOutput { ///<summary> ///The identity that is the output of the doSomething rpc ///</summary> public SomeIdentityIdentity? Response { get; set; } = SomeIdentityIdentity.SomeOtherIdentity; public static async Task<dosomethingoutput> ParseAsync(XmlReader reader) { SomeIdentityIdentity? _Response = default!; while(await reader.ReadAsync()) { switch(reader.NodeType) { case XmlNodeType.Element: switch(reader.Name) { case "response": await reader.ReadAsync(); if(reader.NodeType != XmlNodeType.Text) { throw new Exception($"Expected token in ParseCall for 'response' to be text, but was '{reader.NodeType}'"); } _Response = GetSomeIdentityIdentityValue(await reader.GetValueAsync()); if(!reader.IsEmptyElement) { await reader.ReadAsync(); if(reader.NodeType != XmlNodeType.EndElement) { throw new Exception($"Expected token in ParseCall for 'response' to be an element closure, but was '{reader.NodeType}'"); } } continue; case "rpc-error": throw await RpcException.ParseAsync(reader); default: throw new Exception($"Unexpected element '{reader.Name}' under 'rpc-reply'"); } case XmlNodeType.EndElement when reader.Name == "rpc-reply": return new DoSomethingOutput{ Response = _Response, }; case XmlNodeType.Whitespace: break; default: throw new Exception($"Unexpected node type '{reader.NodeType}' : '{reader.Name}' under 'rpc-reply'"); } } throw new Exception("Reached end-of-readability without ever returning from DoSomethingOutput.ParseAsync"); } public async Task WriteXMLAsync(XmlWriter writer) { if(Response != default) { await writer.WriteStartElementAsync(null,"response","urn:dotnet:yang:andrei"); await writer.WriteStringAsync(YangNode.GetEncodedValue(Response!)); await writer.WriteEndElementAsync(); } } } public class DoSomethingInput { ///<summary> ///The value that is the input of the doSomething rpc ///</summary> public uint? TheBigLeaf { get; set; } = 4; public async Task WriteXMLAsync(XmlWriter writer) { if(TheBigLeaf != default) { await writer.WriteStartElementAsync(null,"the-big-leaf","urn:dotnet:yang:andrei"); await writer.WriteStringAsync(TheBigLeaf!.ToString()); await writer.WriteEndElementAsync(); } } public static async Task<dosomethinginput> ParseAsync(XmlReader reader) { uint? _TheBigLeaf = default!; while(await reader.ReadAsync()) { switch(reader.NodeType) { case XmlNodeType.Element: switch(reader.Name) { case "the-big-leaf": await reader.ReadAsync(); if(reader.NodeType != XmlNodeType.Text) { throw new Exception($"Expected token in ParseCall for 'the-big-leaf' to be text, but was '{reader.NodeType}'"); } _TheBigLeaf = uint.Parse(await reader.GetValueAsync()); if(!reader.IsEmptyElement) { await reader.ReadAsync(); if(reader.NodeType != XmlNodeType.EndElement) { throw new Exception($"Expected token in ParseCall for 'the-big-leaf' to be an element closure, but was '{reader.NodeType}'"); } } continue; case "rpc-error": throw await RpcException.ParseAsync(reader); default: throw new Exception($"Unexpected element '{reader.Name}' under 'doSomething'"); } case XmlNodeType.EndElement when reader.Name == "doSomething": return new DoSomethingInput{ TheBigLeaf = _TheBigLeaf, }; case XmlNodeType.Whitespace: break; default: throw new Exception($"Unexpected node type '{reader.NodeType}' : '{reader.Name}' under 'doSomething'"); } } throw new Exception("Reached end-of-readability without ever returning from DoSomethingInput.ParseAsync"); } } public static async Task<some.module.yangnode> ParseAsync(XmlReader reader) { while(await reader.ReadAsync()) { switch(reader.NodeType) { case XmlNodeType.Element: switch(reader.Name) { case "rpc-error": throw await RpcException.ParseAsync(reader); default: throw new Exception($"Unexpected element '{reader.Name}' under 'some-module'"); } case XmlNodeType.EndElement when reader.Name == "some-module": return new Some.Module.YangNode{ }; case XmlNodeType.Whitespace: break; default: throw new Exception($"Unexpected node type '{reader.NodeType}' : '{reader.Name}' under 'some-module'"); } } throw new Exception("Reached end-of-readability without ever returning from Some.Module.YangNode.ParseAsync"); } public async Task WriteXMLAsync(XmlWriter writer) { await writer.WriteStartElementAsync(null,"some-module","urn:dotnet:yang:andrei"); await writer.WriteEndElementAsync(); } } }
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/DotnetYang