James O'Neill's blog

Windows Platform, Virtualization and PowerShell with a little Photography for good measure.
Posts
  • James O'Neill's blog

    Hyper-v and Snapshots (Part 1)

    • 5 Comments

    We often talk about "rolling back" to a snapshot, but here, some of the snapshots we can apply aren't simply forward or backward, hence Hyper-V talks about applying snapshots.

    It also talks about Deleting snapshots which causes some confusion. Deleting a snapshot means foregoing the ability to return to that point - we can't apply a deleted snapshot, that's obvious enough. The saved memory state (if there is one) is deleted but what about the data in the AVHD file ? If a Snapshot has children we can either delete the whole subtree, or we can delete just the parent snapshot. Internally Hyper-v works out when it can merge and/or delete AVHD files - a process which can take some time (you can tell if this operation is pending because the edit button for the hard disk in the machine's settings is grey'd out). 

    Here's an example The machine is running quite happily and has never been snapped. Late on Monday make a snapshot. This does two thingsHyper-V-Snapshots

    1.If the Machine is running or in a saved state we make a copy of memory

    2. We stop writing changes to its VHD and start writing changes to a new AVHD file. (Lets call this AVHD-1)

    At any point when we want to revert, we go back to the original hard disk and the Monday memory state. Throughout Tuesday changes are written to the AVHD-1then on Tuesday night we do another Snapshot. The same thing happens

    1.If the Machine is running or in a saved state we make a copy of memory

    2. We stop writing changes to AVHD-1 file and start writing changes to a new AVHD file. (Lets call this one AVHD-2)

    Now we can revert to two points, Monday's memory state and the original VHD or Tuesdays memory state and the combination of the original VHD and AVHD-1

    Lets assume that on Wednesday something happens to cause us to go back to Monday's state. We can either (a) Keep AVHD-2 and save the memory state as it was on Wednesday or (b) Discard AVHD-2 and memory state. Either way the server now starts a new AVHD file - lets call this one AVHD3. Thursdays changes get written to this, and on Thursday night as before we do another snapshot and start AVHD4 for Friday's changes and keep the memory state as it was on Thursday.

    Now we can apply 3 states (or 4 if we kept Wednesday's).

    Now for the obligatory bit of powershell, because of course this is scriptable.

       Function New-VmSnapshot  
    {Param( $vm=$(Throw "You must specify a VM") )
    if ($vm -is [string]) {$vm=(Get-VM $vm) }

    $arguments=($vm,$Null,$null) $result=$VSMgtSvc.psbase.invokeMethod("CreateVirtualSystemSnapshot",$arguments) if ($result -eq 4096) { $arguments[2] }  else  {"Error, code:" + $result}
    }

    [Update. A bit of PowerShell 2.0 crept into the above. In 1.0 you can't call the .InvokeMethod  method of a WMI object directly, you have to call it via .psbase]

    In an earlier post I described Get-VM and explained that I set up a variable

       $vsMgtSvc = Get-wmiObject -nameSpace root\virtualization -class Msvm_virtualSystemManagementService

    All the work here is done by the CreateVirtualSystemSnapshot method, and we pass it the Machine and 2 nulls. Normally it will return 4096 - the code for "started processing in the background" and the second Null magically contains the job ID so the function returns that. So you can invoke the function as $JobId=(New-VmSnapshot  $Tenby) and make tracking the job afterwards that bit easier. There are two more methods which we can call, RemoveVirtualSystemSnapshot, and ApplyVirtualSystemSnapshot oddly the latter only requires the machine and the snapshot reference, even though the snapshot says which machine it came from but trying to apply a snapshot to a new, clean VM fails. Maybe it's something that's under consideration for a future version. I'll describe these two in my next post.

     

    [Update somehow the order of the paragraphs got scrambled, they're back in the right order now]

  • James O'Neill's blog

    Hyper-v Snapshots part 2.

    • 4 Comments

    In my last post I explained how snapshots work and gave a little bit of PowerShell for creating a one . In the post before that I talked about creating a generic  choose-tree function. What I wanted was to be able to call Choose-tree  List_Of_Items First_Item  PathPropertyName, ParentPropertyName, PropertytoDisplay and get a tree view I can choose from: like this

        $folders=dir -recurse | where-object {$_ -is [system.io.directoryInfo]}
        choose-tree $folders (get-Item (get-location) ) "PSPath" "PsParentPath" "Name"
    
    

    0    +windowsPowershell
    1    | |--Nivot
    2    | |--Pics Which one ?: 2

    The key thing in this is that PowerShell lets us use variables/parameters to hold field names. The logic is pretty simple. Take an array of items, and tell the function which one to start at, Output that item, if it has any Children call the function recursively for each of them. Checking for children is where this ability to pass property names is important, because I can use  where-object {$_.$parent -eq $startat.$path.ToString()} to say "where the field I said holds the parent, matches the field I said holds the path" I put a "ToString" on the end because I found it doesn't like being passed "path.path" and that's needed for some WMI items; toString() returns the path in this case but it's safe for strings too. When the function calls itself it specifies how many levels deep in the tree the current item is - each level of recursion adds 1 to $indent in the function.

    I wrote an "Out tree" before doing "choose-tree", most of the code below is associated with making choices When processing the topmost item it sets up a counter to allow the user to choose the items, and because the output order might not be the same as the input order it also sets up an array to hold ordered items. Once all the child items have been processed it prompts the user for a selection  and returns the item at that position in the array. 

    The only thing that I've done here that is out of the ordinary for me is I don't like using the -f operator on strings because it makes for unreadable code. "{0, -4}" -f $counter. Says "Put argument at position 0 into this string , right justified to 4 characters", which is just what I need, and I've put in the rest of the output line into the same construction.

       Function Choose-Tree
       {Param ($items, $startAt, $path=("Path"), $parent=("Parent"), 
        $label=("Label"), $indent=0, [Switch]$multiple)
        if ($Indent -eq 0)  {$Global:treeCounter = -1 ;  $Global:treeList=@() } 
    $Global:treeCounter++
    $Global:treeList=$global:treeList + @($startAt)
    $children = $items | where-object {$_.$parent -eq $startat.$path.ToString()} if   ($children -ne $null)
    { $leader = "| " * ($indent)
    "{0,-4} {1}+{2} "-f  $Global:treeCounter, $leader , $startAt.$label | Out-Host
    $children | sort-object $label |
    ForEach-Object {Choose-Tree -Items $items -StartAt $_ -Path $path `
                          -parent $parent -label $label -indent ($indent+1)}
         } else { $leader = "| " * ($indent-1)        
    "{0,-4} {1}|--{2} "-f  $global:Treecounter, $leader , $startAt.$Label  | out-Host } if ($Indent -eq 0) {if ($multiple) { $Global:treeList[ [int[]](Read-Host "Which one(s) ?").Split(",")] }
    else           {($Global:treeList[ (Read-Host "Which one ?")]) } 
                          } }

    And so to snapshots. 

    The parent partition and every child MsVM_ComputerSystem WMI object which represents it. The "Name" field in this object is actually a GUID. There is a second object MsVM_VirtualSystemSettingData: each VM, and each of its snapshots has one of these objects. The Settings data object for VM itself has it's GUID in both the InstanceID and systemName fields, but the Snapshots have their own instanceID with the VMs GUID in the system name field. As with most of my functions I things up so I can pass the MsVM_ComputerSystem object or pass a string and use Get-VM  to convert it. Then I it's one Get-WMObject operation to get the Snapshots.

       Function Get-VMSnapshot
       {Param( $VM=$(Throw "You must specify a VM") )
    
    if ($VM -is [String]) {$VM=(Get-VM -machineName $VM) }
    Get-WmiObject -NameSpace root\virtualization -Query "Select * From MsVM_VirtualSystemSettingData Where systemName='$($VM.name)' and instanceID <> 'Microsoft:$($VM.name)' " }

    Time to combine choose-tree and get-snapshot to choose my snapshots from a tree. As I said above , the Name field is actually a GUID, and the display name for the Snapshot is in the elementName . So I display the tree of choices,starting with the "Root" snapshot. [Note I'm aware that I don't cope with the situation where you delete a root snapshot with two children and get two roots]

       Function Choose-VMSnapshot
       {Param ($VM=$(Throw "You must specify a VM"))
        $snapshots=(Get-VMSnapshot $VM )
        Choose-Tree -items $snapshots -startAt ($snapshots | where{$_.parent -eq $null}) `
            -path "Path" -Parent "Parent" -label "elementname" }

    Now I can choose my snapshots, it's easy to tell a function like Remove-snapshot or apply-snapshot what I want. Here's Remove-Snapshot , which I can call with something like
    Remove-snapshot -snapshot (choose-Snapshot Core). Pretty simple stuff, I use the variable pointing to to the virtual System Management Service, as I did when creating a new snapshot this time I just need to invoke the RemoveVirtualSystemSnapshot method. As with the new snapshot it should return 4096 for "started processing in the background" , and I return the Job ID.

       Function Remove-VMSnapshot 
       {Param( $snapshot=$(Throw "You must specify a snapshot") ) 
        $arguments=($snapshot,$Null) 
        $result=$VSMgtSvc.psbase.InvokeMethod("RemoveVirtualSystemSnapshot",$arguments) 
        if ($result -eq 4096){ $arguments[1] } 
        else                  {"Error, code:" + $result} 
       }

    Finally I might want to apply a snapshot , and this needs us to specify the VM and snapshot. I've written this so that if the Snapshot is omitted the user is prompted to select it. It's the same process again, except this time we use the ApplyVirtualSystemSnapshot Method

       Function Apply-VMSnapshot
       {Param( $VM=$(Throw "You must specify a VM"), $SnapShot=(choose-VMsnapshot $VM))
        if ($VM -is [String]) {$VM=(Get-VM -machineName $VM) }
        $arguments=@($VM,$snapshot,$null)
        $result=$VSMgtSvc.psbase.InvokeMethod("ApplyVirtualSystemSnapshot", $arguments)   
    if ($result -eq 0) {"Success"} elseif ($result -eq 4096) {"Job Started" | out-host $arguments[2]} else {"failed"} }

    [Update. There were a couple of bits of PowerShell 2.0 in the above. In 1.0 you can't call the .InvokeMethod  method of a WMI object directly, you have to call it via .psbase]

    If you're wondering what to with the Virtual Hard disks I showed I'll get round to that soon.

  • James O'Neill's blog

    Moving VMs to Hyper-v...

    • 1 Comments

    There are 3 things I get asked regularly about Hyper-V. The first is "When can I get it ?". I've covered this before, the product group have committed to ship by August 2nd, and I've thought for a while that they're looking good to beat that. At MMS Bob Muglia said how pleased we were with performance and we're running the main Technet and MSDN sites on Hyper-V already. Question 2 is "Can you compare X with feature Y in VMware ?". And the third is "Will I be able to move my VMs from X to the release version of Hyper-V ?". Where X might be Virtual PC, Virtual Server, Pre-release Hyper-V, or VMWare. So let's go over the basic rules.

    1. In many cases Virtualization installs its own drivers and management components into the child OS. Sometimes it is possible to remove these under another Virtualization environment, sometimes it isn't (for example if you used the extensions from Virtual Server before R2 SP1 they won't uninstall under Hyper-V). So if you're changing products it's usually easiest to remove these first. As you move between builds of the same product, it's a good idea (and sometimes a requirement) to install the latest ones in the child VMs.  [With Linux VMs you should check if a update needs new integration components and since these are provided separately make sure they're available].  We're moving to one version of the Integration components disk for all supported Windows OS's, which streamlines the process. . Server 2008 core can't auto-run disks and depending on the OS auto run may be disabled, so simply selecting ‘Insert Integration Services Setup Disk’  may not get the job done. Some of the components look like new hardware, so make sure you cancel the Found New Hardware wizard before installing them
    2. We call OSes without Integration Components "unsupported".* This word tends to panic people. It doesn't mean you become an Outlaw in the eyes of PSS. Supported means if a flaw is exposed, it will get fixed. We have "Quick Fix Engineering" to provide patches to customers in this situation.  A VM running should look like a standard PC, so it should run OS/2, NT 3.x and 4, Novell NetWare 4 and 5, DOS 5 and 6. Some people might say in that case the Virtualization system "supports" those OSes , but if it exposes a latent flaw in an OS which is out of support, it won't get fixed; doesn't matter whose OS it was or whose Virtualization. If an out of support OS exposes a flaw in Hyper-V that doesn't show up any where else, it is only professional pride of the product group that drives a fix.
    3. A VHD is a VHD. You can take a VHD from any product which supports the format and mount it in Hyper-V. In itself that doesn't guarantee that the OS will boot and run properly, but you can mount the disk (as I showed here) and fix it off line. You wouldn't do that without making a copy of the VHD first would you ? Good.
    4. VMWare doesn't use VHD files. We've published our format and they've published theirs so there are conversion tools - we usually call this Virtual to Virtual migration, and we do it in SCVMM.
    5. Hyper-V's SCSI controller is what we call a synthetic one (VM-bus devices are "synthetic", non-VM bus ones are emulated). Synthetic devices are not available until the OS has booted, so Hyper-V must boot from IDE. (In the same way it can PXE boot from the Legacy NIC, but not the VM-bus one). When the OS is booted it also loads components which speed up IDE access substantially so there's no great speed advantage to using SCSI (there was in Virtual Server). If you were booting from SCSI before, using the same drive attached to an IDE controller is not normally a problem.
    6. Generally moving a VHD between Virtualization systems has the same effect as moving a hard disk between physical computers. You see a different BIOS, CPU, Mainboard, Network card etc. This may trigger re-activation. You can use the same product key - if activation tells you the key has been used, go through the telephone service. I've done this, so I know it is not onerous, although doing thousands of servers might be.
    7. Don't expect to move saved state files between builds. I know from personal experience you can't move saved states between Virtual Server 2005-R2 release and Service pack one builds, don't expect to do it between different builds of Hyper-V either, and if you had any thoughts of saving the state of a virtual server VM and bringing it back as a hyper-V one, I commend your optimism.  Online snapshots contain virtual machine saved-states: you can apply snapshots and shut the machine down and snap shot again to give an offline snapshot
    8. There is a KB article (949222) which explains updating from Beta to release candidate requires VM configurations to be recreated. The Beta-RC change was big enough to cause this but it shouldn't be repeated.

    Those are all the bits I can think of, but if I've missed something I'll cover it in a later post

    [Update  Added this  Footnote to point 2 * I'm told that in the future it may be possible to install ICs in a OS which is not supported - as I've described "supported" above, or an OS which lacks ICs may be supported.  ]

  • James O'Neill's blog

    The Hyper-V API - Disks

    • 1 Comments

    In an earlier post in this series (several posts ago now) I showed how the Msvm_virtualSystemManagementService WMI object can be used to configure resources in Hyper-V, and I started with the easy step of setting memory and CPUs which exist on a freshly created virtual machine. What about Network Cards, SCSI controllers, Disk drives and so on ? The 4 steps are the same as I outlined before i.e.

    1. Get a ResourceAllocationSettingData (RASD) object
    2. Modify one or more of its properties.
    3. Covert it to XML formatted Text,
    4. Pass the XMl as one of an array of arguments to one of the Methods of the Msvm_virtualSystemManagementService.

    Where before I was using the ModifyVirtualSystemResources method, new resources need the AddVirtualSystemResources. Where memory and CPU had special ResourceAllocationSettingData (RASD) objects these ones are more generic and I have a "Get-vmRASD" function. This takes type and subtype arguments, here's a quick summary of them

    Resource Type Resource Sub Type
    6 Microsoft Synthetic SCSI Controller
    10 Microsoft Emulated Ethernet Port
    10 Microsoft Synthetic Ethernet Port
    16 Microsoft Synthetic DVD Drive
    21 Microsoft Virtual CD/DVD Disk
    21 Microsoft Virtual Floppy Disk
    21 Microsoft Virtual Hard Disk
    22 Microsoft Synthetic Disk Drive

    So lets see how  we use them in practice. As before I get an object for theVirtual System management service

        $VSMgtSvc=Get-WmiObject -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService"

    If I want to create a SCSI controller, the 4 steps above translate to:

        $SCSIRASD=Get-VMRASD -ResType 6 -ResSubType 'Microsoft Synthetic SCSI Controller' 
        $SCSIRASD.elementName="VMBus SCSI Controller" 
        $arguments = @($VM.__Path, @( $SCSIRASD.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null ) 
        $VSMgtSvc.PSbase.InvokeMethod("AddVirtualSystemResources", $arguments)   

    If I want to create a disk drive I change the type of Resource Allocation settings data, I either use

        $diskRASD=Get-VMRASD -ResType 22 -ResSubType 'Microsoft Synthetic Disk Drive'
    if I want a hard drive or if I want a DVD drive it's:
        $diskRASD=Get-VMRASD 16 'Microsoft Synthetic DVD Drive'

    The remaining 3 steps are the same, this time I have to set two properties: a parent - the disk controller, and the address on the controller (passed as a parameter named $LUN)

        $diskRASD.parent=(Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE).__Path 
        $diskRASD.address=$Lun 
        $arguments = @($VM.__Path, @( $diskRASD.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null ) 
        $VSMgtSvc.PSbase.InvokeMethod("AddVirtualSystemResources", $arguments)

    Then to mount a disk into the drive it's either

        $diskRASD=Get-VMRASD -resType 21 -resSubType 'Microsoft Virtual CD/DVD Disk' -server $vm.__Server 

    if I want a Hard disk or if I want a DVD disk it's

       $diskRASD=Get-VMRASD -resType 21 -resSubType 'Microsoft Virtual Hard Disk'   -server $vm.__Server 
    And then as before the disk needs a parent - the drive it is mounted in, which makes more sense for a DVD than a Hard disk; and it needs a connection to the Virtual hard disk, ISO or Physical disk on the host and then we continue as before
    $diskRASD.parent=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE)  -Lun $lun ).__Path } 
    $diskRASD.Connection=$VHDPath
    $arguments = @($VM.__Path, @( $diskRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
    $VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments)

    OK: you may have noticed that I'm calling functions Get-VMRASD , Get-VMDiskController and Get-VMDrive

    Get-VMDiskController returns the RASD object(s) for either SCSI or IDE disk controller(s) or for both. The only thing which is a bit awkward here is that SCSI controllers don't have an obvious ID, we just get a single RASD object if there is one controller or an array if there is more than one, so I get the one I want with | Select first | select last. I showed before how I can set-up filters to take input from the pipe, and the real version of this code does exactly that ... why will become clear in just a moment

        Filter Get-VMDiskController 
        {Param ($VM , $ControllerID,  [Switch]$SCSI, [Switch]$IDE )
       if ($scsi) { $controllers=Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData
                                                 Where instanceId Like 'Microsoft:$($vm.name)%'
    and resourceSubtype = 'Microsoft Synthetic SCSI Controller' " `
                                             -NameSpace "root\virtualization" 
              if ($controllerID -eq $null) {$controllers}
                         else  {$controllers | select -first ($controllerID + 1) | select -last 1} }
    if ($IDE)  { Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData Where instanceId Like 'Microsoft:$($vm.name)%\\$ControllerID%' and resourceSubtype = 'Microsoft Emulated IDE Controller' " -NameSpace "root\virtualization"}
    }

    Then I wrote Get-VM Drive which takes a controller as a parameter (and again I can pipe one or controller(s) in to that - though I've omitted  the code out for the sake of space. )

        Filter Get-VMDrive 
        {Param ($Controller, $LUN ) 
         $CtrlPath=$Controller.__Path.replace("\","\\")  
    Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData
                    Where PARENT='$ctrlPath' and Address Like '$Lun%' " -NameSpace "root\virtualization" }
    }
    And finally I can pass the drive to Get-Disk, which looks like this
        Filter Get-VMDisk
        {Param ($Drive)
         $DrivePath=$Drive.__Path.replace("\","\\")
         Get-WmiObject -computerName $drive.__server -Query "Select * From MsVM_ResourceAllocationSettingData 
                                                              Where PARENT='$DrivePath' " -NameSpace "root\virtualization" }

    So I can find the VHDs mounted used by one or more VMs with

        Choose-VM -multiple  | GetVmDiskController -IDE -SCSI | GetVMDrive | get-VMDisk 

    Or I can get more detailed information Like This

    Function Get-VMDiskList 
    {Param ($vm)
    foreach ($v in $vm) {
             foreach ($dc in (get-vmdiskcontroller -vm $v -ide -scsi)) {
                     foreach ($drive in (get-vmdrive -controller $dc)) {
                             get-vmdisk -drive $drive | select-object -property `
                                                           @{name="VMName"; expression={$v.elementName}},
                                                           @{name="VMGUID"; expression={$v.Name}},
                                                           @{name="ControllerName"; expression={$dc.elementName}},
                                                           @{name="ControllerInstanceID"; expression={$dc.InstanceId}},
                                                           @{name="ControllerID"; expression={$dc.instanceID.split("\")[-1]}},
                                                           @{name="DriveName"; expression={$drive.caption}} ,
                                                           @{name="DriveInstanceID"; expression={$drive.instanceID}},
                                                           @{name="DriveLUN"; expression={$drive.address}},
                                                           @{name="DiskPath"; expression={$_.Connection}},
                                                           @{name="DiskName"; expression={$_.ElementName}},
                                                           @{name="DiskInstanceID"; expression={$_.InstanceID}} }}}
    }

    The Get-RASD code isn't nice. It's two WMI queries to get the name of the an object we need to create

        Function Get-VMRASD 
         {Param ($ResType, $ResSubType) 
          $allocCapsPath= ((Get-WmiObject -NameSpace "root\virtualization" -Query "Select * From MsVM_AllocationCapabilities
                                              Where ResourceType = $ResType AND ResourceSubType = '$ResSubType'").__Path).replace('\','\\') New-Object System.Management.Managementobject((Get-WmiObject -ComputerName $server -NameSpace "root\virtualization"
              -Query "Select * From MsVM_SettingsDefineCapabilities Where  valuerange=0 and Groupcomponent = '$AllocCapsPath'").partcomponent) }

    Whilst it may not be nice, knowing the Values to pass it from the table above, the properties to set, and how to pass it into AddVirtualSystemResources allows you to create what ever disk related bits you need. In a future post I'll move on to looking at how you similar things with NICs.  Then I'll have one more on creating VMs and setting the motherboard options and then I'll be posting the whole code for download - I've got some internal Microsoft people trying it out at the moment.

    [Update the Original post had numerous proof reading errors - I hope there aren't any left]

  • James O'Neill's blog

    Something for the photographers: Silverlight Deep-zoom formerly "Sea-Dragon"

    • 4 Comments

    With all the other stuff I've been doing lately I'm champing at the bit to get out and take some more photos. Still here I am slaving over a hot laptop.

    Search suggests haven't mentioned Sea-Dragon before - but I've talked about Photosynth, which is one of the technologies that use it. We've been saying since at least last October that Sea-Dragon is going into Silverlight.  Which is exciting stuff, it's also getting a new name "Deep zoom".  Eileen picked up some stuff on this - we have Deep Zoom Composer available for Download. I've had a brief play with it but it's output is really intended to go into something else*; more interesting - to me at least is Photo Zoom. Imagine a "contact sheet" view of your photos with pretty much unlimited zoom in and out. That, in a nutshell is Photozoom. As yet it is not a fully featured photo gallery - it doesn't have the tags and other text that I'd want but as a proof of concept of where this is heading it's exciting stuff.

    We always have bit "in the works" that I can't really talk about, but an interesting bit of software came my way this week, and the folks behind it have said I can talk about what it produces. What they've given some of us is a test harness for both ideas, and code to implement them. In what form these see the light of day outside the company and when - if at all - remains to be seen.

    So here is one of my favourite pictures, party because I sold it a print of it to someone whose London Office is in there

    st pauls Click for the Deep zoom version

    The original was 38 Mega-Pixel image and the print was 150x21 CM in size (60" x 8" ) an 8:1 aspect ratio is lousy to view on screen and even worse if you want to store it online.
    But now I have a version set up for deep zoom. - I can find the window of my patron's office, but I can move around it interactively much like looking at a big print hanging on the wall.

    Before you click through (a) It needs Silverlight 2 Beta installed. If you get an error about Invalid XML character after installing Silverlight you need to close and restart the browser. (b) The experience is OK with the glidepoint device on my laptop, but it's much better with a wheel mouse.

    *Update. My colleague Marc Holmes gave me some info, which is now on his blog, to show me how to upload my frist DeepZoomComposer project to silverlight. You can see it here nothing too fancy, just a few pictures that I happen to like. in one project.

  • James O'Neill's blog

    A little more on PowerShell

    • 6 Comments

    I've been showing PowerShell on the roadshow, and Steve warning me about it becoming the "Look-how-clever-I-am-with-PowerShell show". Actually, I quite like the idea of a "Look-how-clever-PowerShell-makes-you show". Working on some of bits and pieces of PowerShell for managing hyper I've found a couple of new corners which I thought I'd share.

    I wanted to show the tree of snapshots taken of a Hyper-v Virtual Machine ... and let the user choose one of them.  I ended up with a semi-generic "Choose-Tree" function, and that sent me back to the choose functions I've written about before. I wanted to have something totally generic like this

       choose-list (Get-WmiObject win32_diskdrive) @("DeviceID", "Model")
    
    

    ID DeviceID           Model
    -- --------           -----
    0 \\.\PHYSICALDRIVE0 Hitachi HTS721010G9SA00 ATA Device
    1 \\.\PHYSICALDRIVE1 Generic USB Card Reader USB Device
    2 \\.\PHYSICALDRIVE2 Multi Flash Reader USB Device

    Which one ?:

    The idea is simple enough, pass the function an array of data and other array of the field names I want displayed, put a counter alongside them and let the user enter the index into the array for the item they want.

    This led me to using variables and parameters in places I haven't done before One was using a variable to hold a field name. Like this

       $foo="TotalPhysicalMemory"
       (Get-WmiObject win32_computersystem).$foo

    Very useful if I need to pass a parameter to a function to tell it which property to use from the objects it was passed. Building a tree for example, which field is displayed, which gives a node's parent, and so on. Then I started looking at using an array of strings to hold the field names, like this

       $foo=@("TotalPhysicalMemory","Model")
    Get-WmiObject win32_computersystem | format-table -property $foo

    That works very nicely too. Because Powershell lets us mix types of item in an array we can put in hash tables which store custom fields. So a design formed... Pass data and Fields as parameters, add the counter custom-field that I was already using in my choose functions to the Fields array (put it an array and join it with the array of fields passed to the function), and use that in a format-table command. Since some of Choose functions had multiple selections and some single I had a "Multiple" parameter and that changes the prompt / selection at the end. So my generic choose-from a-list function ends up as half a dozen lines.

       function choose-List
       {Param ($Data , $fieldList , [Switch]$Multiple)
        $global:Counter=-1
        $fieldlist=@(@{Label="ID"; expression={ ($global:Counter++) }}) + $fieldList
        $data | format-table -autosize -property $fieldList | out-host
        if ($Multiple) { $Data[ [int[]](Read-Host "Which one(s) ?").Split(",")] }
        else           { $Data[        (Read-Host "Which one ?")           ] }
       }

    Each "choose" function - choose VM, Choose network etc then becomes one line.

    Technorati Tags: ,
  • James O'Neill's blog

    Ways to tidy up my PowerShell - including making a hash of stuff

    • 3 Comments

    Please excuse the bad pun... When I first wrote the function I posted to display the state of virtual machines, I used a construction which has been familiar to programmers since time immemorial.

      If X=1 output this

    If X=2 output that

    etc

    Most modern programming languages, including PowerShell, have some kind of switch construction which is a little tidier but they're still bulky...
    I had put constants for each of the states in the .PS1 file which holds all my PowerShell VM functions. But this was more as a way of having a note of them than something I was going to use in my code. I could have written the Start-vm function (in the same post) like this 

       $VM.RequestStateChange($Running)

    and re-coded my display function as

       switch ($_.EnabledState) { $Running {"Running"}
    $Stopped {"Stopped"}
                             $Paused {"Paused"}
    etc

    but it still needs a line for each state. For completely separate reasons I was looking at hash tables. It takes one line to create a hash-table of return codes:

        $VMState=@{"Running"=2 ; "Stopped"=3 ; "Paused"=32768 ; "Suspended"=32769 ; 
    "Starting"=32770 ; "Snapshotting"=32771 ; "Saving"=32773  ; "Stopping"=32774 }

    So I changed the way I start and stop machines: one function does the work: expanding arrays, converting strings to computerSystem objects and actually changing the state: like this

       Function Set-VMState 
       {Param ($VM , $state)
        if ($VM -is [Array]) {$VM | ForEach-Object {Set-VMState -VM $_ -State $state} }
        if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM) }
        if ($VM -is [System.Management.ManagementObject]) {$VM.RequestStateChange($State) } 
    $VM = $null }

    Using the hash Table and then I have Start, Stop and Pause functions like this:

       Filter Start-VM
       {Param ($VM)
    $if ($vm -eq $null) ($vm=$_} Set-VMState -VM $VM -State $vmStates.running
    $VM = $Null }

    I also made a change to accept input from the pipe e.g. Get-VM "James%" | start-VM , there are two changes (a) use a FILTER instead of a FUNCTION and (b) pick up the piped input in $_ . So I've got quite a few functions where I should  change this.
    [Update, I'm not sure if this is the approved way of Piping, but I quickly learned that I should add the $VM=$Null at the end, other wise when 5 items  are piped in function is run 5 times, using the first one each time.]

    HashTables are a one-way lookup: $VMstates.running returns the value with a key of "Running" - 2 in the Start-VM filter.  If I have "2" and want to get back to "Running" there isn't a built in way(that I know of). However PowerShell has a GetEnumerator which dumps out the whole hash table as Key/value pairs, which that makes it easy to get the name we want.

       function Convert-VMStateID
        {Param ($ID)   
    ($vmState.GetEnumerator() | where {$_.value -eq $ID}).name }

    and using the choose-list function I showed before before , choose-VM becomes a one liner

    Function Choose-VM
    
    {choose-list -data (Get-VM) -fieldList @(@{Label="VM Name"; Expression={$_.ElementName}},
    @{Label="State"; Expression={Convert-VMStateID -ID $_.EnabledState}}) }

    (In principle it is a one liner ... in  practice I'm going to have a -multi switch to allow single or multiple selections.

    One other thing I've done in this tidying up exercise is to make sure I name my parameters in scripts. This means I really should go back to my Choose-list function and rename the "Field list" parameter to "Property" to match Powershell's built-in cmdlets (just as I have been trying to use existing Verbs and write my nouns in the singular !).  Identifying parameters by position doesn't make for readable code:  the following two lines are equivalent, but which would you rather see in a script (not the one you'd rather type at the command line !)

    Set-VMState -VM $VM -State $vmStates.running  
    Set-VMState  $VM  $vmStates.running 

     

  • James O'Neill's blog

    The Hyper-v API Network interfaces

    • 0 Comments

    If you've read my post on adding disks to a Virtual machine, the techniques here should already feel familiar. We create a NIC , and we create a switch port. And then we tell the NIC it is connected to the switch port. Hyper-V creates VM switches which are either bound to a NIC, internal (visible to the Parent partition) or private (visible only to the child VMs). So one of the first things to do when setting up a NIC is to choose the switch, and the first function I'm going to create is Choose-VMSwitch using  the choose-list function I've already shown:  getting the Switches to pass to choose-List is easy enough, just query WMI for MSVM_VirtualSwitch Objects.

        Function Choose-VMSwitch 
        {choose-list  (Get-WmiObject -NameSpace  "root\virtualization" -Class "MsVM_VirtualSwitch") ` 
         @(@{Label="Switch Name"; Expression={$_.ElementName}} ) 
        } 

    So now I can have a command Add-VMNic $VM  (Choose-VmSwitch) . Since Hyper-V supports Legacy and VMBus NICs, I have given the option for a -Legacy switch, and to support giving the NIC a fixed MAC address I've added a -MAC switch too.  The PowerShell Filter is much the same as I've shown previously 

    1. If not passed a VM parameter pick up what is in the pipe
    2. If presented  a string as a VM parameter replace it with a VM WMI Object (or array of VMs)
    3. If presented with an array of Strings or VMs call the function recursively passing it each member of the array [go back to step 2]. N.b. Multiple NICS can't  have the same MAC address so ignore the -MAC parameter.
    4. Assuming we've got a WMI object ... get the appropriate Resource Allocation Settings Data object
    5. Set the properties of the RASD; including the MAC address if one was provided. If no Switch parameter was passed set the connection property to an empty string, if one was passed Call New-VmSwitchPort and set the connection property to point to that. The VMBus NIC needs a GUID as an Identifier and the Emulated one does not.
    6. Set up an arguments array, and call the "AddVirtualSystemResources" method of the VirtualSystemManagementService WMI object (which I get with Get-WmiObject -NameSpace  "root\virtualization" -Class "MsVM_virtualSystemManagementService").

    So here's the code in full.

       Filter Add-VMNIC 
       {Param ($VM , $Virtualswitch, $mac, [switch]$legacy ) 
        if ($VM -eq $null) {$VM=$_} 
        if ($VM -is [Array]) {if ($legacy) {$VM | ForEach-Object {add-VmNic -VM $_ -Virtualswitch $Virtualswitch -legacy} }                               else  {$VM | ForEach-Object {add-VmNic -VM $_ -Virtualswitch $Virtualswitch} } } 
        if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM ) } 
        if ($VM -is [System.Management.ManagementObject]) { 
            if ($Legacy) {$NicRASD = Get-VMRASD -resType 10 -resSubType 'Microsoft Emulated Ethernet Port' 
    
                        $NicRASD.ElementName= "Legacy Network Adapter"} 
         else         {$NicRASD = Get-VMRASD -resType 10 -resSubType 'Microsoft Synthetic Ethernet Port'
                        $NicRASD.VirtualSystemIdentifiers=@("{"+[System.GUID]::NewGUID().ToString()+"}")
                        $NicRASD.ElementName= "VMBus Network Adapter"}    
          if ($virtualSwitch -ne $null) {$Newport = new-VmSwitchport $virtualSwitch                                      if ($Newport -eq $null) {$Newport= ""}
                                         $NicRASD.Connection= $newPort}
          if ($mac -ne $null) {$nicRasD.address = $mac                $nicRasD.StaticMacAddress = $true }       $arguments = @($VM.__Path, @( $nicRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
           $result = $VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments)   if ($result  -eq 0) {"Added NIC to '$($VM.elementname)'."} else {"Failed to add NIC to '$($VM.elementname)', return code: $Result" }}  $vm = $null }

    In this function I call "New-VmSwitchPort", which is a wrapper for a method provided by the Virtual Switch Management Service. Like the the image management service, and the Virtual System Management Service, this is just a WMI Object which we query for. The process goes

    1. If presented with a string as the VirtualSwitch Parameter, replace it with a VirtualSwitch WMI object
    2. Assuming we now have a WMI object, get the SwitchManagementService WMI object.
    3. Get a GUID to use as the port's name and friendly name , and pass it, the Switch object and 2 nulls in one array to the CreateSwitchPort method
    4. The Path to the new port is returned in one of these nulls, so the function picks this up and returns it.
       Function New-VMSwitchPort 
       {Param ($virtualSwitch , $Server=".") 
        if ($Virtualswitch -is [String]) {$Virtualswitch=(Get-WmiObject -computerName $server -NameSpace "root\virtualization" -Query "Select * From MsVM_VirtualSwitch Where elementname = '$Virtualswitch' ")} 
        if ($Virtualswitch -is [System.Management.ManagementObject])  {     $SwitchMgtSvc=(Get-WmiObject -computerName $Virtualswitch.__server -NameSpace  "root\virtualization" -Query "Select * From MsVM_VirtualSwitchManagementService")     [String]$GUID=[System.GUID]::NewGUID().ToString()      $arguments=@($Virtualswitch.__Path, $GUID, $GUID, $null, $null)      $result = $SwitchMgtSvc.psbase.invokeMethod("CreateSwitchPort",$arguments)     if ($result -eq 0) {"Created VirtualSwitchPort on '$($virtualSwitch.elementName)' " | out-host                          @($arguments[4]) }      else               {"Failed to create VirtualSwitchPort on '$($virtualSwitch.elementName)': return code: $Result" | out-host} } 
       } 

    There are some extra functions that I won't show here - I've got a "Remove-Port" function and a Set-VMNICPort function - which removes an existing port and adds a newly created one. I've got a Set-VMNICMacAddress function which changes the MAC address after the NIC is created, and Get-VMNIC and Get-VMNICSwitch which build up to give  a Get-VMNICList function along the same lines as the Get-VMDiskList I showed before

     

    Bonus link Over on the Virtualization Team blog, Taylor has posted the code to connect the Host machines Network card to a VM switch. I'm going to rework that code slightly for the library I'm building -  I'll have a "Choose-ExternalEthernetPort" and so on.

  • James O'Neill's blog

    More on the Hyper-V API

    • 2 Comments

    In which we see how to set the number of CPUs

    I started with getting MSVM Computer System objects - which I showed back in February. With these objects I can ask for the state of the VM to be changed to Running, Stopped or Saved.

    To do things in a proper Powershell Style I re-wrote and re-wrote my functions so I have GET-Vm (which returns one or more VM(s) by name), Choose-VM, which puts up a list and returns one or more VM(s). Plus Start-VM, Stop-VM and Suspend-VM. Over various iterations these have moved from demanding a single MSVM Computer System object, to accepting an or object or display name, then to accepting and of array either, to allowing input to be piped in. Since Stop-VM is a bit brutal, that same February Post showed using the ShutDown integration Component

    Next I moved onto the Msvm_ImageManagementService, and a few weeks back I looked at how Virtual Hard disks can be created , mounted and Compacted.

    From there it was on to the related idea of Snapshots which I covered here and here; Snapshots are handled through the Msvm_virtualSystemManagementService. This is actually a very important WMI class. I mentioned Taylor's post which shows how to manipulate the Exchange of Key/Value pairs (the Host's KVPs are managed through this object). But there are 6 other methods I want to introduce here they are Create-, Modify- & Destroy- VirtualSystem and Add, Modify and Remove Virtual System Resources.

    Creating and Modifying work in the same way. Identify the machine (unless it is being created) and pass a block of XML which describes how you want the machine, or the resource attached to it to be. If you're think ahead and saying "Can I dump that XML out to a file ?" you can: both the Virtual System Management Service WMI object and the MMC console provide interfaces to Export or Import the Machine.  There are quite a lot of things which we want to be able to manipulate.

    • Legacy Network Card
    • VM-Bus Network Card
    • VM-Bus SCSI Controller
    • IDE DVD Drive
    • Virtual DVD Disk (which is inserted into the drive)
    • IDE Hard drive
    • SCSI hard drive
    • Virtual Hard disk-image (inserted into the drive)
    • Memory size
    • CPU cores and reservation
    • The VM itself

    And for each of these we can get the XML by

    • Building it up from Scratch
    • Reading it from a file
    • Getting the existing value from WMI (for modification)
    • Getting a default from WMI (for creating)

    The first 2 are usually a pain, so typically the process goes:

    1. Get a ResourceAllocationSettingData (RASD) object
    2. Modify one or more of its properties.
    3. Covert it to XML formatted Text,
    4. Pass the XMl as one of an array of arguments to one of the Methods of the Msvm_virtualSystemManagementService.

    For example: here's how we set the number of CPUs - we get a variation on the generic RASD object, the Msvm_ProcessorSettingData object for the VM in question.

        Filter Set-VMCPUCount
    {Param ($VM , $CPUCount)
    $procsSettingData=Get-WmiObject -NameSpace  "root\virtualization" `
    -query "select * from MsVM_ProcessorSettingData 
                                    where instanceID like 'Microsoft:$($vm.name)%' "
    $procsSettingData.VirtualQuantity=$CPUCount
    $SettingXML=$procsSettingData.GetText([System.Management.TextFormat]::WmiDtd20) $arguments=@($VM.__Path, @($SettingXML) , $null) $Result=$VSMgtSvc.PSbase.InvokeMethod("ModifyVirtualSystemResources", $arguments) if ($Result  -eq 0) {"Success"} else {"Failure, return code: $Result "} }

    [Update. There were a couple of bits of PowerShell 2.0 in the above. In 1.0 you can't call the .InvokeMethod  method of a WMI object directly, you have to call it via .psbase and .PATH property doesn't exist, you have to get to the path with __Path, not .path.path]

    The process is almost identical for memory, except get the Msvm_MemorySettingData object and set 3 properties named, .Limit, .Reservation   and VirtualQuantity which are all set to the desired memory size in megabytes

    In the next few posts I'll look at using the RASD objects to add disks and Network cards, plus how we can create and configure the VM itself.

  • James O'Neill's blog

    Server 2008 Drives and GUIDs

    • 0 Comments

    One of the questions I've been asked a few times is what happens if I want to have lots of disk-volumes on my server - for example I want 50 VMs each with their own volume - I'm going to run out of drive letters. For some time we have been able to use Mount Points where for example a disk is mounted into c:\users - which is not the same as the rest of C:. Server 2008 also exposes disks by GUID and I wanted to show a couple of examples, so which I've done using my memory stick. Normally I'd have a drive letter for my memory stick but if I go into Disk Management I can remove its drive letter and/or mount it into a folder. The MountVol Utility returns the following information.

        Possible values for VolumeName along with current mount points are: 

            \\?\Volume{d1f72a03-d43a-11dc-8bf1-806e6f6e6963}\
                C:\

     

            \\?\Volume{d1f72a06-d43a-11dc-8bf1-806e6f6e6963}\
                D:\

     

            \\?\Volume{2d018576-d3f9-11dc-9f13-0019b97a962e}\
                C:\temp\Kingston\

    I can remove the mount point in MountVol with "mountvol c:\temp\kingston /d" (I can add it back in again with mountvol "c:\temp\kingston"  \\?\Volume{2d018576-d3f9-11dc-9f13-0019b97a962e}\.

    If I want to assign a letter to it, the syntax would be mountvol "E:\"  "\\?\Volume{2d018576-d3f9-11dc-9f13-0019b97a962e}\"   ).
    guid

    If I remove the all mount points and drive letters then I get this in MountVol

        \\?\Volume{2d018576-d3f9-11dc-9f13-0019b97a962e}\ 
    
            *** NO MOUNT POINTS ***

    Either way I can use the device ID in the path for a VHD file in hyper-V (as I understand it clustering is the same - although I haven't checked this)

    You can get the information from WMI as well. Here's a quick line of  powershell to get and format it.

        get-wmiobject -class "Win32_volume"  | format-table -autosize name, caption, deviceid 

        name              deviceid
        ----              --------
        C:\               \\?\Volume{d1f72a03-d43a-11dc-8bf1-806e6f6e6963}\
        D:\               \\?\Volume{d1f72a06-d43a-11dc-8bf1-806e6f6e6963}\
        C:\temp\Kingston\ \\?\Volume{2d018576-d3f9-11dc-9f13-0019b97a962e}\

    One of the things I want to is get the volumes which contain VHD files. Using the Get-VMDisk function I showed before, I can use a little more PowerShell (Get-VM, Get-VMDiskList and Get-VMSnapshot have all be explained in earlier posts).

      $diskpaths = (get-VMDiskList  (get-vm) | foreach {$_.diskpath}) + 
                   (Get-WmiObject -NameSpace "root\virtualization" -class Msvm_VirtualSystemGlobalSettingData |
    foreach-object {$_.SnapshotDataRoot ; $_.ExternalDataRoot } | select -unique ) +
    (get-vm | get-vmsnapshot| foreach {Get-WmiObject -NameSpace "root\virtualization" -Query "ASSOCIATORS OF {$_} " |
    where {$_.ResourceSubType -eq "Microsoft Virtual Hard Disk"} } | foreach {$_.connection})
      $vols=&{foreach ($volume in (gwmi Win32_volume)) {if ( (($diskpaths| where{$_ -match $volume.name.replace("\","\\")}) -ne $null) -or
                                                     (($diskpaths| where{$_ -match $volume.DeviceID.replace("\","\\")}) -ne $null) ) {$volume} } }

    Sooner or later someone is going to take me to task over calling that "Two lines" of powershell. The key piece is that once I know which volumes hold my Hyper-V files I can start thinking about backup...

  • James O'Neill's blog

    On Ninjas.

    • 1 Comments

    andrew2 Over the weekend one of the many Davids I count among my friends mailed me a curious job title from his organization. Yesterday, in one of those moments of serendipity Viral told me about one of the guys in Redmond who has the job title of "Zune Ninja" - he explains how the the title came about here. A while back I mentioned Tom Lehrer, and Plagiarize. Viral's good friend James Senior is the one whose name is cursed, when we found out he published first.

    So while we're on Ninjas, I should say that when I drew the team cartoons using Janina Köppel's Excellent SP-Studio I couldn't find a way to capture Andrew so I tried doing him as SQL Ninja. This seems like a good time for that cartoon to come to light.

  • James O'Neill's blog

    "Lady licensing" blog.

    • 3 Comments

    George has been telling everyone recently about Emma Healy's blog on Licensing. I don't often plug other blogs, but I think this one is worth your attention

    • Before I came to Microsoft I used to wonder "Why can't they make licensing simpler"... these days I have some idea why.
    • Technical people tend to look on licensing matters as "paperwork" with all the disdain that implies, and never take the time to find out about it.
    • Licensing people don't tend to be visible in public

    I wouldn't pretend that I would go and read about licensing every single day, but that's the joy of RSS, you can sign up and every so often something that you value comes by. This one on Virtualization for example.

    However is it just my puerile mind which makes the title that Emma has picked on Live Spaces - Lady Licensing - sound like something else.

  • James O'Neill's blog

    Off topic. The Cost of fuel, market forces and being green

    • 3 Comments

    Part of my salary package working at Microsoft UK is a company car, for which Microsoft buys the fuel. I can opt out of this scheme and take money instead (which is taxed like any other Salary payment) and the Tax office also works out the notional value of the car and fuel (both are based on the C02 emissions of the car)- the critical thing is that this doesn't change with the amount of fuel I use, or the price of fuel. In effect the cost of fuel to me is fixed however many miles I do; which gives me a financial incentive to use the car rather than greener forms of transport. It also takes away any financial incentive I have to work from home and when I do it is based on productivity and work/life balance (if anything is going to hinder my getting the job done, better that it's my son asking what I'm doing on the computer than the hubbub in our hotdesk pens)

    Last night on the way home I stood idly calculating the price of fuel per (imperial) Gallon, we've been buying fuel in litres in the Britain for 20 years now but we still think of fuel consumption in miles per gallon, like my grandmother converting prices into Shillings to the day she died (10 years after currency went decimal) we still go back to pricing in Gallons. The little display on the pump last night said 132.9 pence per litre; as the displays ticked round to  64 litres and £85, I tried to multiply 132.9 by 4.54 to get price per Gallon. "Call it 4/3 x 4.5 ... thats £6 a gallon !"... " now without the rounding is that just over or just under ?" Queuing to pay I got my phone out and used the calculator £6.03. For readers in the US, your gallons (and pints) are 20% smaller than ours,and with the pound at $1.97 that makes UK diesel abut $9.50 per US Gallon (Petrol/Gasoline is about 10% cheaper)

    For the at least the last 10 years, governments have been raising the cost of fuel above the rate of inflation to try to encourage us to use less of it. (I don't want to get into party politics here, I think the Conservatives started it and Labour thought it was a good idea and continued the policy). There are now differential rates of Vehicle Excise Duty on based on emissions. Those who think of their fuel in gallons remember when this flat rate tax was called the "Road-Fund licence" but for years governments have been using fuel and VED as a way of raising money to pay for anything but roads. I've heard politicians from all the main parties arguing for huge rates of VED for the most polluting cars. Since I have a "clean-diesel" which does about 50 Miles to the Gallon it doesn't affect me, and in any event as a company car driver I'm insulated from rates of VED, so I have no particular interest to turn me against such a plan. But  like calling for extra taxes on "The rich", it's easy politically: if you hit people at one extreme you don't hit the other 95% of people who might vote for you. It doesn't take a mathematical genius to see that someone whose car only does 20 Miles per gallon but only drives 40 Miles a week, uses less fuel than someone whose car does 50 to the Gallon but drives 400. Should they pay tax on fuel used or on their vehicle's potential to pollute ? Increasing the cost of owning an inefficient car might result in some of them being scrapped early - which takes money out of the economy, and results in more demand to manufacture new cars - a process which also uses a lot of energy.  Logic would say scrap VED entirely and raise the same amount of tax from extra fuel duty. Those who use less fuel than average would be better off, those who use more would be worse off. The whole government bureaucracy dealing with VED could be scrapped (saving more money) and the VED tax disk could be replaced with something issued the car's insurers to show it's paperwork was all in order.  The political problem with such a policy is that everybody sees their fuel price go up and has to pay more every week, reminding them the government has done something unpleasant.

    The key, of course, is to find ways to make fewer journeys. and to use more efficient forms of transport for the ones we do make. As someone wrote
    "Here in my Car,
    I feel Safest of all,
    I can lock all  my doors,
    it's the only way to live,
    In cars"

    Of course he it might not have scanned so well to say "I can pick my nose" , "I can shout at the radio" , "I can listen to music without headphones" , "I can set the temperature to what I want" etc. Some of us might be priced out of our cars and into using car-shares or public transport. But if we didn't all go to the office every day, the office could be smaller and the saved Journeys would save money, time and pollution. Think of that next time you curse the traffic or the cost of filling up.

  • James O'Neill's blog

    Ah, the joy of feedback

    • 1 Comments

    As I mentioned before we're getting updates on the feedback from the roadshow: George has a report for us the next day. On one of our past roadshows I had a serious sense of humour failure about the speed we got feedback - the system then cost a fortune and delivered information too late to act on it. I changed my presentation based on a reported comment which turned out to be unrepresentative and made it less effective.  So this change is one I'm particularly pleased with. Numbers give us something measurable, but the comments are things we can act on. But there's not much we can do when we get successive comments which say "Very well presented, nice to have two presenters covering the topics." and "Chit chat between presenters grated."

    So let me just explain why we've taken to having two people on stage. One of things that we know people hate is "Death by PowerPoint" (confusingly some people also want us to provide a slide deck which would stun an Ox). Of the 85 answers to "what was best about the day" we had in London about 1 in 6 picked out demos .

    1. Demoing the product. eye-test
    2. Live demonstrations.
    3. Windows Server 2008 Core demo, explanations regarding powershell.
    4. Demonstration.
    5. I much prefer the hand-on demos than watching power point slides.
    6. Live demos, not all power point.
    7. How to manage Windows Server 2008 presentation.
    8. Excellent demonstrations, little sales pitch (except for hyper-v!).
    9. The material presented, not death by Powerpoint but much more Demo focused. Kept it much more interesting!
    10. The venue was fantastic and demo of installing windows 2008.
    11. The openness of the talks The tech demos.
    12. Live demonstrations.
    13. I liked the fact that we were able to see live demos of the products’ in use and to be able to ask questions.
    14. Quality of speakers and good demos.

    Here's the thing though. Demos are HARD. PowerPoint is EASY. Now I say "easy", actually to speak about something technical, to do it lucidly (not reading) and be interesting, to do in front of audience of between 200 and 600 people without being overcome with nerves, and - given the range of the audience - make sure that at any given time 95% of the room can understand you but you're not patronizing more than 5% .... this is stretching the definition of easy. Building a demo which shows the point, which you can actually do on stage, which is repeatable isn't too hard. If that demo involves writing code or entering long command lines  there is always a risk of errors. When you have a dozen or more (virtual) machines on the network the chance that one is on when it should be off (e.g. you have two DHCP or Remote boot servers for different demos) or off when it should be on becomes greater than 50:50. Everyone has seen a demo fail and the presenter battle to get working long past the point when they should give up. And when you need to concentrate on what you're doing you have a thousand eyes staring at you and every instinct you have is yelling "KEEP TALKING". A second presenter can

    • Do the next part of the presentation, while you fix a problem
    • Explain what you're doing when what you have to type needs you to shut up and concentrate
    • Do a step of the demo when you've stepped out to answer a question from the floor

    They can also be a stooge "So James what's this great feature here do then ? " - but if they do their job right they can ask the question that the audience want to know, or even say "James, at the last event you explained what that did, would it be good to share that again ?". Bottom line ? Having a co-presenter improves my delivery

    A huge amount of effort goes into to these events and we're seeing dozens of positive comments - some by e-mail and some on our blogs as well, and it's good to know we've pleased a large slice of the audience.

    There will always be a few who can't be pleased. There was the person in London who wrote "I was under the impression that Windows Server 2008, Visual Studio 2008 and SQL Server 2008 were going to be the focal points for the day." who must have clicked the link to register without reading the description.

    And then in Manchester we had a couple of comments in the "What would make things better"

    • An understanding by microsoft to move to a new Operating system (Vista) is not just a technology jump but a huge staff training exercise.
    • Stop banging on about Vista and its benefits. Microsoft has to accept that they have misjudged the market and need to put the customer first. By implying Vista SP1 will fix everything is ignorant on Microsoft's part. We can not keep re-training users just because Microsoft have brought out a new interface/Operating system.

    Sorry... "Don't talk about benefits of a new product". Why ever not ? Why come to events about new products if you don't want to hear about them ? Accept that we've "misjudged the market" - that would mean starting to believe that most IT managers are Luddites who don't give a stuff about user productivity or security or the proportion of their own time that is needed just to maintain the status quo. "We can't keep re-training users just to make them more productive". If that were the case we'd still be on DOS, or Windows 3.1 or Windows 95. I doubt if either of these guys have even used Vista. Contrary to what you might think most people who work at Microsoft UK (many of whom are agency staff) work in non technical roles - Sales, Marketing, HR and so on. One great boss in my pre-Microsoft days had sold Custard Powder (he worked for Bird's selling it to supermarkets after the famous factory explosion) and he said he knew no more IT than he did about Custard powder. Our marketing people got 2 hours on vista and office 2007, tops. The greater productivity that you get from both pretty quickly cancels out the initial dip that comes from finding your way round a new environment. With more and more Vista PCs going into homes it won't be long before you have to spend time training people how to use the unfamiliar - and less productive - older software.

  • James O'Neill's blog

    iSCSI, clustering and Hyper-V

    • 0 Comments

    One of the things I've been saying I'd blog for a while is how to set up a cluster on Hyper-V. Since Hyper-V does not support sharing SCSI disks between machines, you need to use iSCSI - which is all fine and good if you're doing it in production with real workloads and a proper budget; but a bit of a pain if you're doing it in a lab or training environment. Inside Microsoft I can get what I need courtesy of storage server, but what about the rest of the world ? There are several  iSCSI products with free evaluation versions so I was going to try a couple out, document them and post the results. Well my colleague in Ireland Gavin McShera, has saved me the trouble, with a blog post which explains it all. Once you've set-up iSCSI you Gavin links to the article on setting up a 2 node file serving cluster and you can see how easy clustering really is in 2008.

Page 1 of 2 (27 items) 12

May, 2008