Having gone through the exercise of getting Vagrant to build machines with multiple interfaces consistently, I am now ready for the next step.
The next step is being able create multiple interconnected machines. I tested the basic configuration of two machines in a previous blog entry. I could continue on with manually defining machines and their relationships, which becomes a lot of manual editing and book-keeping.
Instead, I wanted to be able to see if it could be done with some simple lists and some code. So... having not programmed in Ruby before (upon which Vagrant relies), I picked up the third edition of 'Beginning Ruby', took a read through, figured out the basics of the language, and was able to successfully program an auto-build configuration.
Two sets of definitions are required. A machine list, and a list of connections between each machine. My test example:
# a list of machines to create within VirtualBox machineNames = ['edge01','edge02','core01','core02','host01'] # a list of connections to be made between machines # this example makes use of a series of point to point links vlans = [ ['edge01','core01'],['edge01','core02'], ['edge02','core01'],['edge02','core02'], ['core01','core02'], ['host01','core01'],['host01','core02'] ]
In the example, there are a number of links, and each link is defined by identifying the machines taking part. For example, one link contains edge01 and core01. This link will have an ip subnet automatically assigned and will have a unique VirtualBox internal network.
By changing the machineNames list and the vlans list, a network of desired topology will be provisioned enabled with the 'vagrant up' command.
Here is the full Vagrantfile listing for the example:
# -*- mode: ruby -*- # vi: set ft=ruby : require 'ipaddr' # a list of machines to create within VirtualBox machineNames = ['edge01','edge02','core01','core02','host01'] # a list of connections to be made between machines # this example makes use of a series of point to point links vlans = [ ['edge01','core01'],['edge01','core02'], ['edge02','core01'],['edge02','core02'], ['core01','core02'], ['host01','core01'],['host01','core02'] ] # track the vlans to be used on each machine class Machine attr_accessor :name, :interfaces def initialize(name) @name = name @interfaces = [] end end # create each machine with an empty set of interfaces machines = {} machineNames.each do |name| puts 'machine name exists: #{name}, over-writing' if machines.has_key?(name) machines[name] = Machine.new( name ) end # the subnet isn't used at the moment, but the class does provide a sequence of addresses class IPAddressing attr_accessor :subnet def initialize(subnet) @subnet = subnet @nextAddress = subnet.succ # don't use .1 as Vagrant emits warning messages end def next @nextAddress = @nextAddress.succ end end # assign vlan names and ip address ranges for each connection ipAddresses = {} ixNet = 101 #provide each link with a unique number vlans.each do |vlan| net = "vlan#{ixNet}" ipAddresses[net] = IPAddressing.new( IPAddr.new "10.0.#{ixNet}.0/24" ) vlan.each do |endPoint| if machines.has_key?(endPoint) machines[endPoint].interfaces << net else puts "endPoint #{endPoint} doesn't exist" if !machines.has_key?(endPoint) end end ixNet += 1 end # generate connectivity information maxConnections = 0 machines.values.each do |machine| maxConnections = (maxConnections > machine.interfaces.length) ? maxConnections : machine.interfaces.length out = "#{machine.name}: " out += machine.interfaces.join(', ') puts out end puts "maxConnections: #{maxConnections}" Vagrant.configure("2") do |config| config.vm.box = "stretch" config.vm.box_url = "file://boxes/stretch.box" # ensure all connections are virtio config.vm.provider "virtualbox" do |v| cnt = 1; while ( cnt <= (maxConnections + 1) ) # the extra one is for the NAT interface v.customize ["modifyvm", :id, "--nictype#{cnt}", "virtio"] cnt += 1 end end # provision the machines, NAT is automatically provisioned, and is not listed machines.values.each do |machine| config.vm.define machine.name do |instance| instance.vm.hostname = machine.name machine.interfaces.each do |interface| ipAddress = ipAddresses[interface].next.to_string instance.vm.network :private_network, virtualbox__intnet: interface, ip: ipAddress puts "#{machine.name}: #{interface} = #{ipAddress}" end end end end
Here is the initial part of the output during 'vagrant up':
$ vagrant up edge01: vlan101, vlan102 edge02: vlan103, vlan104 core01: vlan101, vlan103, vlan105, vlan106 core02: vlan102, vlan104, vlan105, vlan107 host01: vlan106, vlan107 maxConnections: 4 edge01: vlan101 = 10.0.101.2 edge01: vlan102 = 10.0.102.2 edge02: vlan103 = 10.0.103.2 edge02: vlan104 = 10.0.104.2 core01: vlan101 = 10.0.101.3 core01: vlan103 = 10.0.103.3 core01: vlan105 = 10.0.105.2 core01: vlan106 = 10.0.106.2 core02: vlan102 = 10.0.102.3 core02: vlan104 = 10.0.104.3 core02: vlan105 = 10.0.105.3 core02: vlan107 = 10.0.107.2 host01: vlan106 = 10.0.106.3 host01: vlan107 = 10.0.107.3 Bringing machine 'edge01' up with 'virtualbox' provider... Bringing machine 'edge02' up with 'virtualbox' provider... Bringing machine 'core01' up with 'virtualbox' provider... Bringing machine 'core02' up with 'virtualbox' provider... Bringing machine 'host01' up with 'virtualbox' provider... ... more stuff follows as the machines are built ....