Our team is in the process of rearchitecting the test and develop infrastructure and we needed a way to easily install new OSs. This installation will be done in both physical nodes and Virtual Machines. To do this we selected The Foreman as the installation server.
For physical nodes we use the standard foreman workflow where we add a new host, select its OS and install the OS. For virtual machines we wanted it to be a bit more flexible and control it from OpenNebula itself. The idea is to configure the different operating systems in foreman and let our developers select the OS that was going to be installed in the machine.
To do this we have a hook that is able to communicate with foreman and register new hosts in foreman when a VM with certain parameters is created. The parameters that we can add in the template are these ones:
- FOREMAN_OS_ID: Operating System identifier in foreman
- FOREMAN_SUBNET: Network where the VM is going to start
The subnet is provided as we have two networks in our infrastructure. The hook will only run when FOREMAN_OS_ID is found in the template.
This is the hook we have added to OpenNebula. Bear in mind that this is a work in progress and we want to make it more straight forward for the user, like selecting the OS by its name and not a number.
[code language=”ruby”]
#!/usr/bin/env ruby
# Add OpenNebula ruby library path. Alternatively you can install OpenNebula
# ruby gem
$: << ‘/usr/lib/one/ruby’
require ‘rubygems’
require ‘foreman_api’
require ‘opennebula’
require ‘base64’
require ‘rexml/document’
# Parameters received by the hook from OpenNebula
ID=ARGV[0]
TEMPLATE_ENCODED=ARGV[1]
# Log file for script debugging
LOG=File.open(‘/tmp/hook.log’, ‘w+’)
# Change your credentials and endpoint here
CREDENTIALS={
:base_url => ‘http://foreman’,
:username => ‘admin’,
:password => ‘amazingly_strong_password’
}
# In our infrastructure we have two network, here are the IDs of these networks
# in foreman
SUBNETS={
‘building’ => 1,
‘internal’ => 2
}
# There are some values hardcoded for the VMs as we don’t use many different
# parameters but these can also be changed
def create_foreman_host(params ={})
host = ForemanApi::Resources::Host.new(CREDENTIALS)
description={
"host" => {
:name => params[:name],
:mac => params[:mac],
:ip => params[:ip],
:architecture_id => 1, # x86_64
:environment_id => 1, # production
:domain_id => 1, # local
:subnet_id => params[:subnet_id],
:operatingsystem_id => params[:os_id].to_i,
:puppet_proxy_id => 1, # Only one proxy
:hostgroup_id => 1, # We only have one hostgroup
:build => 1, # Enable VM building
:ptable_id => params[:ptable_id],
:medium_id => params[:medium_id]
}
}
host.create(description)
end
def get_foreman_os(id)
os = ForemanApi::Resources::OperatingSystem.new(CREDENTIALS)
res = os.index
res[0].select {|o| o["operatingsystem"]["id"]==id }[0]["operatingsystem"]
end
@client=OpenNebula::Client.new
template_decoded=Base64.decode64(TEMPLATE_ENCODED)
xml=Nokogiri::XML(template_decoded)
vm=OpenNebula::VirtualMachine.new(xml, @client)
LOG.puts vm.inspect
os_id=vm[‘VM/USER_TEMPLATE/FOREMAN_OS_ID’]
subnet_name=vm[‘VM/USER_TEMPLATE/FOREMAN_SUBNET’]
# We only execute the hook when FOREMAN_OS_ID is set in the VM template
exit(0) if !os_id
os=get_foreman_os(os_id.to_i)
# We need to fill medium and ptable values from OS parameters as Foreman uses
# the values from the hostgroup
medium=os[‘media’][0][‘medium’][‘id’]
ptable=os[‘ptables’][0][‘ptable’][‘id’]
subnet=1
subnet=SUBNETS[subnet_name] if SUBNETS[subnet_name]
# Fill VM parameters
info={
:name => vm[‘VM/NAME’],
:ip => vm[‘VM/TEMPLATE/NIC/IP’],
:mac => vm[‘VM/TEMPLATE/NIC/MAC’],
:subnet_id => subnet,
:os_id => os_id,
:medium_id => medium,
:ptable_id => ptable
}
LOG.puts create_foreman_host(info).inspect
# Chill out a bit an let foreman do its job
sleep 5
vm=OpenNebula::VirtualMachine.new(
OpenNebula::VirtualMachine.build_xml(ID), @client)
# Release the VM hold so it can start
LOG.puts vm.release.inspect
[/code]
This hook requires foreman_api gem. Now we add this hook to OpenNebula configuration with this stanza:
[code language=”bash”]
VM_HOOK = [
name = "foreman-create",
on = "CREATE",
command = "/var/lib/one/foreman_create_hook.rb",
arguments = "$ID $TEMPLATE" ]
[/code]
Now to create new VMs we have created an empty qcow2 image that will be used as the disk for new VMs. Making them qcow2 will let us clone them very fast and will be much smaller. We also have a template for all the VMs, something like this:
[code language=”bash”]
OS=[
ARCH="x86_64",
BOOT="network" ]
CPU="1"
MEMORY="768"
DISK=[
IMAGE="empty_10gb_disk" ]
NIC=[
NETWORK="building" ]
GRAPHICS=[
LISTEN="0.0.0.0",
TYPE="vnc" ]
FOREMAN_OS_ID="2" # in our case this is a Ubuntu 12.10
FOREMAN_SUBNET="building"
[/code]
The VM should be launched on hold so we have time to add the host to foreman and configuring DHCP and TFTP servers. At this time we can only do this using the CLI:
$ onetemplate instantiate foreman-base --hold
We can also change the OS to be installed without changing the template
$ onetemplate instantiate foreman-base --hold --raw FOREMAN_OS_ID=1
After the VM is created the hook kicks in, adds the new host to foreman and releases the VM from hold so it can start and be installed. When the installation procedure is finished we can start using the VM or capture it so we can use as a base for other VMs. To do this we can use a disk snapshot (not hot) and shutdown the machine to save the new image.
Things to take into account:
- Add installation of OpenNebula contextualization packages in the Foreman templates so the images are ready to be used in OpenNebula
- Configure puppet, chef or other CMS so the images can serve as basis for your app deployments
Features we want to add to the integration:
- Select OS by name, not id
- Select the subnet from the OpenNebula network so it does not need to be specified
- Automatically hold the VM on startup so Sunstone can be used to install new VMs
- New hook to delete the host from foreman after it is deleted in OpenNebula
You can find the code from this post in this gist.
Any updates on this?
Unfortunately I had no time to look at this any more. We continue to use this integration in the state described in the post as it is only needed to install new OS versions and only two of us do that.
Are you interested on improving this integration? Any specific feature you need? It may be interesting to add this bits to a proper repository an make a call in the mailing list to people interested on contributing or giving ideas.
btw., there are plans to do it the other way:
http://projects.theforeman.org/issues/3643
tom, thanks for pointing it out. These are great news.