Monday, December 19, 2011

Ubuntu Desktop 10.11 on Hyper-V piix4_smbus

So, I needed a small virtual machine that had the Hyper-V Integration Components working it (I need the clean shutdown action).

XP is just too large, but usually works well.  Linux, why not.  And the RedHat family just incorporated the latest of the Hyper-V Linux Integration Components.  Perfect.

Now, the flavor.  RedHat?  No, no license.  Fedora? Well, now that I think of it, I forgot this one before.  CentOS?  Great for a server.  Ubuntu.  Yep, I chose Ubuntu.

I created a VM and installed Ubuntu Server without a hitch.  Just be sure to use a Legacy Network Adapter during the install process.

I then created a VM and tried to install Ubuntu Desktop.  Big fail.
First of all, for the life of me I could not get the installer screen to popup.  I could not figure out what was going on.  After all, the server VM went straight to the language selection.

Well, at the bottom of the screen is this cryptic graphic of a keyboard, and equal sign, and what looks to be a person with arms and legs spread.  After a few reboots I interpreted this symbol to mean; “if you want to install, hit enter now”.
Hey, the language selection pops up!

Now, I select my language and hit enter.  Error message, drop to a prompt.  Fail two.

The error:  piix4_smbus 0000:07.3: SMBus base address uninitialized – upgrade BIOS or use force_addr=0xaddr

I only play a Linux guy on TV.  And a bit of searching showed me that this error is common on VirtualBox and the settings change suggested is not a safe thing to try.  Oh, and that I needed to re-compile my initrd.

This is a brand new build and freshly downloaded media.  Crazy talk!

Well, I looked around and figured out a workaround.
I managed to get the installer to load by selecting the “other options” at the installation selection menu (F6) and setting “acpi=off” (highlight it, enter or spacebar, ESC to close the options dialog).

Oh, and a nice feature, on the Windows 8 Developer Preview I actually had a mouse during the GUI installer of Ubuntu Desktop.

Now, enabling the Integration Components..

I did just a couple things.  Bring up a terminal using “Ctrl + Alt + t”
Then you need to do sudo –i to switch to root for the remainder of the session or type sudo at the beginning on each command.

Using nano (I never got any handle on vi and always have fallen back to nano) added 4 lines in /etc/initramfs-tools/modules  ( “nano /etc/initramfs-tools/modules” ) and add:
hv_vmbus
hv_storvsc
hv_blkvsc
hv_netvsc

Save that.  Then generate an updated initramfs with “update-initramfs –u” and reboot.

If you had rebooted prior to this you may have noticed a storage layer inaccessibility error that is now gone.

I then ran “apt-get update” and “apt-get upgrade” – two hours go by…Not necessary, but it does update all packages.

This entire time I have been using a Legacy Network Adapter with the Ubuntu Desktop VM.  After the update completed I shutdown the VM, removed the Legacy Network Adapter, added a (synthetic) Network Adapter and all was good.

Friday, December 16, 2011

Provisioning Server (PVS) Cache on device issues

I recently ran into some issues streaming VMs and taking advantage of the local storage on the hypervisors for the local cache.

Many of these issues were due to my lack of experience with the product, so I am making notes here for future use.

First, if you are not familiar with Provisioning Server or have not taken a look at it for a few revisions, go download the latest version.  There have been a lot of improvements since the last time I looked at it (a few years ago). 

Cache on Device Hard Drive is actually from the PVS feature to stream to bare metal.  In the VM world it allows me to use local storage that is relatively inexpensive (unless you use SSDs – but hey, just another possibility) and just fine when you don’t want to persist the cache disk.

Okay, enough of that, back to my cache disk issue notes.
Firstly, when you create your Template VM, be sure to create the local cache disk as its local drive.

Secondly, don’t forget to format this disk for your streamed OS.  So, for Windows, format it NTFS (I am just booting the VM that will be my template to WinPE and using DiskPart to avoid ACLs.)
Third, make that cache disk large enough.  This was my most perplexing puzzle and I fiddled for a couple days with this off and on.

When I booted the VM and login I would get this strange error message:  “Windows created a temporary paging file on your computer”

