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
-
命令前缀转为container节点,示例命令中ip为前缀,转换如下:
container ip{ ... }
-
子视图转为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; } }
-
单个命令转为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;
}
}
}
}
}