Thursday, 25 June 2015

Set CPU family for a specific VM on oVirt 3.5

I have been playing around with an oVirt test lab now on and off for a couple of months and I must say I love it. The community is active and helpful (either via the users@ovirt.org mailing list or #ovirt on irc.oftc.net using IRC) and the software is continuously upgraded and improved by a well organised development team.

One of the features that is missing in version 3.5 is the ability to set the CPU family on a VM to CPU family with a lower set of features than the current hardware (actually the cluster but that is likely to be set to the same thing). Sounds daft, why would anyone want to do this, well you can test if a VM would work on a older class of hardware I suppose. What I wanted it for a bug with Windows 10 previews that throw a SYSTEM THREAD EXCEPTION NOT HANDLED which prevents installing on a SandyBridge CPU on the KVM hypervisor (the hypervisor used by oVirt). The answer is to fall back to the older CPU family of Westmere. To do that on stock 3.5 oVirt you need to set the whole cluster to that CPU family, there is not option to do just a VM.

I presumed someone else had figured a way to do this so asked on the #ovirt IRC channel, no one knew but did try to put me in touch with some who might help but they were away. I then emailed the mailing list and got back two prompt answers.  First saying I need to use something called VDSM hooks and another says the feature will be available in 3.6 if I can wait or upgrade before final release.

I looked up VDSM hooks, these are (mostly python) scripts that can effect certain events on a running ovirt engine. The event I needed to look at is before_vm_start, following instructions and modifying the example script on this page I was able to customise my lab to enable CPU setting.
Create the following script on each host in the cluster.
vi /usr/libexec/vdsm/hooks/before_vm_start/50_cpufamily
Paste in the following text:
#!/usr/bin/python

import os
import sys
import hooking
import traceback


if os.environ.has_key('cpufamily'):
    try:
       domxml = hooking.read_domxml()   #here we read the VM XML into the domxml variable
       vcpu = domxml.getElementsByTagName('cpu')[0] #find and read the CPU definition in the VM XML
       sys.stderr.write('cpufamily: Changing cpu family to: %s\n' % os.environ['cpufamily'])  #sys.stderr.write is caught by vdsm and logged into vdsm.log for debugging
       e = domxml.createElement('model')
       txt = domxml.createTextNode(os.environ['cpufamily'])
       e.appendChild(txt)
       modelnode = vcpu.getElementsByTagName('model')[0]
       vcpu.replaceChild(e,modelnode)
       hooking.write_domxml(domxml)                       #and write to the altered domxml
    except:
       sys.stderr.write('cpufamily: [unexpected error]: %s\n' % traceback.format_exc())
       sys.exit(2)

Change the script to be executable:
# chmod +x /usr/libexec/vdsm/hooks/before_vm_start/50_cpufamily
If you are using the hosted engine setup on one of the hosts put the HA agents into global maintenance with:
# hosted-engine --set-maintenance --mode=global
Then on just the engine machine:
# engine-config -s UserDefinedVMProperties='cpufamily=^(Conroe|Penryn|Nehalem|Westmere|SandyBridge)$' --cver=3.5
# service ovirt-engine restart
Take the ha agents out of maintenance mode:
# hosted-engine --set-maintenance --mode=none
This should now allow you to log into the engine GUI, edit a VM (or create a new one) under advanced options go to custom properties and select the cpufamily key and set an appropriate CPU family, the next time you boot your VM will only have access to those CPU features relevant to the CPU family you have selected.