This issue is specific to using the Cache on Device setting when your vDisk is set to shared mode.  The root is that the default page file location cannot be written to as it is the shared vDisk image.  The ancillary detail is that the page file gets moved to the device cache disk which is some drive other than %system%.

So, I had my local cache, all formatted, as small as it thought it needed to be.  I booted my vDisk and the error.  I set the vDisk to private mode and fiddled around with paging file settings.  I went back to my template VM, booted in shared mode, no paging file. 

I searched and found all kinds of related errors; moving it off C:, using a SCSI attached VHD instead of IDE (I happen to be using XenServer so this was not it), ACLs on files within the VHD, also obscure errors where the programs are expecting specific folders to exist on the cache disk.  All of these are totally valid errors and entirely possible in their scenario, just not mine.

Very frustrating.  It was when I booted with the VM template in private mode with the cache disk attached and I attempted to move the page file that I got a useful error:  the destination is not large enough.

Duh!  My device cache disk (the virtual disk of the VM) was not large enough for the caching of temporary files AND the page file of the VM operating system.  So silly.

I went back and recreated my template VM, with a larger cache disk – taking into account any dynamic memory settings.

Once I made this larger VHD (formatted with Diskpart) and booted using private image mode with cache on device hard drive all was good.

Thursday, December 15, 2011

Changing a VM vNIC MAC from dynamic to static

Through my VM creation exercise I am taking advantage of Hyper-V and its practice of assigning MAC address from a pool so that I don’t need to manage all that in my script.

Well, if you have not noticed, I have Provisioning Server in my environment.  And that means that I need to have static MAC addresses.  And I am using Export and Import to copy my MasterVM.

This means that I cannot set the MasterVM with a static MAC.  I would at least have to modify it to have a dynamic and then still set the individual VMs to static.

So, I just set the MasterVM to dynamic and do the rest when I assign the MAC to the VM by powering it on and off (I power the VM on to cause Hyper-V to assign the MAC to the vNIC – again, taking advantage of what the system does for me).

I have this array of new virtual machine names; $arrNewVms that I want to loop through.  I then power on each VM (I have to wait for the power on to complete), then I power off, then I modify the MAC.

Here is where things get tricky.  To discover the MAC of the VM I had previously used an association class this does not return a modifiable object.  So I have to actually get the VM vNIC, modify it, and then send it back to the ModifyVirtualSystemResources method.

foreach ($vmName in $arrNewVms) {
    $vm = Get-WmiObject Msvm_ComputerSystem -Filter "ElementName='$vmName'" -Namespace "root\virtualization" -ComputerName $hypervHost
    $Result = $vm.RequestStateChange(2) # start
    ProcessWMIJob $Result
    $vssd = $vm.getRelated("Msvm_VirtualSystemSettingData") | where {$_.SettingType -eq 3}
    $vmLegacyEthernet = $vssd.getRelated("Msvm_EmulatedEthernetPortSettingData") #This returns information not an actionable object
    foreach ($e in $vmLegacyEthernet) {
        $mac = $e.Address
        $macDash = $mac.Substring(0,2) + "-" + $mac.Substring(2,2) + "-" + $mac.Substring(4,2) + "-" + $mac.Substring(6,2) + "-" + $mac.Substring(8,2) + "-" + $mac.Substring(10,2)
        $arrUpdatedVms += ($vmName + "," + $macDash)
    }
    $vm.RequestStateChange(3) # stop
   
    #Set the MAC to static by first getting the vNIC object
    $vnic = Get-WmiObject Msvm_EmulatedEthernetPortSettingData -Filter "Address='$mac'" -Namespace "root\virtualization" -ComputerName $hypervHost
    $vnic.StaticMacAddress = 1
    $VMManagementService.ModifyVirtualSystemResources($vm.__PATH, $vnic.psbase.gettext(1))
}

Dealing with the Hyper-V default MAC pool

This is my little hack around dealing with the Hyper-V default MAC address pool range.

If you have been following, I have a bunch of virtual machines that I am creating.  And it is important that each has a unique MAC address.

Here is where software behavior gets into my way.

By default a Hyper-V Server MAC address range is only 255 MAC addresses ( 00 – FF ).  But a Hyper-V server can support 385 VMs, or VMs can have multiple virtual NICs.  So my pool can get depleted pretty quickly.

