Thursday, April 24, 2014

Python on the Juniper QFX 5100

I noticed that the Juniper QFX5100 has a python interpreter included in Junos. This is new to the Juniper platforms. From the data sheet this is for management support. I know that Arista allows python scripting on their switches, so my guess is this is supported to keep up with the competition. This is a good first step for Juniper. While slax scripting is powerful, the learning curve for most networking engineers is steep and almost proprietary as you really have to know Junos. Slax is not transferrable to other vendor platforms, while python is.

Juniper's Python is accessible from the shell.

user@QFX5100> start shell

root@QFX5100% python
Python 2.7.1 (r271:86832, Jan 21 2014, 12:38:40)
[GCC 4.2.1 (for JUNOS)] on junos
Type "help", "copyright", "credits" or "license" for more information.

However I don't know what modules bundled in the OS are used to actually access Junos API.

>>> help ('modules juniper')

Here is a list of matching modules.  Enter any module name to get more help.


>>> help ('modules junos')

Here is a list of matching modules.  Enter any module name to get more help.


>>> help ('modules jnpr')

Here is a list of matching modules.  Enter any module name to get more help.

I did find that it does support telnetlib.


>>> help ('modules telnet')

Here is a list of matching modules.  Enter any module name to get more help.

telnetlib

>>> exit()

Which means you can actually access the QFX via telnet. In the below script I telnet to the switch and send a broadcast message

root@QFX5100% python hello.py
                                                                             
Broadcast Message from jnpr@SW3a                                            
        (/dev/ttyp2) at 22:32 PDT...                                        
                                                                             
hello world                                                                  

----------

I'm not when the Junos API will be available or how to access it. I'm guessing there may be some restrictions for this as you wouldn't want have a hacker creating mischief with python scripts.


source code:
root@QFX5100% more hello.py
---------------------                                                                            

import sys
import telnetlib

HOST = "192.168.1.1"
tn = telnetlib.Telnet(HOST)

user = "user"
password = "password"

tn.read_until("login:")
tn.write(user + "\n")

tn.read_until("Password:")
tn.write(password + "\n")

tn.read_until(">")
tn.write('request message all message "hello world" \n')
tn.read_until(">")
tn.write("exit\n")

tn.close

Monday, April 21, 2014

JUNOS scripts on a EX Virtual Chassis needs each potential Routing Engine to have a copy of all scripts in /var/db/scripts/

How do we go about copying these to all devices in a Virtual Chassis?

normally you would use the file copy command:

run file copy /var/db/scripts/commit/script.slax fpc0:/var/db/scripts/commit/

This is a manual process. So let's try to automate this. The name of the test script is called "vc-copy.slax" you could create an argument in the script to pass the file name to the op script.

This script will look at the member id and see which fpcs are present to copy the scripts.


jnpr@EX4200-VC1# run show virtual-chassis status  

Virtual Chassis ID: 030b.bd6f.ed90
                                          Mastership            Neighbor List
Member ID  Status   Serial No    Model    priority    Role      ID  Interface
0 (FPC 0)  Prsnt    BP0208192416 ex4200-48t      200  Master*    3  vcp-0    
                                                                 1  vcp-1    
1 (FPC 1)  Prsnt    BP0208377206 ex4200-48t      199  Backup     0  vcp-0    
                                                                 2  vcp-1    
2 (FPC 2)  Prsnt    BP0208377298 ex4200-48t      198  Linecard   1  vcp-0    
                                                                 3  vcp-1    
3 (FPC 3)  Prsnt    BP0208192367 ex4200-48t      128  Linecard   2  vcp-0    
                                                                 0  vcp-1    

Member ID for next new member: 4 (FPC 4)


jnpr@EX4200-VC1# run file list /var/db/scripts/op

/var/db/scripts/op:
vc-copy.slax

{master:0}[edit]
jnpr@EX4200-VC1# run op vc-copy                          
fpc0:/var/db/scripts/op/vc-copy.slax
fpc1:/var/db/scripts/op/vc-copy.slax
fpc2:/var/db/scripts/op/vc-copy.slax
fpc3:/var/db/scripts/op/vc-copy.slax

