List all subnets in use by hosts in Foreman

The College of Engineering has been running a Foreman service for tenants within our college (and some outside) for a few years now. Our involvement in cultivating and growing the Campus Linux Services (CLS) group has led to one inevitable terminus: It’s time for us to move all of our Puppet-based hosts from one CFM infrastructure to another!

We (the CLS community) will be making a set of posts here to share what we’re learning as we move hundreds of hosts from one environment to another. Several important documents detailing our process have already been posted to the CLS area on Google Team Drive (email for access). One of MANY issues that have to be solved is actually doing the grunt-work of re-creating all Foreman hosts (and their dependencies) in the target infrastructure. This post covers ONE of those dependencies, subnets.

The CLS infrastructure treats subnet data very differently than our Engineering Foreman service did. Instead of synchronizing subnet data from our IPAM system as it’s own separate thing, we now attach subnets to tenant configuration. Take for example the “COEDEAN” (that’s us) tenant configuration:

      members_group: COE-Foreman.Administrators
          vlanid: 213
          vlanid: 37
          vlanid: 912

This is our tenant configuration as of 7/25/2018. At this point, we had a few test machines in a few VLANs, but by no means had every VLAN we provision machines into represented. We needed an easy way to see which VLANs we were actually using so that we could send a pull request to add the VLANs that were missing.

Ruby to the rescue!

We use a Ruby script to call Hammer CLI commands on the source Foreman node. Basically, the gist is to iterate over every host record and scrape the “subnet_name” from it. From that information alone, we have all we need to provision the subnets in the CLS infrastructure. The script is hosted on GitHub, but here it is in its entirety for purposes of discussion in this post:

#!/bin/env ruby

require 'yaml'
require 'optparse'

options = {}
optparse = do |opts|
  opts.banner = 'Usage: foreman_subnets.rb --slug="TENANT"'

  opts.on('-s', '--slug ARG', 'Root-level hostgroup name') do |arg|
    options[:slug] = arg

  opts.on('--no-subnet', 'Find hosts with no subnet') do |arg|
    options[:no_subnet] = true

raise OptionParser::MissingArgument if options[:slug].nil?

host_list = YAML.load(%x{ hammer --output=yaml host list --search="hostgroup_fullname ~ "#{options[:slug]}*"" })

if options[:no_subnet]
  hosts = do |host|
    host["Name"] if host["Operating System"]["subnet_name"].nil?

  puts hosts
  hosts = do |host|
    host["Operating System"]["subnet_name"]

  puts hosts

Using this command, you can do the following:

[root@engr-for-200 scripts]# ruby foreman_subnets.rb --slug=COEDEAN
VLAN209-MN-CE-1 (
VLAN202-BR-MAE-1 (
VLAN254-DAN-6 (
VLAN403-PT1-2 (

Finally, to catch any hosts who aren’t associated with any subnet at all…

[root@engr-for-200 scripts]# ruby foreman_subnets.rb --slug=COEDEAN --no-subnet

These machines will need to be fixed up before they can be migrated.

Up next

I’ll be making a post about exporting host data from one Foreman environment to be able to create new hosts in a separate target Foreman environment.