TCL/Expect read from file (executing custom config in bulk)

At this point in time I need a nifty little tool to configure some ports on network devices. But the challenge is that the ports differ from device to device.

In order to do this I have to create a script to harvest data out of the network. Output files will be parsed through combination of bash/grep/awk.

The main thing I could not get my head around was how to read a file and hand data over to a spawned ssh session.

As a proof of concept I created a file with commands to execute on another node. And also a tiny tcl script to open a ssh session, logon, read the commands file, close the ssh session.

Contents of my commands.txt file is below, nothing fancy just to get the proof of concept.

du -h

Open a file to read , read into $file_data, close the file.

set fp [open "commands.txt" r]
set file_data [ read $fp ]
close $fp

Split the $file_data into usable chunks.

set dataln [ split $file_data "\n"]

Walkthrough the $dataln and give the data to the ssh session.

# loop through $dataln and store data in $line
foreach line $dataln {
  # just an obvisous expectation.
  expect ">" {
   # send the $line to ssh  session..
   send "$line\r"

Complete test.tcl (awesome name right 😉 )


set nethost [lindex $argv 0]
set netuser [lindex $argv 1]
set netpass [lindex $argv 2]

# -- file open magic here    
set fp [open "commands.txt" r]
set file_data [ read $fp ]
close $fp

# -- file_data read here , split to dataln
set dataln [ split $file_data"\n"]

spawn ssh "$netuser@$nethost"

expect "assword"{
   send "$netpass\r"

# -- walk through $dataln
foreach line $dataln{
   expect ">"{
   send "$line\r"

expect ">"{
   send "exit\r"

# in case exit fails...

This is the final result;

$ ./test.tcl  DS 1user SecRet
spawn ssh 1user@DS
1user@ds's password:  

BusyBox xxxxxxxxxxx
Enter 'help' for a list of built-in commands.

DiskStation> du -h
8.0K    ./.ssh
--//output omited for brevity//--
408.0K  ./script-test
496.0K  .
DiskStation> uname
DiskStation> exit
Connection to ds closed.

As you can imagine a little work still has to be done. Like creating a several files with configuration. While executing the bash/tcl wrapper handing over the config file as an argument. But that shouldn’t be to hard. Maybe something thing like this; (of course some sanity check have to be build in)

ls *config.txt |
while read file 
   echo "./wrapper.tcl ${file%_*} user pass $file"
Posted in CCNP | Tagged , | Comments Off on TCL/Expect read from file (executing custom config in bulk)

PDF tools on Ubuntu

Ubuntu is my preferred operating system. I even use this OS on my work computer. This choice has some minor down sides. Most business software is developed for windows environments. But fortunately most commercial software on windows has an opensource alternative. This is also the case for tools for handling pdf files.

A little line up of tools that I frequently use;

Document Viewer – gui

This is the default pdf viewer on ubuntu 20.04lts. It does the job splendidly.

PDFArranger – gui

This is my go-to tool for merging pdf’s together. rotate or discarding pages.

PDFinfo – commandline

Also a ubuntu standard application. This shows the basic information of the pdf including page dimensions in points and standardized page formats ( A0, LEGAL etc.).

pdfposter – command line

This is a little harder to explain in one sentence. Lets say you want to print a A3. But all you have access to is an A4 printer. With pdfposter you can create a ‘poster’ pdf file according to your requested dimensions.

The command would look like ;

pdfposter -mA4 -pA3 source.pdf destination.pdf

Be sure to set the -p (postersize) otherwise the pdf page dimension is used.

It would be nice if a tool exsisted that would combine PDFArranger, pdfposter and document viewer. Until than, I have to switch between those tools.

Posted in CCNP | Comments Off on PDF tools on Ubuntu

European situation – Special military operation

My father was born in 1935. He lived his childhood in Rotterdam. As a 5 year old he expirienced firsthand the invasion of German troops of his beloved city. The expirience of the entire war that followed, hunted him for the rest of his live.

Now, seventy odd years later, the world politics have changed. Several mechanisms were created to prevent war and to prevent civilian suffering.

We are at a point in time where UN Security Council is held hostage by one of its permanent members, by forced of veto.

Nato is paralyzed because Russia threatens to use nuclear force.

Russian foreign policy is best described as mushroom management. Keep ‘m in the dark and feed them shit. There is no accountability of their actions.

Time and time again Russia tells the world that Russia is not to blame.

  • Flight MH17 is shot down – Ukraine is to blame. Whilest a Russian operated BUK was used.
  • Crimea was annexed by soldiers without name tags and country patch on their uniform. After occupation the name tags and country patches were attached to the uniforms. Violation of the Geneva convention.
  • Hospitals and childcare facilities are bombed, Russia told the world that this is the work of Ukrainian army.
  • Civilians are shot down execution style, hand bound on their backs. Later their corpses burned. Russia tries to persuade the world that this is staged. And that actors are used to create a scene like this.

Russian government can only be trusted to create smoke screens to cover the ugly truth. For example the phrase “special military operation”. If it quacks like a duck and walks like a duck it must be a duck. Hence, an invasion is an invasion and a war is a war.

UN and Nato are seemingly paper warriors. This makes me thing of a history lesson I had from a very passionate teacher.

Otto von Bismarck: Nicht durch Reden und Majoritätsbeschlüsse werden die großen Fragen der Zeit entschieden sondern durch Eisen und Blut.

The biggest questions of our time cannot be resolved by reason or majority decisions but by iron and blood.

Being a child of a WorldWar 2 child, I think we have seen enough to start acting. Provide more weapons e.g. handheld, drones, tanks, fighters.

Eisen und Blut is the only language Mr. Putin understands. Off course I am afraid of a nuclear war. BUT so is Putin.

Posted in CCNP | Comments Off on European situation – Special military operation

Laptop battery status

Recently the battery of my laptop is play up. It is constantly hungry for a powercord. And this is not the reason why I am using a laptop. The battery health was rapidly declining.

So I installed a new laptop battery, An now I want to check the battery health periodically.

The command upower can give information about know battery held devices.

upower -i $(upower -e | grep 'BAT')

This commend present much information if not all information about the battery. To entertain my needs I only need;

upower -i $(upower -e | grep 'BAT') | egrep "design|full:"
Posted in CCNP | Tagged , , | Comments Off on Laptop battery status


I found this little script on internet to get a list of all the usb connected devices. Unfortunaltly this script provides too much information for what I need it to do.

Original script can be found on : Pay stackexchange a visit, it is an very informative site.

The script has been slightly altered to fit my needs. I only want to see the TTY interface, if any.

Original code :


for sysdevpath in $(find /sys/bus/usb/devices/usb*/ -name dev); do
        devname="$(udevadm info -q name -p $syspath)"
        [[ "$devname" == "bus/"* ]] && exit
        eval "$(udevadm info -q property --export -p $syspath)"
        [[ -z "$ID_SERIAL" ]] && exit
        echo "/dev/$devname - $ID_SERIAL"

As mentioned before, this provides too much information. Information of all connected usb devices. All I had to do is change a filter which exludes “bus/” but now only include “tty”.

This is the altered script I use;


for sysdevpath in $(find /sys/bus/usb/devices/usb*/ -name dev)
(      syspath="${sysdevpath%/dev}"
       devname="$(udevadm info -q name -p $syspath)"

       # I only want to see tty related devices.
       #no webcam, no mouse, no keyboards just tty interfaces
       [[ "$devname" != "tty"* ]] && exit

       # enrich the info with vendor/model information
       eval "$(udevadm info -q property --export -p $syspath)"
       [[ -z"$ID_SERIAL" ]] && exit
       echo -e"/dev/$devname\t-- $ID_SERIAL"

If a tty device is found you will get an outlut like ;

robert@laptop:~$ lstty                        
/dev/ttyUSB0    -- FTDI_FT232R_USB_UART_<some identifier>

This script is placed in ~/bin. With the proper rights to execute the script; “chmod +x ~/bin/lstty”. Since bash is defined in the shebang, there is no need to keep the “.sh” suffix. But be carefull when no tty device is found, no output will be given.

Posted in CCNP | Tagged , , | Comments Off on lstty

Bash: IF shorthand

We all know how to make an if then else decision in programming or scripting languages. In bash , and most other languages it is possible to reduce several lines of code to a one-liners. Lets call that short-hand.

A typical if then else

if [ $num -eq 4 ] 
    echo "variable num equals 4"
    echo "variable num contains a different value"

This can also written down in short-hand. The basic construct is [[ test ]] && actions_if_true || action_if_false

The code looks like this;

[[ $num -eq 4 ]] && echo "variable num equals 4" || echo "variable num contains a different value"

Posted in CCNP | Tagged | Comments Off on Bash: IF shorthand

Remove SSH host keys from known_hosts file

As a nwetwork admin you have to replace hardware because it’s faulty or of old age. After replacing the hardware , you will be warned of a man-in-the-middle attack while gaining access using SSH.

Warning looks like:

$ ssh some-host
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:<super long, ECDSA finger print here>.
Please contact your system administrator.

When you are sure that the man-in-the-middle warning is caused by hardware replacement proceed by removing the old ssh key. If you are NOT sure DO NOT proceed and find the root-cause of this warning.

Proceed by removing the old key using the following command;

ssh-keygen -R <hostname>

Now you can get access using SSH. But first you will be prompted to add a new ssh-key to your known-hosts file.

Comments Off on Remove SSH host keys from known_hosts file

Multiple patterns with expect. Handling a SSH login dialog.

When automating network tasks, you a can hit a node which you have not logged on earlier. Using SSH you will end up in a dialog where the ssh client wants to add ssh-key to the known key list. You will hit a wall when using expect in a rather unsophisticated way, e.g.:

expect "this"
send "that\r"

Expect provides a way to conditionally send answers;

expect {
      "pattern_a" { send "answer_a\r"}
      "pattern_b" { send "answer_b\r"}

This resembles a construction like switch/case known in other programming/scripting languages. Like any other proper switch/case statement there is a default to act like a catch all. Like most programming/scripting languages it is possible to nest statements. Take a look at the next code ;

expect {
  "outer pattern_a" { 
    send "answer_a\r"}
  "outer pattern_b" {
    send "answer_b\r"
    #nesting starts------------------------
    expect {
      "inner pattern" {
        send "answer inner pattern\r" }
      default {
        send_user "Inner pattern not found\r"
        exit }
    #nesting ends--------------------------
  #default behavior defined below.
  default {
    send_user "Pattern not found, bugging out\n"

Nesting easily confuses the code, keep your code clearly readable. Provide comments in your code, to enable you to troubleshoot your own code in the future.

For a ssh login dialog you need to program the following steps;


set user uberuser
set passwd SuPeRseCriT
# grab the first argument and use it as var $host
set host [lindex $argv 0]

#set the timeout to a pleasantly low number, but not too low. 
set timeout 2

spawn ssh $user@$host

#--login shizzle starts here -------------------------------------------------
expect {
  "yes/no" {
    send "yes\r"
    #-- nesting start -- yes/no -> passwd dialog -----------------------------
    expect { 
      "assword:" {
        send "$passwd\r"
        #catch all undefined patterns
        default {
          send_user "Login failure\n"
      #-- nesting ends -- yes/no -> passwd dialog ----------------------------
      # catch all undefined patterns
       default {
         send_user "Login failure\n"
   "assword:" {  
     send "$passwd\r"
     expect {
       ">" { exp_continue }  
       default { 
         send_user "Login failure\n"
  default {
    send_user "Login failed\n"
#--login shizzle ends here----------------------------------------------------

# Your magic goes here.. or
# and type the commands your self 😀

This code will effectively handle the ssh yes/no dialog. I tend to keep my code as tidy as possible in regards of comment, tabs and braces. This helps read the code afterwards.

It may seem redundant to this many “default” sections, but you want to handle wrong usernames as well. Which is handled implicit.

Hopefully this will help you understanding expect somewhat more.

Posted in TCL/Expect | Comments Off on Multiple patterns with expect. Handling a SSH login dialog.

Custom logfile on Juniper

Recently I placed a switch for a project. And now I want to see how often the switch is used by wired users. Lets for argument sake asume interface bounce is not handled by the standard log files.

Juniper provides a a way to create a logfile for your specific needs. Only thing you need to do is create a regular expression to catch the event. Interface bounce events are recognized by the keyword “ifOperStatus”

set system syslog file interface-logs any any
set system syslog file interface-logs match ifOperStatus
set system syslog file interface-logs archive size 500k
set system syslog file interface-logs archive files 20
  • File name is defined by file interface-logs
  • Filter is defined by match ifOperStatus
  • Archive size is defined by archive size 500k
  • and archive history is defined by archive files 20

This does the trick,. But an interface up event results is two log entries. One for the interface and one for the attached vlan subinterface.

ninja@juniper-ex3400> show log interface-logs | trim 25
juniper-ex3400 mib2d[15326]: SNMP_TRAP_LINK_DOWN: ifIndex 665, ifAdminStatus up(1), ifOperStatus down(2), ifName xe-0/2/0
juniper-ex3400 mib2d[15326]: SNMP_TRAP_LINK_UP: ifIndex 665, ifAdminStatus up(1), ifOperStatus up(1), ifName xe-0/2/0
juniper-ex3400 mib2d[15326]: SNMP_TRAP_LINK_UP: ifIndex 666, ifAdminStatus up(1), ifOperStatus up(1), ifName xe-0/2/0.0

A nice to have would be to filter out the vlan subinterfaces. The solution I came up with is replace the match statement with;

set system syslog file interface-logs match "ifOperStatus[ 0-9a-zA-Z(),-/]{1,}\/[0-9]{1,}$"

Now you only have one down notification and one up notification.Configuration looks like ;

set system syslog file interface-logs any any
set system syslog file interface-logs match "ifOperStatus[ 0-9a-zA-Z(),-/]{1,}\/[0-9]{1,}$"
set system syslog file interface-logs archive size 500k
set system syslog file interface-logs archive files 20

Et voila… you have created a logfile for your own specific purpose. The only problem I experience is creating a propper regexp to catch the log message. Luckily there are some tools available on the internet such as which you can use to test run your rexexp skills.

Posted in Commands, Juniper | 2 Comments

Pre-shared key on Cisco ASA55x0

Cisco devices store password crypted when “service password-encryption” is turned on. You should enable this on your network equipment.
You want to give imposters a hard time, so this is behavior you want.

Last week I found out a astonishing thing…

I configured a vpn on a Cisco ASA55x0. And misplaced the password.

I had two options;

  1. give the vpn a new pre-shared key,
  2. start a little search to find a loop hole. I decided the latter.

Show run or show start gives me, albeit in a decent way, the “finger“. The output is pre-shared key *, just an asterik nothing more nothing less. It gave the a gut feeling that pre-shared key a somewhere parsed and replaced by “*”. I know there is another way to read the config file.

more system:running-config

This did the trick. The proper pre-shared key was found after a little tinkering with pipe subcommands.

In hind sight; “show” parses the config file and replaces the password information. Whereas “more” does not parse the config file, it shows the content.

I do not know which software versions are affected. But I do know that you must keep access to the nodes restricted to the people and systems that  must have access. In other words: keep your config files and access to the config files safe.

Posted in security | Comments Off on Pre-shared key on Cisco ASA55x0