Go & Junos

I'm a huge fan of the Go programming language, and it has quickly become my "go to" for pretty much everything related to scripting/programming. I'm also a big fan of the Junos network operating system, from Juniper Networks.

There are a lot of Python, Ruby modules out there that are extremely helpful when automation Junos devices. I decided to write on in Go to try to accomplish the same things. It's very much a work in progress still, but is quite functional at this time.

So let's get started with a basic tutorial of how to use it!

The go-junos Module

This article assumes that you have basic knowledge of Go and writing Go programs.

The go-junos module is one I created to interact with and automate Junos devices. Please visit the GoDoc page for complete documentation on it.

If you don't already have Go installed, download it from their website. I highly recommend taking a look at the following resources as well, to better familiarize yourself with Go:

For the start of our program, we'll need to import the go-junos module. We can easily do this within our script by having the following line under our imports section:

"github.com/scottdware/go-junos"

Next, we will need to connect to our Junos device:

jnpr, err := junos.NewSession(host, user, password)  
if err != nil {  
    fmt.Println(err)
}
defer jnpr.Close()  

Once we have made the connection, let's take a look at what we can do. Say you want to view only the routing-instances section of the configuration, and in text format:

routing, _ := jnpr.GetConfig("text", "routing-instances")  
fmt.Println(routing)  

Your output will then be similar to this:

## Last changed: 2015-01-01 13:00:00 EST
routing-instances {  
    srx-vr {
        instance-type virtual-router;
        interface lo0.0;
        interface reth0.500;
        interface reth0.525;
        interface st0.1;
        routing-options {
            static {
                route 192.168.3.5/32 {
                    next-hop 10.2.2.2;
                    install;
                    passive;
                }
            }
            autonomous-system 88881;
        }
        protocols {
            bgp {
                group vpn {
                    type external;
                    peer-as 99992;
                    local-as 88881;
                    neighbor 10.1.1.1 {
                        import VPN-Import-Policy;
                        export VPN-Export-Policy;
                    }
                }
            }
        }
    }
}

If you use the keyword "full" instead of a configuration section, you will get the entire config back. Output format can be either "text" or "xml."

Let's say you want to compare your current (active) configuration to a given rollback one, then we can do that by using the ConfigDiff() function:

diff, _ := jnpr.ConfigDiff(5)  
fmt.Println(diff)  

This will compare to the rollback 5 configuration, and the output will be just like it is if you were issuing the command on the CLI:

[edit system]
+  name-server {
+      192.168.1.2;
+  }
[edit security address-book global]
     address company.com { ... }
+    address pc1 192.168.20.2/32;
+    address pc2 192.168.20.3/32;

Now that we have compared a couple of configurations, let's delete our old "rescue" config and create a new one based off of the current configuration:

jnpr.Rescue("delete")  
jnpr.Rescue("save")  

Ok, now that we have our rescue configuration set, let's rollback our configuration to a different one:

err := jnpr.RollbackConfig(5)  
if err != nil {  
    fmt.Println(err)
}

We can also rollback to our rescue configuration that we created earlier:

err := jnpr.RollbackConfig("rescue")  
if err != nil {  
    fmt.Println(err)
}

Now that we've worked with rolling back our configuration, let's configure our Junos device with a config file we have saved.

In this example, we'll have our config files commands in set format. You can set it to text or xml as well.

err := jnpr.LoadConfig("C:/Configs/juniper.txt", "set", true)  
if err != nil {  
    fmt.Println(err)
}

If you don't have (or want) your configuration commands in a file, and just want to run them from the script...then we can accomplish that as well:

config := []string{  
    "set interfaces ge-0/0/10 vlan-tagging",
    "set interfaces ge-0/0/10.1138 description \"Remember to escape double-quotes if you use this format!\"",
    "set interfaces ge-0/0/10.1138 vlan-id 1138 family inet address 192.168.1.1/24",
    "set routing-instances srx-vr interface ge-0/0/10.1138",
    "set security zones security-zone trust interface ge-0/0/10.1138",
}

// Or

config := `  
    set interfaces ge-0/0/10 vlan-tagging
    set interfaces ge-0/0/10.1138 description "This style works better as you don't have to escape double-quotes"
    set interfaces ge-0/0/10.1138 vlan-id 1138 family inet address 192.168.1.1/24
    set routing-instances srx-vr interface ge-0/0/10.1138
    set security zones security-zone trust interface ge-0/0/10.1138
`

err := jnpr.Config(config, "set", true)  
if err != nil {  
    fmt.Println(err)
}

The "true" option tells the function that we want to commit our configuration after it is loaded. If you set it to "false," then you will have to issue a commit afterwards, like so:

jnpr.Commit()  

There are a few different commit options we have. All of the following will work:

// Normal commit
jnpr.Commit()

// Check our syntax/config
jnpr.CommitCheck()

// Commit at a specific time
jnpr.CommitAt("23:30:00")

// Commit confirm after 5 minutes
jnpr.CommitConfirm(5)  

You can also load a configuration from an FTP or HTTP server:

err := jnpr.Config("ftp://<username>:<password>@hostname/pathname/file-name", "set", true)  
if err != nil {  
    fmt.Println(err)
}

This is very basic usage, but I hope you can see how helpful this module might be when you are coding in Go, and wanting to interact with Junos devices.

I will be continually adding to this module, and if you see something that you might want, just drop me a line or head over to my Github repo and open an issue/pull request.