{master:0}[edit]
jnpr@EX4200-VC1# run file list fpc1:/var/db/scripts/op
fpc1:
--------------------------------------------------------------------------

/var/db/scripts/op:
vc-copy.slax

{master:0}[edit]
jnpr@EX4200-VC1# run file list fpc2:/var/db/scripts/op  
fpc2:
--------------------------------------------------------------------------

/var/db/scripts/op:
vc-copy.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";

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

match / {
    <op-script-results> {
        <output method = "text"> {
            var $rpc = <command> "show virtual-chassis status";

            var $out = jcs:invoke($rpc);
              
            for-each ($out//member){
         
            var $fpc = ./fpc-slot;
  
            var $strip-space = translate($fpc, ' ', '');
            var $strip-left = translate($strip-space, '( ', '');
            var $strip-right = translate($strip-left, ')', ''); 
            var $result = translate($strip-right, 'FPC', 'fpc');
           

            var $dest = concat ($result, ':/var/db/scripts/op/vc-copy.slax'); 
            expr $dest _ "\n";


            var $ftp = {
                         <file-copy> {
                           <source> "/var/db/scripts/op/vc-copy.slax";
                           <destination> $dest;
                         }
            }
            var $exec = jcs:invoke($ftp);   




            }
            
        }
    }
}

Friday, April 18, 2014

Junos Scripting - Is it possible to pass multiple values as part of the same argument?


For example, assume I want to check something only on certain interfaces.  Can I say something like:
op check-intf <argument> [ ge-0/0/0 ge-0/0/1 ge-0/0/2 ]

 If possible, I assume this would be passed as an array.  If so, what is the syntax to for-each over each element?

you should be able to enclose the multiple values in quotes to make a single string.  You could use jcs:split( " ", $argument ) to separate out the different values from the combined string.


jnpr@EX4200-VC1# run op parse objects "ge-1/0/0 ge-1/1/0 ge-1/2/0" 
field: ge-1/0/0
field: ge-1/1/0
field: ge-1/2/0


script
-----------------------------------


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";
 
import "../import/junos.xsl";
 
var $arguments = {
            expr $objects;
         
    <argument> {
       <name> "objects";
       <description> "enter objects";
    }
 
}
 
param $objects;
 
match / {
    <op-script-results> {
        <output method = "text"> {
            var $field = jcs:split(' ', $objects);        

            for-each ($field) {

               expr "field: " _ . _ "\n";    
            }
        }
    }
}

Tuesday, April 8, 2014

Event script - auto log off on-hold telnet sessions

My customer sees that when a user opens a telnet to Juniper Router but the user doesn't type a username or password, the open telnet stays on-hold for hours.

When the telnet connection-limit is reached (my customer limits the connection-limit to 10), a new telnet session can't be opened and the login prompt doesn't appear.

Look at the following:

Attempt 1:

user@linux:/export/home/admin> telnet 192.168.0.10
Trying 192.168.0.10...
Connected to 192.168.0.10.
Escape character is '^]'.
router-RE0 (ttyp0)
login:
login:
telnet> z
Suspended



Attempt 10:
user@linux:/export/home/admin> telnet 192.168.0.10
Trying 192.168.0.10...
Connected to 192.168.0.10.
Escape character is '^]'.
router-RE0 (ttyp1)
login:
login:
telnet> z
Suspended



Attempt 11:

user@linux:/export/home/admin> telnet 192.168.0.10
Trying 192.168.0.10...
Connected to 192.168.0.10.
Escape character is '^]'.



Is there a way to reduce the time for this on-hold sessions so my customer can authenticate?, under the login class, we have an idle-timeout but this timeout only applies for authenticated sessions.
--

I believe you could use an event script that cleans up the ttys by looking first at system processes

[edit]
user@router# run show system processes | match login
 5461  p0  Is+    0:00.00 login [pam] (login)
 7614  p1  Ss+    0:00.00 login               <<<<<<<<<<<< HERE
 7299  p2  Is     0:00.01 login [pam] (login)

Compares that with system users


