数通网络开放可编程命令行到yang模型转换

cli2yang

基本语法

ip vpn-instance 2G-RAN
ipv4-family
 route-distinguisher 100:1
 tnl-policy LDP
 apply-label per-intance
 vpn-target 100:11 export-extcommunity
 vpn-target 100:11 import-extcommunity
  1. 命令前缀转为container节点,示例命令中ip为前缀,转换如下:

    container ip{
    	...
    }
    
  2. 子视图转为list节点,示例中vpn-instance, 子视图参数作为list节点的key, 转换如下:

    list vpn-intance{
    	key vpn-intance-name;
    	leaf vpn-intance-name{
    		hw:render-name false; #此处是定制功能,指定vpn-inter-name不显示再命令中
    		type string;
    	}
    }
    
  3. 单个命令转为leaf节点,实例中tnl-policy,转换如下:

    leaf tnl-policy{
    	type string;
    }
    

拓展语法

由于,厂商设备命令格式不统一,提供了自定义的拓展语法,华为定义的拓展语句在cli_annotations.yang中

extension”语句允许定义YANG语言中的新语句。这个新的语句定义可以被其他模块导入和使用。

extension”语句的参数是一个标识符,它是扩展名的新关键字,后面必须跟着一个包含详细扩展信息的子语句块。 “extension”语句的目的是定义一个关键字,使其可以被其他模块导入和使用。

扩展名可以像普通的YANG语句一样使用,如果语句名由“extension”语句定义,则语句名后跟一个参数,并且可以包含一个可选的子语句块。该语句的名称是通过组合定义扩展名的模块的前缀,冒号(“:”)和扩展的关键字来创建的,没有交织空白。扩展的子语句由“extension”语句定义,使用本规范范围之外的某种机制。语法上,子语句必须是YANG语句,包括使用“extension”语句定义的扩展名。在扩展中的YANG语句


render-name false

配置命令不需要节点名称,即节点名字不在CLI中显示

render-view true

默认规则下,container不进入视图,此扩展来指定container进入试图

render-undo-value true

对于删除操作,如果生成的命令包含leaf的值,则需要通过扩展参数导致,true包含,flase 不包含

render-single-line

将list中所有的leaf节点转化为一条命令,而不是每个leaf一行

allow-multi-token

实现leaf携带多个字符串参数,用空格隔开,数目不超过指定的参数(xx)

render-nested-delete true

实现递归删除,先删除子节点,再删除容器本身

render-sequential-order

告诉cli解析引擎只接受container顺序与数据目录下出现的顺序相同的leaf

cli_annotations.yang中定义了很多拓展语句,文档中只用当了上面这些

拓展语句的解释

华为的系统有一个yang解析器(就是文档提到的yang driver),把yang转为cli, cli_annotations.yang定义的拓展语句对应了解析器的一条解析规则, 如:默认container 是被解析为命令前缀,不进入子视图,list 是被解析为进入子视图, 如果container中使用了render-view true,那么container就被解析为进入子视图的cli语句

对于基本语法实现的自动转换

#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@author: lagelanren
@file: main.py
@time: 2021/11/17
"""
cli = "router rsvp hello-timeout <int> \n aa bb aa \n cc"

def get_layers(cli):
    return len(cli) - len(cli.lstrip())

def grouping_by_layers(cli_list):
    tmp_layer = []
    tmp_layers = 0
    result = []
    for item in cli_list:
        if get_layers(item) == tmp_layers:
            tmp_layer.append(item)
        else:
            result.append(tmp_layer)
            tmp_layer = []
            tmp_layers += 1
            tmp_layer.append(item)
    result.append(tmp_layer)
    return result

def work(cli_file):
    with open(cli_file, "r") as f:
        cli = f.readlines()
    yang = ""
    brackets = []
    group_cli = grouping_by_layers(cli)
    tem_brackets = 0
    for index_group, layers in enumerate(group_cli):
        for index_layer, layer in enumerate(layers):
            # 处理进入子视图的命令
            if len(group_cli) > index_group + 1 and len(layers) == index_layer + 1:
                if len(layer.strip().split(" ")) == 1:
                    layer = layer.strip().split(" ")
                    yang += "container "
                    yang += layer[-1]
                    yang += " {\n"
                    brackets.append(brackets[-1] + 1 if brackets else 0)
                    yang += (brackets[-1] + 1) * 2 * " "
                    yang += "hw:render-view true;\n"
                    # 进入子视图的container 只有一个,不写}
                    # yang += " " * brackets.pop() * 2
                    yang += (brackets[-1] + 1) * 2 * " "
                else:
                    layer = layer.strip().split(" ")[0: -1]
                    for prefix in layer[0: -1]:
                        yang += "container "
                        yang += prefix
                        yang += " {\n"
                        brackets.append(brackets[-1] + 1 if brackets else 0)
                        yang += (brackets[-1] + 1) * 2 * " "
                    yang += "list "
                    yang += layer[-1]
                    yang += " {\n"
                    brackets.append(brackets[-1] + 1 if brackets else 0)
                    yang += (brackets[-1] + 1) * 2 * " "

            else:
                type = layer.strip().split(" ")[-1]
                layer = layer.strip().split(" ")[0: -1]
                for prefix in layer[0: -1]:
                    yang += "container "
                    yang += prefix
                    yang += " {\n"
                    tem_brackets += 1
                    brackets.append(brackets[-1] + 1 if brackets else 0)
                    yang += (brackets[-1] + 1) * 2 * " "
                yang += "leaf "
                yang += layer[-1]
                yang += " {\n"
                tem_brackets += 1
                brackets.append(brackets[-1] + 1 if brackets else 0)
                yang += (brackets[-1] + 1) * 2 * " "
                yang += "type "
                yang += type
                yang += ";"

            # 开始写封闭括号
            for i in range(tem_brackets):
                yang += "\n"
                yang += " " * brackets.pop() * 2
                yang += "}"
                if index_layer + 1 != len(layers):
                    if i + 1 == tem_brackets:
                        yang += "\n"
                        yang += (brackets[-1] + 1) * 2 * " "


            tem_brackets = 0
    # 开始写封闭括号
    for i in range(len(brackets)):
        yang += "\n"
        yang += " " * brackets.pop() * 2
        yang += "}"
    yang += "\n"
    return yang

if __name__ == '__main__':
    yang = work("cli")
    print(yang)

输入

ip vpn-instance 2G-RAN
 ipv4-family
  route-distinguisher 100:1
  tnl-policy LDP
  apply-label per-intance
  vpn-target 100:11 export-extcommunity
  vpn-target 100:11 import-extcommunity

输出

container ip {
  list vpn-instance {
    container ipv4-family {
      hw:render-view true;
      leaf route-distinguisher {
        type 100:1;
      }
      leaf tnl-policy {
        type LDP;
      }
      leaf apply-label {
        type per-intance;
      }
      container vpn-target {
        leaf 100:11 {
          type export-extcommunity;
        }
      }
      container vpn-target {
        leaf 100:11 {
          type import-extcommunity;
        }
      }
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值