The other issue is that when assigning a MAC address to a VM the MACs are taken from the pool sequentially.  Not a big issue, and it actually adds some predictability.  However, once the end of the list is reached it starts over from the beginning.

The last issue, a Hyper-V Server only reserves the MAC of a VM when that VM is on that host.  Here is where my scenario makes all the problems.  I have a cluster of Hyper-V Servers, I am using one of them to create all my VM copies, then my VMs will migrate away.  Once the VM is no longer on the host where it was created its MAC is marked available in the pool (this is not tracked at the cluster level).

Combine this with the circular assignment action and this MAC can be assigned a second time, to a different VM.

SCVMM can solve this, as it tracks static MACs for all VMs that it manages.  But why use SCVMM just to prevent MAC duplication?

My hack?  Make the pool larger.  Only modify the octet that is necessary to achieve it.  And, randomize it to reduce the possibility of collision from a different host.

In a previous post I modified the MAC pool of a Hyper-V Server.  Here is where I embellish it to help with this duplicate MAC address issue.  Some interesting parts are converting from hexadecimal to decimal and then back again.

Additional background; leave the Hyper-V range "00155D", modify some part of the last 3 octets.  Increasing the next to last octet should give enough MAC addresses to prevent the range from rolling over and cover the number of VMs in a cluster (maximum of 1000).  I just want to make the pool bigger and the last octet of the MaximumMacAddress is always 255 (FF).

$VMManagementService = Get-WmiObject -Namespace root\virtualization -Class Msvm_VirtualSystemManagementService -ComputerName $hypervHost

$vsmssd = Get-WmiObject Msvm_VirtualSystemManagementServiceSettingData -Namespace "root\virtualization" -ComputerName $hypervHost

$intOctFive = [Convert]::ToInt32(($vsmssd.MaximumMacAddress.Substring(8,2)),16)

If ($intOctFive -lt 254) {
    $newIntOct = Get-Random -minimum ($intOctFive + 1) -maximum 255
    $newStrOct = ([Convert]::ToString($newIntOct, 16)).ToUpper()
    $vsmssd.MaximumMacAddress = ($vsmssd.MaximumMacAddress.Substring(0,8) + $newStrOct + $vsmssd.MaximumMacAddress.Substring(10,2))
}
Else {
    $intOctFour = [Convert]::ToInt32(($vsmssd.MaximumMacAddress.Substring(6,2)),16)
    $newIntOct = Get-Random -minimum ($intOctFour + 1) -maximum 255
    $newStrOct = ([Convert]::ToString($newIntOct, 16)).ToUpper()
    $vsmssd.MaximumMacAddress = ($vsmssd.MaximumMacAddress.Substring(0,6) + $newStrOct + $vsmssd.MaximumMacAddress.Substring(8,4))
}

$VMManagementService.ModifyServiceSettings($vsmssd.psbase.gettext(1))

Modifying Hyper-V Server settings with WMI

Here is a little thing that I recently put together to modify Server level settings of my Hyper-V Server.  Most of my focus so far has been on the VMs themselves, why not tweak the server?

Why might you need to change settings of the Hyper-V server itself?  Um, because you can, and therefore you should.  You might need to change the MAC address range, or the default path, or some other crazy thing.

What can you change?

Well, first, you have to get the Msvm_VirtualSystemManagementServiceSettingData WMI object.  This class is the settings of the hypervisor and things that the management OS must keep in check.

If you get the WMI object and then type it out in the PowerShell windows you will see something like this:


