📜 ⬆️ ⬇️

Automation in JunOS: writing scripts

In the life of every system administrator, there are times when it becomes necessary to automate the execution of some operations, or, for example, one has to delegate part of his authority to subordinates. Moreover, it is desirable to do it safely, and minimize the ability to mow. Well, if we are talking about a server running * NIX-systems - there are a great many tools designed for this. But not always so lucky ...

It would seem that a rather trivial task is to enable the junior technical staff to hang and remove the blackhole community on routers running JunOS in the simplest way possible. Everything would be fine, but bash + ssh + expect didn't suit me very much, perl + Net :: OpenSSH / Net :: SSH / etc - too. In the process of thinking about the task, I remembered Tcl in IOS, and it turned out that JunOS also has its own similar automation tools, elegant and really powerful, and, as for me, more convenient than Tcl. And the most surprising thing is that for some reason I did not find Google in runet.

Well, let me try to fill this gap, at least for general education, in a volume that allows one to appreciate the "beauty of the game." So, JunOS has a built-in ability to write your own scripts and then use them as usual commands, like this:
')
 / * Create a new entry establishing the blackhole community * /
 user @ junosbox> op blackhole set 10.0.0.1 

Agree, it is much more convenient than to enter configuration mode, manually create routes, hang community on them and commit the changes. To err in the case of performing these actions using a script is much less likely. In addition, in the script, you can perform all the necessary checks so that the operator does not perform anything unnecessary.

This feature is provided by the JunOS Automation Tool Kit, which allows you to use the JunOS XML built-in capabilities, which is a standard JunOS component and, accordingly, is present on any device running JunOS. The main tool for this is the XSLT scripting language. And this, I think, is bad news - writing on it is, of course, more convenient than on Brainfuck, but still somewhat peculiar. But there is news and good: to simplify the life of administrators, the guys from Juniper Networks also implemented support for SLAX. In JunOS up to version 11, SLAX 1.0 is supported, in the newer ones - 1.1. The documentation mentions that its syntax somewhat resembles perl. This statement seems somewhat controversial to me, but definitely, there is some distant similarity, so leave this point on the conscience of the authors of the documentation.

JunOS has several script classes:


Obviously, op-scripts are the most popular, although others can also be used.

To install the script in the system, copy it to the appropriate subdirectory of the / var / db / scripts directory, then execute the command edit system scripts type file filename to activate it. After activating the script, it can be invoked like any other command allowed by the user in operation mode: op filename arguments . It's nice that the valid parameters of the activated CLI script by pressing "?" Shows exactly the same as for built-in system commands. Help is displayed in the same way, if it is provided for:

  user @ junosbox> op blackhole?
 Possible completions:
   <[Enter]> Execute this command
   <name> Argument name
   detail display detailed output
   set set blackhole community for ip address
   |  Pipe through a command 

Of course, the institution of the user and empowering him with the rights necessary to run the script is the task of the administrator. But she, as a matter of fact, is well documented and every one has come across it at least once.

So up to this point, everything seems to be quite simple. The nuances begin with the writing of the script: the examples from the documentation are somewhat useless, since they implement things that are no less conveniently performed by the built-in commands. Therefore, let me draw your attention to some points to which the authors of the documentation have paid insufficient attention, but in real life they matter:

  1. Remember, JunOS is XML. The most necessary command when writing scripts is show configuration ... | display xml . This is the only way to understand which templates and parameters should be used to build a new configuration or make changes to the existing one;
  2. Documentation is not always true. For example, in SLAX 1.0 it. JunOS did not succeed in declaring a function by using <func: function>, since the interpreter cursed all references to “var”, “param”, etc. within it. At the same time, a similar result was obtained by using custom templates, about which nothing is written in the documentation of Juniper Networks (see example);
  3. In functions, the order of the arguments is important; in templates it does not matter - here their name is important (again, see example);
  4. In XSLT and SLAX it is very bad with variable reuse - the value of a variable, once assigned, cannot change in the general case. Theoretically, this question is removed using mvar (mutable var) instead of var to declare variables, but in some situations their behavior becomes strange (the assigned values ​​are lost). Therefore, since the documentation says that a lot of variables are normal and you just need to take note of this nuance of the syntax, you should probably do so.

Well, and finally:

Example - op / blackhole.slax
 version 1.0;

 ns junos = "http://xml.juniper.net/junos/*/junos";
 ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
 ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";
 ns ext = "http://xmlsoft.org/XSLT/namespace";
 import "../import/junos.xsl";

 var $ arguments = {
   <argument> {
     <name> "set";
     <description> "Set blackhole community for IP address";
   }
 }
 param $ set;

 match / {
   <op-script-results> {
     var $ instance = "INSTANCE";
     var $ community = "65535: 666";
     if ($ set) {
       var $ addr = jcs: parse-ip ($ set);
       var $ ip = $ addr [1] _ "/ 32";
       var $ connection = jcs: open ();
       call set_community ($ connection, $ ip, $ instance, $ community);
       var $ close-results = jcs: close ($ connection);
       if ($ close-results) {expr jcs: output ($ close-results);  }
     }
     else {
       call show_community ($ community);
     }
   }
 }

 template set_community ($ connection, $ ip, $ instance, $ community) {
   var $ configuration = <configuration> {
     <routing-instances> {
       <instance> {
         <name> $ instance;
         <routing-options> {
           <static> {
             <route> {
               <name> $ ip;
               <discard>;
               <community> {<name> $ community;  }
             }
           }
         }
       }
     }
   }
   var $ commit-options: = {<commit-options> {<log> "setting blackhole community for" _ $ ip;  }}
   var $ results: = {call jcs: load-configuration ($ connection, $ configuration, $ commit-options);  }
   copy-of $ results;
 }

 template show_community ($ community) {
   var $ command = {<command> "show route community" _ $ community;  }
   var $ results = jcs: invoke ($ command);
   copy-of $ results;
 }

References:

Source: https://habr.com/ru/post/304578/


All Articles