user@router# run show system users  
 3:36PM  up 23:04, 2 users, load averages: 0.01, 0.04, 0.00
USER     TTY      FROM                              LOGIN@  IDLE WHAT
user     p0       172.24.67.238                    10:22AM     - -cli (cli)  
user     p2       172.24.67.238                    3:17PM      3 vi logout.slax


then logout the tty (p1) that doesn't have a user

[edit]
user@router# run request system logout terminal p1


user@router# run show system processes | match login  
 5461  p0  Is+    0:00.00 login [pam] (login)
 7299  p2  Is     0:00.01 login [pam] (login)

--------------------
Here's a working script that will boot off only those logins with no matching user

user@router# run show system processes | match login
 9256  p0  Is     0:00.01 login [pam] (login)
 9201  p1  Is+    0:00.01 login [pam] (login)
12474  p2  Is+    0:00.00 login               <<<<<< here
12689  p4  Ss+    0:00.00 login               <<<<<< here

[edit]
user@router# run show system users
 3:27PM  up 1 day, 22:55, 2 users, load averages: 0.02, 0.10, 0.07
USER     TTY      FROM                              LOGIN@  IDLE WHAT
user     p0       172.24.67.238                    10:30AM     4 vi logout.slax
user     p1       172.24.67.238                    10:30AM     - -cli (cli)  

[edit]
user@router# run op logout
tty p2 no matching user logging them off
tty p4 no matching user logging them off

[edit]
user@router# run show system users  
 3:28PM  up 1 day, 22:55, 2 users, load averages: 0.26, 0.15, 0.09
USER     TTY      FROM                              LOGIN@  IDLE WHAT
user     p0       172.24.67.238                    10:30AM     4 vi logout.slax
user     p1       172.24.67.238                    10:30AM     - -cli (cli)  

[edit]
user@router# run show system processes | match login  
 9256  p0  Is     0:00.01 login [pam] (login)
 9201  p1  Is+    0:00.01 login [pam] (login)


I ran this as an op script. You can test this as well. Put the file in the /var/db/scripts/op directory

Then configure the router to point to it.

user@router# show system
scripts {
    op {
        file logout.slax;

    }
}

If this works out, then you can put this in the event-options configuration.


Save the below script as logout.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";

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

match / {
    <op-script-results> {
        <output method = "text"> {

         var $showpid = <command> "show system process" ;
         var $result = jcs:invoke($showpid);
         /* break up all the lines */
         var $line = jcs:break-lines($result);
            for-each ($line) {
                 var $test = current();
                 /* find all processes with name login */
                 if (contains($test, "login")) {
                              /* var $proc = substring-before ($test, "Is"); */
                              var $tty = jcs:regex ("p[0-9]", $test);
                              var $users = <command> "show system users";
                              var $user = jcs:invoke ($users);
                              if ( not (contains ($user, $tty)) ) {
                                expr "tty " _ $tty _ " no matching user logging them off  \n";
                                var $rpc = <command> "request system logout terminal " _ $tty;
                                var $response = jcs:invoke($rpc);  
                                   
                              }
                 }
             }

        }
    }
}


-------------------------------------



user@router# show event-options
policy TIME-OUT {
    events SYSTEM;
    attributes-match {
        SYSTEM.message matches ".*Number of telnet connections at max limit.*";
    }
    then {
        event-script logout.slax;
    }
}

[edit]
user@router# show system
services {
    telnet {
        connection-limit 10;
    }
}


Jul 22 17:20:39  router inetd[1053]: Number of telnet connections at max limit (10) Jul 22 17:20:39  router login: LOGIN_INFORMATION: User user logged in from host 172.24.67.238 on device ttyp0 Jul 22 17:20:39  router root: invoke-commands: Executed /tmp/evt_cmd_kQxqou, output to /tmp/evt_op_WP7zmx in text format Jul 22 17:20:39  router root: transfer-file: Transferred /tmp/evt_op_WP7zmx Jul 22 17:20:42  router xntpd[1079]: bind() fd 9, family 2, port 123, addr 128.0.0.1, in_classd=0 flags=0 fails: Can't assign requested address