__GENUS                    : 2
__CLASS                    : Msvm_VirtualSystemManagementServiceSettingData
__SUPERCLASS               : CIM_SettingData
__DYNASTY                  : CIM_ManagedElement
__RELPATH                  : Msvm_VirtualSystemManagementServiceSettingData.InstanceID="Microsoft:BJEDAR2"
__PROPERTY_COUNT           : 13
__DERIVATION               : {CIM_SettingData, CIM_ManagedElement}
__SERVER                   : BJEDAR2
__NAMESPACE                : root\virtualization
__PATH                     : \\BJEDAR2\root\virtualization:Msvm_VirtualSystemManagementServiceSettingData.InstanceID="Microsoft:BJEDAR2"
BiosLockString             :
Caption                    : Hyper-V Virtual System Management Service
DefaultExternalDataRoot    : C:\Users\Public\VMs\
DefaultVirtualHardDiskPath : C:\Users\Public\VMs\
Description                : Settings for the Virtual System Management Service
ElementName                : Hyper-V Virtual System Management Service
InstanceID                 : Microsoft:BJEDAR2
MaximumMacAddress          : 00155D748DFF
MinimumMacAddress          : 00155D680A00
NumaSpanningEnabled        : True
PrimaryOwnerContact        :
PrimaryOwnerName           : Administrators
ScopeOfResidence           :

Now, how can I do something useful with this?  Read on.  I am going to modify the MAC address range of the host.

First, you have to get an instance of the Virtual Machine Management Service as this will give you the method you need to actually modify the WMI objects.  If you don’t do this, you only have a bunch of read only properties through CIM.

$VMManagementService = Get-WmiObject -Namespace root\virtualization -Class Msvm_VirtualSystemManagementService -ComputerName $hypervHost

Now, I want to get the object I typed out above:

$vsmssd = Get-WmiObject Msvm_VirtualSystemManagementServiceSettingData -Namespace "root\virtualization" -ComputerName $hypervHost

And see the MAC address range:

$vsmssd.MinimumMacAddress
$vsmssd.MaximumMacAddress

Then change the value:

$vsmssd.MaximumMacAddress = “00155D74FFFF”

Then put the change:

$VMManagementService.ModifyServiceSettings($vsmssd.psbase.gettext(1))

A return value of “0” is success, a return of “4096” is generally a long running job or something went bad.

Making VMs highly available with PowerShell

If you have been following you will know that I have a bunch of copies of a MasterVM.

These were created using Export / Import so they are all nice and tidy in their own folders on the same volume.  If your target volume was a CSV (Cluster Shared Volume) then you are all set for this one and are ready for a multi-node cluster.  If your destination was not a CSV, but possibly another local volume, you can still make your VMs Highly Available, but you won’t be able to migrate.  This is fine for testing, but not really of high value in production.

Here is the script:

if ($highlyAvailable -match "yes") {
    write-progress -Id 1 "Making each VM highly available" -Status "Executing"
    # Make a remote session here to run the below loop passing in $arrUpdatedVms
    $session = New-PSSession -computerName $hypervHost
    Invoke-Command -scriptblock { Import-Module FailoverClusters } -session $session
    Import-PSSession -module FailoverClusters -session $session
    foreach($e in $arrUpdatedVms){
        $vm = $e.Split(",")
        $deviceName = $vm[0]
        Add-ClusterVirtualMachineRole -VirtualMachine $deviceName
    }
    write-progress -Id 1 "Making each VM highly available" -Completed $TRUE
}

The most interesting thing that I do here is that I am using Import-PSSession to bring the Clustering cmdlets (installed on the remote Hyper-V server) into my local script execution session.  This allows me to use the cmdlets as if they were installed locally, but without installing them.  This is pretty useful.

The hitch is that your networking cannot incur any blips.  Your session must remain solid or the script simply begins to fail.

Monday, December 12, 2011

Joining PVS target devices to the domain

In my last post I created a bunch of PVS target devices (the VMs that I previously copied from my MasterVM).

I now want to have Provisioning Server create the Active Directory computer accounts for the VMs and do its magic of making each VM a unique machine without requiring sysprep – this is a nifty feature.

In my last post I imported the PVS cmdlets (MCLI) so I won’t repeat that step.

$arrUpdatedVms is simply my array of VMs, and all I need is the target device name (also the VM name) so I am doing a split.

if ($joinDomain -match "yes") {
    write-progress -Id 1 "Creating domain computer accounts" -Status "Executing"
    foreach($e in $arrUpdatedVms){
        $vm = $e.Split(",")
        $deviceName = $vm[0]
        #Create Active Directory Computer Accounts
        & Mcli-Run AddDeviceToDomain -p deviceName=$deviceName, OrganizationUnit=$ou
    }
    write-progress -Id 1 "Creating domain computer accounts" -Completed $TRUE
}

That is it.  Really fast.