Customizing AHV VMs

CUSTOMIZING AHV VMS

Table of Contents

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! 🙂

© 2024 Nutanix, Inc. All rights reserved. Nutanix, the Nutanix logo and all Nutanix product, feature and service names mentioned herein are registered trademarks or trademarks of Nutanix, Inc. in the United States and other countries. Other brand names mentioned herein are for identification purposes only and may be the trademarks of their respective holder(s). This post may contain links to external websites that are not part of Nutanix.com. Nutanix does not control these sites and disclaims all responsibility for the content or accuracy of any external site. Our decision to link to an external site should not be considered an endorsement of any content on such a site. Certain information contained in this post may relate to or be based on studies, publications, surveys and other data obtained from third-party sources and our own internal estimates and research. While we believe these third-party studies, publications, surveys and other data are reliable as of the date of this post, they have not independently verified, and we make no representation as to the adequacy, fairness, accuracy, or completeness of any information obtained from third-party sources.