Is Automating Your Network Like Finding the Lost Book In A Library? How Infoblox Automation Can Hel
[Reprinted with Permission by Scott Ware]
Over the past month-and-a-half, I've shared some articles with you about the go-junos library, mostly regarding how it works. With most things in this industry, I like to see them in action, and how others, as well as myself, might use them on a day-to-day basis.
This post is about just that. I'll show you an example of how I use this library to help simplify and save lots of time when deploying new SRX installations.
A Little Background
I work for a large retailer, with a lot of remote sites. Each year, we add roughly 10-12 new locations. We have this process down so it's pretty "cookie-cutter," but when it comes to deploying the networking gear, there still is a lot of manual configuration that has to be done initially.
A cluster of SRX240's is deployed at each location, and we have many other clusters throughout our company, ranging from the 240's up to the 3600's. To manage all of these, we rely heavily on Junos Space.
We use Infoblox for all of our IP/DNS/DHCP managment.
When we know the new locations that will be built in the upcoming year, we create all of their network subnets in Infoblox. Each location uses a /21, and a co-worker of mine created an Excel spreadsheet to easily lay out the subnets for the new location.
Before Automation...
Once all the new location information is in Infoblox, I would create the SRX configurations (interfaces, zones, etc.) from the subnet information that was populated. I then would create the networks for each location within Junos Space, so when it came time to publish the policy, all the necessary information was already there.
I had some scripts to do a few tasks, but I still had to touch every system in order to either input the information or extract it. Doing this 10+ times, it took me the better part of a few days (weaving in other projects, workload, etc.).
Where go-junos, Infoblox API's Help
Now we'll get into the nuts and bolts of how I use this library to save time on deployments.
So let's recap the steps I need to take:
- Get all the subnet information from within Infoblox regarding each location.
- Build the configuration that I will need to upload/paste into each SRX cluster.
- Create all the networks for each location within Junos Space to be applied to a policy.
Surely there has to be a better way, right? Right!?
So, with the go-junos library, and how well it works with Junos Space, I was able to write a script that takes a given store location ID (numerical value), and create everything from the bullet points I mentioned above.
Get Subnet Information From Infoblox
First I had to query Infoblox and get the subnet information. I did this by using their API and searching for the location ID in the extended attributes field of "subnet_org." I then take that information, and based on the subnet, I easily identify what interfaces need to be created, and in what zones they belong.
With this information I can easily use it later to build the config.
Here's a sample of the Infoblox query. We have a couple of structs
defined which model the format in which we want to place our data in:
type storeNetworks <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">struct</SPAN> { Number <SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">string</SPAN> Networks []storeNetwork } type storeNetwork <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">struct</SPAN> { Vlan <SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">string</SPAN> InterfaceIP <SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">string</SPAN> Network <SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">string</SPAN> Mask <SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">string</SPAN> Zone <SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">string</SPAN> Reth <SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">string</SPAN> }
And here's the function we use to query Infoblox:
<SPAN class="hljs-function" style="max-height: 1000000em;">func <SPAN class="hljs-title" style="max-height: 1000000em; color: #990000; font-weight: bold;">getStoreNetworks</SPAN><SPAN class="hljs-params" style="max-height: 1000000em;">(s, user, password <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">string</SPAN>)</SPAN> <SPAN class="hljs-params" style="max-height: 1000000em;">(*storeNetworks, error)</SPAN> </SPAN>{ storeNumber := leadingZero(s) tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">true</SPAN>, }, } url := fmt.Sprintf(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"<A href="https://infoblox.company.com/wapi/v1.0/network?*subnet_org~:=%s"" target="_blank" rel="nofollow noopener noreferrer">https://infoblox.company.com/wapi/v1.0/network?*subnet_org~:=%s"</A></SPAN>, storeNumber) client := &http.Client{Transport: tr} req, _ := http.NewRequest(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"GET"</SPAN>, url, nil) req.SetBasicAuth(user, password) res, err := client.Do(req) defer res.Body.Close() <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">if</SPAN> err != nil { <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">return</SPAN> nil, err } data, _ := ioutil.ReadAll(res.Body) json.Unmarshal([]<SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">byte</SPAN>(data), &networks) <SPAN class="hljs-comment" style="max-height: 1000000em; color: #999988; font-style: italic;">// Regex to skip subnets we do not care about.</SPAN> <SPAN class="hljs-comment" style="max-height: 1000000em; color: #999988; font-style: italic;">// ** some values ommited **</SPAN> skipRegex := regexp.MustCompile(`^.*(Open|VoIP|WAN|Loopback|Reserved|MPLS).*$`) vlanRegex := regexp.MustCompile(`^.*Vlan(\d+).*[\r\n]*$`) subnets := []storeNetwork{} <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">for</SPAN> _, network := range networks { <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">if</SPAN> skipRegex.MatchString(strings.Trim(network.Comment, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"\r\n"</SPAN>)) { <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">continue</SPAN> } <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">else</SPAN> { <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">var</SPAN> zone <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">string</SPAN> vlans := vlanRegex.FindStringSubmatch(network.Comment) subnet := strings.Split(network.Network, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"/"</SPAN>) net := subnet[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">0</SPAN>] mask := subnet[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">1</SPAN>] iface := gateway(vlans[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">1</SPAN>], net) <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">switch</SPAN> vlans[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">1</SPAN>] { <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">case</SPAN> <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"1"</SPAN>: zone = <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"management"</SPAN> <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">case</SPAN> <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"914"</SPAN>: zone = <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"corp"</SPAN> <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">case</SPAN> <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"915"</SPAN>, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"916"</SPAN>: zone = <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"servers"</SPAN> <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">case</SPAN> <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"920"</SPAN>: zone = <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"lan"</SPAN> <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">case</SPAN> <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"980"</SPAN>, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"981"</SPAN>: zone = <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"vendors"</SPAN> <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">case</SPAN> <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"950"</SPAN>, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"951"</SPAN>: zone = <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"wireless"</SPAN> } subnets = append(subnets, storeNetwork{ Vlan: vlans[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">1</SPAN>], InterfaceIP: iface, Network: net, Mask: mask, Zone: zone, Reth: fmt.Sprintf(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"reth0.%s"</SPAN>, vlans[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">1</SPAN>]), }) } } storeData := storeNetworks{Number: storeNumber, Networks: subnets} <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">return</SPAN> &storeData, nil }
Build The Base SRX Config
Given the information returned from querying Infoblox, I can build my SRX config. Most of this is just looping over the VLAN's/subnets to create the interfaces, security-zones, forwarding-options, routing-instances, etc.
I won't place the whole config here, as it's pretty redundant. All the commands are in "set" format and I just do variable replacement (interpolation) for most of it. Here is a sample of the interface creation code:
<SPAN class="hljs-comment" style="max-height: 1000000em; color: #999988; font-style: italic;">// the buildInterfaces function</SPAN> <SPAN class="hljs-function" style="max-height: 1000000em;">func <SPAN class="hljs-title" style="max-height: 1000000em; color: #990000; font-weight: bold;">buildInterfaces</SPAN><SPAN class="hljs-params" style="max-height: 1000000em;">(buf *bufio.Writer, vlan, iface, mask, reth, network <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">string</SPAN>)</SPAN> </SPAN>{ <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">if</SPAN> vlan != <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"914"</SPAN> { fmt.Fprintf(buf, fmt.Sprintf(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"set interfaces %s vlan-id %s disable family inet address %s/%s\n"</SPAN>, reth, vlan, iface, mask)) } <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">else</SPAN> { fmt.Fprintf(buf, fmt.Sprintf(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"set interfaces %s vlan-id %s family inet address %s/%s\n"</SPAN>, reth, vlan, iface, mask)) } } <SPAN class="hljs-comment" style="max-height: 1000000em; color: #999988; font-style: italic;">// txtBuf is a buffer (bufio.NewWriter()) I write everything to before writing it to the file.</SPAN> fmt.Fprintf(txtBuf, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"set interfaces reth0 vlan-tagging\n"</SPAN>) <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">for</SPAN> _, i := range stores.Networks { buildInterfaces(txtBuf, i.Vlan, i.InterfaceIP, i.Mask, i.Reth, i.Network) }
Create Hosts/Networks in Junos Space
We always have a few specific static hosts that we know will be in each location. Knowing the DNS names, I do a lookup to get the IP, and create the hosts in Junos Space using the AddAddress()
function.
NOTE: I also create all the networks using this same function.
Here's a sample of creating the networks in Junos Space, as well as adding certain ones to (Address) groups we use. The first section is where I do an DNS query to get the IP address of the hosts I need to add.
server := fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"server.st%s.meijer.com"</SPAN>, *store) dns := fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"dns-server.st%s.meijer.com"</SPAN>, stores.Number, *store) windows := fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"windows-server-%s.meijer.com"</SPAN>, stores.Number) serverIP, _ := net.LookupIP(server) dnsIP, _ := net.LookupIP(dns) windowsIP, _ := net.LookupIP(windows) <SPAN class="hljs-keyword" style="max-height: 1000000em; font-weight: bold;">for</SPAN> _, s := range stores.Networks { space.AddAddress(fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"net-st%s-vlan%s"</SPAN>, stores.Number, s.Vlan), fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"%s/%s"</SPAN>, s.Network, s.Mask), <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">""</SPAN>) } space.AddAddress(server, fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"%s"</SPAN>, serverIP[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">0</SPAN>]), <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">""</SPAN>) space.AddAddress(dns-server, fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"%s"</SPAN>, dnsIP[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">0</SPAN>]), <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">""</SPAN>) space.AddAddress(windows-server, fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"%s"</SPAN>, windowsIP[<SPAN class="hljs-number" style="max-height: 1000000em; color: #008080;">0</SPAN>]), <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">""</SPAN>) space.ModifyObject(<SPAN class="hljs-literal" style="max-height: 1000000em; color: #008800;">true</SPAN>, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"add"</SPAN>, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"store-servers"</SPAN>, fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"net-st%s-vlan915"</SPAN>, stores.Number)) space.ModifyObject(<SPAN class="hljs-literal" style="max-height: 1000000em; color: #008800;">true</SPAN>, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"add"</SPAN>, <SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"store-vendors"</SPAN>, fmt.S<SPAN class="hljs-built_in" style="max-height: 1000000em; font-weight: bold; color: #0086b3;">printf</SPAN>(<SPAN class="hljs-string" style="max-height: 1000000em; color: #dd1144;">"net-st%s-vlan980"</SPAN>, stores.Number))
Conclusion
Within a couple of keystrokes and a few seconds, I have all of my template configs generated, and all of my objects and networks published in Junos Space ready to be deployed when the site comes online. It may not seem like much, but we are able to cut our deployment time down by a lot, thus using that time to put towards other projects/resources.
I hope you enjoyed the article. If you would ever like to know more, please feel free to contact me. find the original post here: http://sdubs.org/go-junos-real-world-use-cases/
Categories
- All Categories
- 5.1K Forums
- 4.6K Critical Network Services
- 463 Security
- Visibility and Insights
- Ideas Portal
- Webinars & Events
- 266 Resources
- 266 News & Announcements
- Knowledge Base Articles
- Infoblox Documentation Portal
- Infoblox Blog
- Support Portal
- 4 Members Hub
- 4 Getting Started with Community
- Community Support