有没有办法将 juniper "json" 或 "xml" 配置转换为 "set" 或 "show" 配置?

Is there a way to convert juniper "json" or "xml" config to "set" or "show" config?

我们使用带有 junos 版本 15 的瞻博网络硬件。在这个版本中,我们可以将我们的配置导出为 "json" 或 "xml",我们希望使用我们的自动化工具对其进行编辑。 但是只能以 "set" 或 "show" 格式导入。

有没有工具可以将"json"或"xml"格式转换为"set"或"show"格式? 我只能找到 "show" 和 "set" 之间的转换器。

我们无法升级到可以导入 "json" 的版本 16。

您可以使用编辑配置 RPC 或加载配置 RPC 加载 XML 配置。更多详情:

https://www.juniper.net/documentation/en_US/junos/topics/reference/tag-summary/netconf-edit-config.html

https://www.juniper.net/documentation/en_US/junos/topics/reference/tag-summary/junos-xml-protocol-load-configuration.html

XML 内容可以通过 "op" 脚本加载,方法是将内容放在对 "junos.xsl" 中定义的 junos:load-configuration() 模板的调用中。类似于以下内容:

version 1.1;

ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";

import "../import/junos.xsl";

var $arguments = {
    <argument> {
        <name> "file";
        <description> "Filename of XML content to load";
    }
    <argument> {
        <name> "action";
        <description> "Mode for the load (override, replace, merge)";
    }
}

param $file;
param $action = "replace";

match / {
    <op-script-results> {
        var $configuration = slax:document($file);
        var $connection = jcs:open();
        call jcs:load-configuration($connection, $configuration, $action);
    }
}

谢谢, 菲尔

这是我在工作中制作的脚本,将其放入您的容器中,您可以通过提供文件名或管道输出来获取它。这假设 linux 或 mac 所以 os.isatty 函数有效,但逻辑可以在任何地方工作:

使用演示:

person@laptop ~ > head router.cfg
## Last commit: 2021-04-20 21:21:39 UTC by vit
version 15.1X12.2;
groups {
    BACKBONE-PORT {
        interfaces {
            <*> {
                mtu 9216;
                unit <*> {
                    family inet {
                        mtu 9150;
person@laptop ~ > convert.py router.cfg | head
set groups BACKBONE-PORT interfaces <*> mtu 9216
set groups BACKBONE-PORT interfaces <*> unit <*> family inet mtu 9150
set groups BACKBONE-PORT interfaces <*> unit <*> family inet6 mtu 9150
set groups BACKBONE-PORT interfaces <*> unit <*> family mpls maximum-labels 5
<... output removed... >

convert.py:

#!/usr/bin/env python3
# Class that attempts to parse out Juniper JSON into set format
#   I think it works? still testing
#
#   TODO: 
#      accumulate annotations and provide them as commands at the end. Will be weird as annotations have to be done after an edit command
from argparse import ArgumentParser, RawTextHelpFormatter
import sys, os, re

class TokenStack():
    def __init__(self):
        self._tokens = []

    def push(self, token):
        self._tokens.append(token)

    def pop(self):
        if not self._tokens:
            return None
        item = self._tokens[-1]
        self._tokens = self._tokens[:-1]
        return item

    def peek(self):
        if not self._tokens:
            return None
        return self._tokens[-1]

    def __str__(self):
        return " ".join(self._tokens)

    def __repr__(self):
        return " ".join(self._tokens)

def main():
    # get file
    a = ArgumentParser(prog="convert_jpr_json",
            description="This program takes in Juniper style JSON (blah { format) and prints it in a copy pastable display set format",
            epilog=f"Either supply with a filename or pipe config contents into this program and it'll print out the display set view.\nEx:\n{B}convert_jpr_json <FILENAME>\ncat <FILENAME> | convert_jpr_json{WHITE}",
            formatter_class=RawTextHelpFormatter)
    a.add_argument('file', help="juniper config in JSON format", nargs="?")
    args = a.parse_args()
    if not args.file and os.isatty(0):
        a.print_help()
        die("Please supply filename or provide piped input")
    file_contents = None
    if args.file:
        try:
            file_contents = open(args.file, "r").readlines()
        except IOError as e:
            die(f"Issue opening file {args.file}: {e}")
            print(output_text)
    else:
        file_contents = sys.stdin.readlines()

    tokens = TokenStack()
    in_comment = False
    new_config = []

    for line_num, line in enumerate(file_contents):
        if line.startswith("version ") or len(line) == 0:
            continue
        token = re.sub(r"^(.+?)#+[^\"]*$", r"", line.strip())
        token = token.strip()
        if (any(token.startswith(_) for _ in ["!", "#"])):
            # annotations currently not supported
            continue
    
        if token.startswith("/*"):
            # we're in a comment now until the next token (this will break if a multiline comment with # style { happens, but hopefully no-one is that dumb
            in_comment = True
            continue
    
        if "inactive: " in token:
            token = token.split("inactive: ")[1]
            new_config.append(f"deactivate {tokens} {token}")
        if token[-1] == "{":
            in_comment = False
            tokens.push(token.strip("{ "))
        elif token[-1] == "}":
            if not tokens.pop():
                die("Invalid json supplied: unmatched closing } encountered on line " + f"{line_num}")
        elif token[-1] == ";":
            new_config.append(f"set {tokens} {token[:-1]}")
    if tokens.peek():
        print(tokens)
        die("Unbalanced JSON: expected closing }, but encountered EOF")
    print("\n".join(new_config))

def die(msg): print(f"\n{B}{RED}FATAL ERROR{WHITE}: {msg}"); exit(1)
RED = "3[31m"; GREEN = "3[32m"; YELLOW = "3[33m"; B = "3[1m"; WHITE = "3[0m"
if __name__ == "__main__": main()