This article provides an introduction to the nature of Guest Customization support for AHV VMs and how one can configure this using the Prism Central v3 APIs.
Overview
Using the AHV Image Services and Clone functionality, users can reduce the amount of time needed to bring up an AHV Virtual Machine (VM). As per their organisational security standards, users can have base images with the necessary software and patches installed in the Guest OS. However, in certain deployments, there is a need to make changes specific to a particular instance. This can be achieved via the Guest Customization functionality which allows users to have OS customization while creating a VM from AHV Image or Cloning an existing VM. The mechanism to customize a guest depends on the Guest OS; Cloud Init is used for customizing Linux Guests and SysPrep is used for Windows Guests.
In order to customize guests via v3 API, one has to make a POST request to the vms endpoint. Please refer to the vms POST documentation for how the complete VM spec has to be populated for creating a VM. Let’s look at examples around how the spec has to be populated when customizing guests.
Pre-Requirements
Having Boot Disks Available as Images in Prism Central
In the course of this write up it’s assumed that any image referred to has been uploaded, registered and is available via Image Service on Prism Central. For information regarding this announceement, please see the Nutanix Community article titled Nutanix Just Made AHV Image Management Painless. The UUID references to the said images can be obtained by issuing a LIST API request on images endpoint. Please refer to the images list endpoint documentation for information on the required payload and expected response.
Converting Scripts to base64 Format
The API expects the customization payload to be encoded in base64. There are multiple ways to convert a script to an encoded format; here’s an example to do it in Python. Please modify to fit your environment:
import base64
import sys
def convert(file_name):
try:
with open(file_name) as f:
buf = f.read()
except IOError:
print "Error reading data from %s" % file_name
sys.exit(1)
encoded_buf = base64.b64encode(buf)
with open(file_name + "_b64_encoded", "w") as of:
of.write(encoded_buf)
print "Encoded data present in %s" % file_name+"_b64_encoded"
def usage():
print """Usage: ./b64_encode
This will create a new file _b64_encoded with the encoded data."""
if __name__ == "__main__":
if len(sys.argv) != 2:
usage()
sys.exit(1)
file_name = sys.argv[1]
convert(file_name)
Customizing AHV VMs
AHV VMs can be customized using the one of Cloud Init or SysPrep, hence, it’s expected to have either one of ‘cloud_init’ or ‘sysprep’ sections of ‘guest_customization’ subschema to be filled. Note that one can only specify customization configuration during VM creation and it can’t be modified after the VM is successfully created. Attempting to specify configuration during a VM update operation would be considered an invalid operation and will result in an error.
Configuring Guest Customization on a Windows VM
In order to customize a Windows guest, we use Microsoft’s Sysprep (System Preparation) format. Detailed information on Sysprep itself can be found in the official Sysprep documentation.
Briefly, there are two types of customization possible.
Prepared
Using a Windows guest image that has been sysprep-ed previously. For detailed information on how to use Sysprep for this purpose, please see the official Sysprep documentation.
Relevant parts of the VM spec in this case:
"resources" = {
[...]
"guest_customization":{
"sysprep":{
"install_type": "PREPARED",
"unattend_xml": ""
}
},
"disk_list": [
{
"data_source_reference": {
"kind": "image",
"uuid": ""
},
"device_properties': {
'device_type': 'DISK'
},
}
],
[..]
}
Fresh
This method involves using a bootable Windows guest image which has not been Sysprep-ed. This would require an additional Nutanix Virt-IO ISO containing the drivers to discover the devices.
Below are the relevant parts of the VM spec which makes sure the Sysprep is configured to reflect it’s a fresh installation, above mentioned guest images are referenced along with the necessary boot order:
"resources": {
[...]
"guest_customization": {
"sysprep": {
"install_type": "FRESH",
"unattend_xml": ""
}
},
"boot_config": {
"boot_device": {
"disk_address": {
"device_index": 0,
"adapter_type": "IDE"
}
}
},
"disk_list": [
{
"data_source_reference": {
"kind": "image",
"uuid": ,
"name": "name of the iso"
},
"device_properties": {
"device_type": "CDROM"
},
"disk_size_mib": 1
},
{
"data_source_reference": {
"kind": "image",
"uuid": ,
"name": "Nutanix-VirtIO-1.1.1.iso"
},
"device_properties": {
"device_type": "CDROM"
},
"disk_size_mib": 1
}
]
}
}
Windows Example with Sysprep
Below is a Sysprep script which helps with the following tasks on a Windows guest:
- Assigning hostname
- Disabling DHCP
- Sets a static IP address and DNS configuration
<?xml version="1.0" encoding="UTF-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="specialize">
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<ComputerName>MyHostName</ComputerName>
<RegisteredOrganization>MyOrg</RegisteredOrganization>
<RegisteredOwner>Acropolis</RegisteredOwner>
<TimeZone>Pacific Standard Time</TimeZone>
</component>
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-TCPIP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<Interfaces>
<Interface wcm:action="add">
<Identifier>Ethernet</Identifier>
<Ipv4Settings>
<DhcpEnabled>false</DhcpEnabled>
<RouterDiscoveryEnabled>true</RouterDiscoveryEnabled>
<Metric>30</Metric>
</Ipv4Settings>
<UnicastIpAddresses>
<IpAddress wcm:action="add" wcm:keyValue="1">10.15.193.11/24</IpAddress>
</UnicastIpAddresses>
<Routes>
<Route wcm:action="add">
<Identifier>10</Identifier>
<Metric>20</Metric>
<NextHopAddress>10.15.193.1</NextHopAddress>
<Prefix>0.0.0.0/0</Prefix>
</Route>
</Routes>
</Interface>
</Interfaces>
</component>
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<UseDomainNameDevolution>true</UseDomainNameDevolution>
<DNSDomain>nutanix.com</DNSDomain>
<Interfaces>
<Interface wcm:action="add">
<Identifier>Ethernet</Identifier>
<DNSDomain>nutanix.com</DNSDomain>
<DNSServerSearchOrder>
<IpAddress wcm:action="add" wcm:keyValue="1">8.8.8.8</IpAddress>
</DNSServerSearchOrder>
<EnableAdapterDomainNameRegistration>true</EnableAdapterDomainNameRegistration>
<DisableDynamicUpdate>true</DisableDynamicUpdate>
</Interface>
</Interfaces>
</component>
</settings>
<settings pass="oobeSystem">
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<UserAccounts>
<AdministratorPassword>
<Value>MyPassword123</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
<OOBE>
<HideEULAPage>true</HideEULAPage>
</OOBE>
</component>
<component xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguageFallback>en-us</UILanguageFallback>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
</settings>
</unattend>
Configuring Guest Customization on a Linux VM
The vms API allows users to customize a VM running Linux guests using Cloud-init Config Drive V2 format. Please see the official Cloud-Init documentation for detailed information on Cloud-Init formatting. The current API allows users to configure Cloud metadata and User data.
In this example, relevant parts of the VM spec are as follows:
"resources" = {
[...]
"guest_customization":{
"cloudinit":{
"meta_data": "",
"user_data": ""
}
},
"disk_list": [
{
"data_source_reference": {
"kind": "image",
"uuid": ""
},
"device_properties': {
'device_type': 'DISK'
},
}
],
[..]
}
Linux Example with Cloud-Init
The following Cloud-Init Config Drive v2 YAML script demonstrates the following tasks:
- Create new users
- Create user groups
- Adds resolv.conf
- Runs a few commands
#cloud-config
# Add groups to the system
# The following example adds the ubuntu group with members 'root' and 'sys'
# and the empty group cloud-users.
groups:
- ubuntu: [root,sys]
- cloud-users
users:
- default
- name: foobar
gecos: Foo B. Bar
primary_group: foobar
groups: users
manage_resolv_conf: true
resolv_conf:
nameservers: ['8.8.4.4', '8.8.8.8']
searchdomains:
- foo.example.com
- bar.example.com
domain: example.com
options:
rotate: true
timeout: 1
runcmd:
- [ ls, -l, / ]
- [ sh, -xc, "echo $(date) ': hello world!'" ]
- [ sh, -c, echo "=========hello world'=========" ]
- ls -l /root
Wrapping Up
We hope the above information has been informative and helpful.
Thanks for reading and have a great day! 🙂