Simple script to customize VM clones (or OVF template deployments) with new network settings taken from OVF/vApp metadata
OVF Environment is a way to pass arbitrary data to guest VM on power on, widely used to customize network parameters while deploying OVF templates. In form of "vApp properties" it could be also used to customize VM metadata in vCenter and to pass it to VM at startup. This script reads that metadata and updates OS configuration if required. Script is short, relatively easy to extend and not have external dependences like XML parsers, making it easy to integrate it in golden VM templates or OVF images.
See OVF specification and William Lam blog for further details about OVF Environment, and VMware blog post for some earlier example of OS customization script.
Script is intended to be run at system startup to re-configure freshly deployed image or clone (but could be run later manually or be tried in test mode, see below). After getting data from OVF environment it checks for hostname and IP address change. If changed, new IP address and host name are set in
- interface configuration file
- hosts file
- hostname config file
- "root" user description in
/etc/passwd
- some application configs (sshd, nrpe, syslog-ng)
Other optionally configurable vApp parameters include:
- gateway IP address
- DNS domain
- DNS servers
- NTP servers (ntpd and chrony are supported)
- remote syslog server address (syslog-ng and rsyslogd are supported)
- smarthost SMTP server address (for postfix)
Finally, ssh host keys are erased (to be automatically regenerated for new host) and so clone is fully configured at the end of boot process.
Script is tested on Suse Linux Enterprise (11.0 to 15.1) and CentOS 7 (and will refuse to run elsewhere) under VMWare ESXi 5.5 - 6.5 hypervisor. Changes to adapt it to other platforms will be minimal, and there is a special "test" mode to check effects on copy of "/etc" (see source and "test" directory).
It is safe to run this script at every startup: if environment is not changed (or not available for some reason) no changes are performed.
Just copy ovfconf
script to /usr/local/sbin
and install startup scripts:
-
centos 7, sles 12
cp ovfconf /usr/local/sbin/ cp ovfconf.service /etc/systemd/system/ systemctl enable ovfconf.service
-
sles 11
cp ovfconf /usr/local/sbin/ cp boot.ovfconf /etc/init.d/ chkconfig boot.ovfconf on
Example RPM spec for SLES 11 is also provided.
There are no requirements except perl
, open-vm-tools
(classic Vmware Tools will do too)
and usual system tools like sed
.
To simplify cloning between data centers, "network profile" could be assigned to main VM administrative network (later referred as "adm-vm"). Settings for subnet mask, gateway, DNS servers could be obtained from that profile. To configure it, go to "Networking" section of vCenter administrative interface, select "adm-vm" and associate network profile with it with multi-step wizard:
- name: "adm-vm ip profile"
- configure subnet and gateway, no DHCP, configure DNS server addresses, no ip pool
- dns is specified as "10.0.128.1, 10.0.128.2" but passed w/o space in OVF env
- possible separators are comma, semicolon on space
- skip ipv6
- configure DNS domain, no host prefix, search domain is same as, no proxy
After that, some properties like "gateway" could be replaced with their "dynamic" versions: instead of "static String" dynamic "Gateway from " could be used.
Configure properties metadata for vApp before cloning: on VM settings in vCenter go to last page ("vApp options"), enable them. Proceed to create metadata as follows (do not forget to specify current values as defaults, or VM will be reconfigured at startup):
- Ensure ip allocation policy = "manual"
- Expand "Properties", add our props (set "Category" and "Label", do not change rest)
- Category
name
: mandatoryhostname
: static "String" 3-30, default: current hostnamedomain
: static "String" 5-50, default: current DNS domain name- or dynamic "Domain name" from "adm-vm" network
- Category
address
: mandatoryip
: static "vApp IP address" in "adm-vm", default: current IPgateway
: dynamic "Gateway from adm-vm" or static "vApp IP address" in "adm-vm", default: current GW
- Category
services
: optional; will be kept untouched if not useddns
: dynamic "DNS servers" from "adm-vm"ntp
: static String 5-50, default: current NTP servers, comma-separated (there is no ntp property in network profile)syslog
: static String 5-30, default: current syslog server IP or name- "External IP Address" would be better, but there is no "default value" field for it in 5.5 for some reason
relay
: static String 5-50, default: current smarthost
- Category
- "IP Allocation": scheme "OVF environment", "IPv4"
- "Autoring"/"OVF Environment transport": check "VMware Tools"
Manual creation of properties in OVF template is possible too, but usually it is easier to
configure them in vCenter and export template with ovftool
(see below). Usually exported
template requires a bit of manual customization, but most things work.
Better power off VM before cloning. If "host file" is connected to CD-ROM disconnect it
before cloning or copy of this file will be created. Better enable DSA key generation in
/etc/sysconfig/sshd
or older ssh clients will be unable to connect.
Clone machine as usual, do not opt for "customize OS". On step 1e ("Customize vApp properties"), enter new host name and IP address (and rest of params if moving to other domain or network). Review configuration and power on VM.
If everything worked as expected, new IP address will be assigned and visible in vCenter
shortly after boot. If something is amiss, VM will probably boot with old IP address and
hostname (as exact copy of original), investigate log (/tmp/ovfconf.log
) and retry.
ovftool
is command-line utility to work with VM images (not limited to
OVF templates). It could be used to export VM to OVF image and import it again, and also
to clone VMs, extract metadata and so on (see documentation for details), like this:
-
preview VM metadata and properties
ovftool vi://<user>@<vca>/<datacenter>/vm/<vm-name>
-
clone VM from one vCenter to another: only hostname and ip specified, rest of properties assumed to be same (or come from network profile)
ovftool --name=new-vm \ --datastore=host-storage1 --diskMode=thin \ --network=adm-vm \ --prop:"hostname=new-vm" \ --prop:"ip=1.2.3.4" \ vi://user@vca1/dc1/vm/template-vm \ vi://alex@vca2/dc2/host/newhost.domain.com
One small caveat: ovftool
does not work correctly when password contains special
symbols that require HTML escaping, so try to use alphanumeric passwords ("-" is ok too).
Deploying OVF to standalone ESXi host with ovftool
is not reliable: even with
--X:injectOvfEnv --powerOn
it sometimes fail to actually pass OVF info. This could be
worked around with injecting OVF directly into VM config (I've found with idea in William
Lam blog post). Basically, one must
-
clone VM somehow (
ovftool
is easiest way)ovftool --name=newvm \ --X:logLevel=verbose --X:logFile=newvm.log \ --datastore=host2-ds1 --diskMode=thin \ --network=adm-srv \ vi://root@oldhost/template-vm \ vi://root@newhost/
-
perform basic customization like enabling VNC console (just in case)
-
prepare XML file (see "Technical Details" section for format description and example)
-
convert it to one long line by escaping double-quote to "|22", newline to "|0A":
cat ovfEnv.newvm.xml | perl -ane '$_ =~ s/"/|22/g; $_ =~ s/\n/|0A/g; print'
-
add
guestinfo.ovfEnv = "<that blob">
into<vm>.vmx
-
vim-cmd vmsvc/reload <vm>
and start VM
To get OVF environment from hypervisor script calls vmware-rpctool 'info-get guestinfo.ovfEnv'
producing XML like
<?xml version="1.0" encoding="UTF-8"?>
<Environment
xmlns="http://schemas.dmtf.org/ovf/environment/1"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
xmlns:ve="http://www.vmware.com/schema/ovfenv"
oe:id=""
ve:vCenterId="vm-136">
<PlatformSection>
<Kind>VMware ESXi</Kind>
<Version>5.5.0</Version>
<Vendor>VMware, Inc.</Vendor>
<Locale>en</Locale>
</PlatformSection>
<PropertySection>
<Property oe:key="dns" oe:value="1.2.3.1,1.2.3.2"/>
<Property oe:key="domain" oe:value="domain.com"/>
<Property oe:key="gateway" oe:value="1.1.1.254"/>
<Property oe:key="hostname" oe:value="testvm1"/>
<Property oe:key="ip" oe:value="1.1.1.1"/>
<Property oe:key="ntp" oe:value="ntp1.domain.com, ntp2.domain.com"/>
<Property oe:key="relay" oe:value="smtp.domain.com"/>
<Property oe:key="syslog" oe:value="log.domain.com"/>
</PropertySection>
<ve:EthernetAdapterSection>
<ve:Adapter ve:mac="00:50:56:90:1f:50" ve:network="adm-vm" ve:unitNumber="7"/>
</ve:EthernetAdapterSection>
</Environment>
Values for configuration parameters are then extracted and compared with current settings, and configs are changed when necessary.
On every boot copy of that XML will be created in /tmp/ovfEnv.xml
(this could be used as
a template for customization or as debugging aid), log of activity is in /tmp/ovfconf.log
.
- only one interface is supported
- no ipv6 support
- untested on redhat/centos 6.x, sles 12 etc
- only some hard-coded apps and services are supported
- has some assumptions about configuraton file formats
- configuration parametrs (like log file name) are hard-coded too