Sugerir traducción
 
Otros han sugerido:

progress indicator
No hay más sugerencias.
MSDN Magazine > Inicio > Todos los números > 2008 > Diciembre >  Ejecución de prueba: configuración prueba con V...
Ver contenido:  en paraleloVer contenido: en paralelo
La información aquí ofrecida es contenido traducido automáticamente que los miembros de la comunidad pueden editar. Quienes deseen contribuir a mejorar la traducción, pueden hacer clic en el vínculo “editar” asociado a cualquiera de los siguientes enunciados.
Test Run
Configuration Testing With Virtual Server, Part 2
Dr. James McCaffrey and Paul Despe
Code download available from the MSDN Code Gallery
Browse the Code Online
There are several ways you can perform software configuration testing. One effective approach for certain scenarios is to use Microsoft Virtual Server to create a library of virtual machines. Because Virtual Server is built on a set of COM modules, you can completely automate the process of creating and exercising virtual machines.
Although individual Virtual Server automation tasks are quite well documented, in discussions with our testing colleagues we learned that there is a need for a complete end-to-end example that puts together all the parts of automating Virtual Server for software configuration testing. Additionally, almost all current Virtual Server automation references use the old VBScript language rather than the much more powerful Windows PowerShell.
In this month's column, we're going to walk you through the process of automating software configuration testing using Virtual Server and Windows PowerShell.We will assume that you have a basic familiarity with them, but you should be able to follow our examples even if these technologies are new to you.
(If you do want more information, you can read about Windows PowerShell in the May 2007 installment of Test Run—" Lightweight Testing with Windows PowerShell "—and in " CMDLETS: Extend Windows PowerShell with Custom Commands " by Jim Truher. I also covered configuration testing with Virtual Server in my September 2008 Test run column " Configuration Testing With Virtual Server, Part 1 .")
The screenshot in Figure 1 gives you a good idea of where we're headed in this column. Our physical host machine is running Windows Server 2008; however, all the techniques described here also work with Windows Server 2003. The host machine has Virtual Server 2005 R2 SP1 installed. The screenshot in Figure 1 shows a virtual guest machine named VitualMachine-Test that is running Windows XP SP2. What is not apparent from the screenshot is that the virtual machine was created using a Windows PowerShell 1.0 script. In the background of the screenshot, you can see that a second Windows PowerShell script has set up a Scheduled Task on the virtual machine. This task is the test automation, which is running in the cmd.exe shell in the foreground of the virtual machine.
Figure 1 Example of Configuration Testing (Click the image for a larger view)
In the sections of this column that follow, we'll explain in detail the script that created the virtual machine shown in Figure 1 and the script that set up and executed the test automation. We think you'll find the techniques we present here a useful addition to your software testing, development, and management skill sets.

Virtual Server Automation with Windows PowerShell
Virtual Server is built on a set of objects that use classic DCOM technology. You can automate Virtual Server using any COM-aware language, including VBScript, JavaScript, C#, Visual Basic .NET, and Windows PowerShell. Our preferred approach is to use Windows PowerShell. Its advantages include its ability to directly call into the Microsoft .NET Framework and its command-line capabilities, which allow you to develop scripts interactively. Because VBScript and JavaScript are native Win32 scripting languages, they can directly create and use Virtual Server automation objects. But because Windows PowerShell is a .NET-compliant language, in order to use Virtual Server automation remote objects with Windows PowerShell you must enable impersonation on each object.
There are several approaches you can use. The most straightforward approach is to use Visual Studio and C# to create a custom DLL that contains a method that allows you to set an impersonation level on a native Win32 object, and then call that custom method from Windows PowerShell. We use a somewhat surprising alternative approach, which is to write and compile a custom DLL on the fly using Windows PowerShell.
The script in Figure 2 creates a Windows PowerShell function named SetImpersonation, which we can use to set the impersonation level on Virtual Server automation objects. We decided to place our code inside the special Windows PowerShell $profile startup script. We begin our script with two write-host messages to alert users that a custom startup script is adding additional functionality to Windows PowerShell. Next, we set up references to the C# compiler and a temporary file:
$csc = (join-path ($env:windir) Microsoft.NET\Framework\v2.0.50727\csc.exe)
$tempFile = [IO.Path]::GetTempFileName() + ".cs"
write-host "Executing custom startup script"
write-host "Creating custom SetImpersonation function'n"
set-location D:\

$csc = (join-path ($env:windir) Microsoft.NET\Framework\v2.0.50727\csc.exe)

$tempFile = [IO.Path]::GetTempFileName() + ".cs"

$source = @"
  using System;
  using System.Runtime.InteropServices;

  public class MySecurity
  {
    [DllImport("Ole32.dll", CharSet=CharSet.Auto)]
    public static extern int CoSetProxyBlanket(IntPtr pProxy,
      uint dwAuthnSvc,
      uint dwAuthzSvcp2,
      IntPtr pServerPrincName,
      uint dwAuthnLevel,
      uint dwImpLevel,
      IntPtr pAuthInfo,
      uint dwCapabilities);

    public static int EnableImpersonation(object objDCOM) {
      return CoSetProxyBlanket(Marshal.GetIDispatchForObject(objDCOM), 
      10, 0, IntPtr.Zero, 0, 3, IntPtr.Zero, 0);
    }
  }
"@

set-content $tempFile $source
$targetDLL = [IO.Path]::GetTempFileName() + ".dll"

invoke-expression "$csc /nologo /target:library /out:$targetDLL $tempFile"
[System.Reflection.Assembly]::LoadFrom($targetDLL) | out-null

function SetImpersonation($obj)
{
  [MySecurity]::EnableImpersonation($obj) | out-null
}
We use the join-path cmdlet to create a full path to the .NET Framework 2.0 C# compiler, which we assume is on our host machine. Then we call directly into the System.IO namespace and use the static GetTempFileName method of the Path class to generate a unique file name with a .cs extension, located in the system Temp folder of our host machine.
Next we set up C# source code in order to create our custom impersonation function:
$source = @"
  using System;
  using System.Runtime.InteropServices;

  public class MySecurity
  {
    . . . 
  }
"@
We use the here-string feature of Windows PowerShell to create, in essence, a complete C# program as a single string. The idea is to use the C# P/Invoke mechanism to create a managed wrapper method named EnablePersonation around the Win32 CoSetProxyBlanket function. The details of the CoSetProxyBlanket function are outside the scope of this column, but the bottom line is that we create a method that allows objects to impersonate the client's security context on remote systems.
With our C# source code created, next we actually build a managed DLL on the fly:
set-content $tempFile $source
$targetDLL = [IO.Path]::GetTempFileName() + ".dll"
invoke-expression "$csc /nologo /target:library /out:$targetDLL $tempFile"
[System.Reflection.Assembly]::LoadFrom($targetDLL) | out-null
We use the set-content cmdlet to place the C# source code into our temp file. Next we use the GetTempFileName to create a unique name for our DLL file. We could have left this step out, because the C# compiler by default will create a DLL name based on the name of the source code file. Then we use the invoke-expression cmdlet to run the C# compiler.
Because our argument to invoke-expression is a double-quoted string, the objects inside the string will be evaluated. After the C# compiler builds our custom DLL with our EnableImpersonation method, we use the LoadFrom method to load the DLL into our current Windows PowerShell execution environment.
Our script then finishes by creating a Windows PowerShell wrapper function named SetImpersonation around the C# EnableImpersonation method:
function SetImpersonation($obj) 
{
  [MySecurity]::EnableImpersonation($obj) | out-null
}
We pipe our call to EnableImpersonation to the out-null cmdlet simply to suppress printing diagnostic information when the function is called. By placing this script in your Windows PowerShell startup program, every time you launch a new instance of Windows PowerShell, the script will execute and you will create a new custom DLL with a SetImpersonation function.
The disadvantage of this approach is that you are recreating the same DLL every time you launch an instance of Windows PowerShell. Alternatively, you can place this code directly into the beginning of any Windows PowerShell script that needs to access Virtual Server COM objects. But we do not recommend that you simply build the custom DLL once (with C# or on the fly with Windows PowerShell) and then place that DLL somewhere on your host machine.
Automation scripts often have a way of persisting much longer than you initially expect. You don't want to be in a position months or even years later where you've lost your DLL and have no idea how to recreate it.

Creating a Virtual Machine
The screenshot in Figure 3 summarizes the steps you perform when using automation to create a Virtual Server virtual machine. We begin by verifying that we have our custom SetImpersonation function available. Next we check to see if our virtual machine configuration files already exist from a previous automation run. If the configuration files exist, we stop the background Virtual Server service and delete the files, then restart the service.
Figure 3 Creating a Virtual Machine (Click the image for a larger view)
Next we instantiate the primary Virtual Server automation object, which contains all automation functionality. Then we create a virtual machine and provision its RAM, hard disk drive (with operating system), CD/DVD drive, and network adapter. We finish by starting the virtual machine, determining when the virtual machine's operating system is up and running.
Let's walk through the createVM.ps1 Windows PowerShell script shown running in Figure 3. Our script begins by looking for the SetImpersonation function described in the previous section:
write-host "'nChecking to see if SetImpersonation function exists"
$funcs = get-childitem function:\*
$found = $false
foreach ($f in $funcs) {
  if ($f.name -eq "SetImpersonation") {
    $found = $true
  }
}
We use the get-childitem cmdlet to retrieve a collection of all functions known to our current Windows PowerShell environment. We could also have used the get-command cmdlet with a "-commandtype function" argument. We iterate through every item in the collection looking for a function with a SetImpersonation name property. Alternatively, we could have used the Windows PowerShell break statement to exit our iteration loop early if SetImpersonation is found.
After examining the function:\ collection, we can use our Boolean $found variable to determine if the SetImpersonation function is in our current environment:
if ($found) {
 write-host "Found custom SetImpersonation function"
}
else {
 throw "No SetImpersonation function found"
}
Here we decide to throw an exception if the SetImpersonation function is not found, because the rest of our automation will certainly fail. The next phase of our automation prepares to delete a virtual machine configuration file if it already exists. To delete a virtual machine configuration file using Windows PowerShell, you must first stop the background Virtual Server service, so we create a StopService utility function to do so.
The helper function begins by retrieving information about the Virtual Server service:
function StopService
{
  write-host "'nStopping Virtual Server service"
  $s = get-service "Virtual Server"

  if ($s.status -ne "Stopped") {
    $s.stop()
  }
  . . .
}
We use the handy get-service cmdlet to fetch an object that contains a Status property and then call the object's Stop method. At this point we could pause our automation to give the Virtual Server service time to stop, but the problem is that we have no way of knowing how long to pause.
A much better approach is to go into a delay loop:
$ct = 0
while ( ((get-service "Virtual Server").status -ne "Stopped")
          -and ($ct -lt 100) ) {
  $ct++
  start-sleep -m 200
  write-host "Waiting for Virtual Server service to stop . . ."
}
We initialize a counter to keep track of how many times we pause our automation. Then we go into a delay loop that will exit as soon as the service Status is equal to Stopped or if we exceed 100 delays. Inside our delay loop, we use the start-sleep cmdlet to pause for 200 milliseconds, so we will delay at most 20,000 milliseconds (20 seconds). After our delay loop terminates, we check to see how it exited:
if ( (get-service "Virtual Server").status -ne "Stopped" ) {
  throw "Failed to stop Virtual Server service in allowed time"
}
If the Virtual Server status is not Stopped, that means the loop exited because it exceeded 100 iterations, so we throw an exception. We define a StartService helper function, which uses the same logic as the StopService function:
. . .
if ($s.status -ne "Running") {
  $s.start()
}
. . .
while ( ((get-service "Virtual Server").status -ne "Running")
        -and ($ct -lt 100) ) {
. . .
}
if ( (get-service "Virtual Server").status -ne "Running" ) {
  throw "Failed to start Virtual Server service in allowed time"
An alternative to placing the StopService and StartService helper functions directly inside your virtual machine creation script is to place these helper functions inside the Windows PowerShell $profile startup script. With our two helper functions defined, we can now determine if we need to delete any virtual machine configuration files left from previous automation:
$vmcFile = test-path "D:\VMs\VirtualMachine-Test.vmc"
$lnkFile = test-path "D:\Users\All Users\Microsoft\Virtual Server\Virtual Machines\VirtualMachine-Test.lnk"
We use the test-path cmdlet to return a true/false value depending upon whether the cmdlet's file argument exists or not.
When you create a virtual machine, you can place the .vmc configuration file anywhere on your host machine. Virtual Server also creates a shortcut .lnk file to the .vmc file at a standard location, which depends upon your host operating system—for example, %SystemRoot%\Users\All Users\Microsoft\Virtual Server\Virtual Machines\ in the location in Windows Server 2008.
To create a virtual machine with a particular name, you must delete both the .vmc and corresponding .lnk files or Virtual Server will complain that your virtual machine already exists. Notice that our virtual machine name is hardcoded into our script for clarity; in a production environment, you will likely want to parameterize this value and pass it to your script. Once we've determined if any old virtual machines exist we can delete them, as we do in Figure 4.
if ($vmcFile -or $lnkFile) {
  write-host "'nFound existing .vmc and/or .lnk file"
  StopService
  if ($vmcFile) {
    write-host "Deleting .vmc file"
    remove-item "D:\VMs\VirtualMachine-Test.vmc"
  }
  if ($lnkFile) {
    write-host "Deleting .lnk file"
    remove-item "D:\Users\All Users\Microsoft\Virtual Server\Virtual
      Machines\VirtualMachine-Test.lnk"
  }
  StartService
}
else {
 write-host "'nDid not find existing .vmc or .lnk files"
}
If either our target .vmc or .lnk files exist, we call our StopService helper function to halt the background Virtual Server service. Then we use the remove-item cmdlet to delete the appropriate files and then restart the service.
At this point in our script, we are ready to create the primary Virtual Server COM automation object:
if ((get-service "Virtual Server").status -ne "Running") {
  StartService
}
$vs = new-object -com "VirtualServer.Application"
if ($vs -eq $null) {
  throw "Failed to create main COM automation object"
}
SetImpersonation $vs
First we make sure that the Virtual Server service is running. Next we use the new-object cmdlet to instantiate an instance of the Virtual Server automation library, which has a ProgID of VirtualServer.Application. And if that creation fails, we throw an exception because our automation is doomed. Otherwise, we invoke our custom SetImpersonation function on the automation object so that the object can impersonate the client's security context through Virtual Server's underlying DCOM transport mechanisms.
Creating a virtual machine is almost too easy:
write-host "'nCreating virtual machine"
$vm = $vs.CreateVirtualMachine("VirtualMachine-Test", "D:\VMs")
if ($vs -eq $null) {
  throw "Failed to create virtual machine object"
}
SetImpersonation $vm
We simply call the CreateVirtualMachine method and pass the name of the virtual machine as a string, and the location of the host where we want the resulting .vmc file to reside—in this case a hardcoded D:\VMs\ directory. Because we need to manipulate the virtual machine object, we set its impersonation level.
At this point in the process, we can begin provisioning our newly created virtual machine:
write-host "Assigning 256 MB memory"
$vm.Memory = 256 

write-host "'nAdding existing virtual hard disk drive"
write-host "Copying .vhd file from library to working directory"
copy-item "D:\VirtualMachinesLibrary\VirtualMachine-WinXP-SP2.vhd"
  "D:\VirtualMachines\"
First we assign RAM for the guest machine, keeping in mind the fact that the total amount of RAM available for all running virtual machines plus the host machine is limited by the amount of RAM on the host machine. Next we prepare to attach a .vhd virtual hard disk drive file to our virtual machine. We copy a .vhd file that contains our OS and other key software from a backup location to a working directory. The idea is that we want to make sure that if our .vhd file becomes corrupted during configuration testing, we still have an original version of our .vhd file to use later.
Because .vhd files are typically about 2.0GB in size, this file copy operation usually takes a couple of minutes. When you create a .vhd file with Virtual Server, the .vhd file is placed in the same directory as its associated .vmc file. However, when attaching an existing .vhd file to a new virtual machine, the .vhd and .vmc files can be in different directories, as we're doing here. And once we have our working .vhd file, we can attach it to our virtual machine:
$loc = "D:\VirtualMachines\VirtualMachine-WinXP-SP2.vhd"
if (test-path $loc) {
  write-host "Found the .vhd file"
  $hd = $vm.AddHardDiskConnection("D:\VirtualMachines\VirtualMachine-WinXP-SP2.vhd", 0, 0, 0) 
}
else {
  throw "Failed to find .vhd file"
}
First we use the test-path cmdlet to make sure our working copy of the target .vhd file exists. Next we use the AddHardDiskConnection method to attach the virtual hard disk. The first parameter to the AddHardDiskConnection method is hard drive bus type, which is an enumeration where vmDriveBusType_IDE = 0 and vmDriveBusType_SCSI = 1.
Interestingly, even if your host machine has only IDE drives, you can specify a SCSI drive for your virtual machine for improved performance. The second parameter is the bus number where the value can be 0 or 1 for IDE drives, and 0 through the number of SCSCI controllers for SCSI drives. The third parameter is the device number where the value can be 0 or 1 for IDE drives and 0 through 6 for SCI drives.
After attaching a .vhd virtual hard drive, we are able to make that drive Undoable:
write-host "Making .vhd disks undoable"
$vm.undoable = $true
By default, Virtual Server automatically saves all changes made to a virtual machine while the machine is running. But if you configure Undo disks, then all changes made to the virtual machine while the machine is running are saved to a .vud virtual undo disk file. And when you shut down the virtual machine, you get the option either to discard the Undo disk (and revert back to the original .vhd disk) or commit the Undo disk (and save changes to the .vhd file.)
Now, with the scheme we're using here—copying a base configuration .vhd file to a working directory—it doesn't matter if our hard disk is undoable or not. But we show you this technique in case you need it for your particular scenario.
Now we provision the CD/DVD drive:
write-host "'nAdding CD/DVD drive"
$dvd = $vm.AddDVDROMDrive(0,1,0)
if ($dvd -eq $null) {
  write-host "Failed to add CD/DVD drive"
}
SetImpersonation $dvd

write-host "Attaching CD/DVD drive to F:"
$dvd.AttachHostDrive("F")
When you create a virtual machine using the Virtual Server GUI interface, Virtual Server automatically adds and attaches a CD/DVD drive to your guest machine if the host machine already has a CD/DVD drive. But when you create a virtual machine with automation, you must explicitly configure a CD/DVD drive.
The first parameter to the AddDVDROMDrive method is the bus type, which must be 0 for a CD/DVD drive. The second parameter is the bus number, which must be 0 or 1. The third parameter is the device number, which also must be 0 or 1. Next we use the AttachHostDrive method to associate the virtual CD/DVD drive on the guest machine with the physical drive on the host machine. Notice we've hardcoded "F:" in our script for clarity, but you may want to parameterize this value and pass it into your script. Now we're ready to configure our network adapters:
$vn = $vs.FindVirtualNetwork('External Network (Broadcom 802.11g Network
   Adapter)')
if ($vn -eq $null) {
  write-host "Failed to find virtual network adapter"
}
SetImpersonation $vn
We use the FindVirtualNetwork method to locate an existing network adapter. Notice that Virtual Server often uses the terms network and network adapter more or less interchangeably. When you install Virtual Server, the setup program scans the host machine for all active adapters and then names the adapters by placing the adapter name in parentheses, preceded by the string "External Network". However, if you add a network adapter after Virtual Server installation, you can name the adapter anything you wish.
After setting the impersonation level on the adapter object, we attach our virtual adapter to our virtual network:
$adapters = $vm.NetworkAdapters
SetImpersonation $adapters
SetImpersonation $adapters.Item(1)
$adapters.Item(1).AttachToVirtualNetwork($vn)
We use the NetworkAdapters property to fetch a collection of all adapters. We must set impersonation on the collection object and each adapter in the collection in order to access an adapter with Windows PowerShell. The first adapter, Item(0), is the built-in "Internal Network" adapter, so Item(1) is the first external adapter. We use the AttachToVirtualNetwork method to connect the adapter to the network. Our configuration is now complete, and we can start our virtual machine:
write-host "'nStarting Virtual Machine"
$startTask = $vm.StartUp()
SetImpersonation $startTask
$startTask.WaitForCompletion(60000) # wait up to 1 minute
The Startup method returns VMTask object that can be used to monitor the method's progress. The task object has a convenient WaitForCompletion method that accepts a maximum value for time, in milliseconds, to wait for the task to complete.
Next we must wait until the operating system on the guest virtual machine boots up:
   write-host "'nWaiting for guest virtu al machine to boot                up'n"
   $ct = 0
   while ( ((test-path "\\VM-017\Public") -eq $false) -and $ct 
          -lt 100 )
   {
     $ct++
     write-host "Waiting . . . $ct" 
     start-sleep -s 10
   }
The approach we take is to use a delay loop. In each iteration through the delay, we pause for 10 seconds. Our while loop condition probes into the virtual machine using test-path and exits if we discover a directory that we know exists on the guest, or if we exceed a maximum number of probing attempts. Our script concludes by determining how our delay loop terminated:
if ((test-path "\\VM-017\Public") -eq $false) {
  throw "'nFailed to discover guest machine"
}
else {
  write-host "'nVirtual machine is up and running"
}
If we cannot discover the virtual guest machine, we throw an exception. Otherwise we display a success message.

Configuration Tests on a Virtual Machine
The previous section describes how to create and provision a virtual machine using a script. At this point you can perform manual testing, but let's look at the key techniques for automating the process of configuration testing. If you look at the screenshot in Figure 5, you can see we have a Windows PowerShell script named exerciseVM.ps1. Our script first verifies that the target virtual machine is running and then copies a test-related file from the host machine to the guest machine. Next our script creates a Windows task on the guest machine that will fire off an automated test at a specified time. The script concludes by turning the virtual machine off and discarding the machine's Undo disk.
Figure 5 Placing Test Automation on a Virtual Machine (Click the image for a larger view)
The exerciseVM.ps1 script begins by using the test-path cmdlet to look for the target virtual machine:
if ((test-path "\\VM-017\C$") -eq $false) {
  throw "'nFailed to discover guest machine"
}
else {
  write-host "Found virtual guest machine"
}
If we can't find the system root of the virtual machine, we throw an exception to terminate the script. Next we verify that test automation file exists on the host:
if (test-path "D:\Public\testHarness.js") {
  write-host "Found testHarness.js file to copy"
}
else {
  throw "Did NOT find testHarness.js file to copy"
}
Then we verify that the guest has a public share to receive our test automation. This approach assumes that the virtual machine's .vhd file has been configured with a share that allows users to copy files to that share. There is no easy way for a script running on a host machine to perform such a security configuration on a guest.
if (test-path "\\VM-017\Public") {
  write-host "Found \\VM-017\Public directory to copy to"
}
else {
  throw "Did NOT find \\VM-017\Public directory to copy to"
}
copy-item "D:\Public\testHarness.js" "\\VM-017\Public"
We simply use the copy-item cmdlet to copy our single automation file from host to guest. Now we set up a Windows task to execute the test automation on the virtual guest machine:
$dt = new-object -com WbemScripting.SWbemDateTime
$now = [DateTime]::now 
$later = $now.addSeconds(90)
write-host "'nTest automation testHarness.js schedeuled to run at " $later
$later.ToUniversalTime() | out-null
$dt.SetVarDate($later)
We are using Windows Management Instrumentation (WMI)/Common Information Model (CIM) technology, so first we create an SWbemDateTime object (as opposed to a .NET DateTime object). We use the .NET static Now property to fetch the current date and time on the host. Then we use the AddSeconds method to create a .NET Date time object that is 90 seconds later. Next we convert the .NET execution time to a Coordinated Universal Time (UTC) object. Then we use the SetVarDate to configure the SWbemDateTime date-time object.
Now we schedule our test automation task:
[System.Management.ManagementClass] $mc =
  new-object System.Management.ManagementClass("\\VM-017\root\cimv2",
    "Win32_ScheduledJob", $null) 
$mc.Create("cscript.exe //nologo C:\Public\testHarness.js",
           $dt.value, $false, 0, 0, $true) | out-null
Instead of wrestling directly with WMI technology using the Windows PowerShell get-wmiobject cmdlet, we use the fact that Windows PowerShell can directly call into the .NET Framework, and we use the ManagementClass of the System.Management namespace. You can think of this class as a friendly .NET wrapper around WMI functions.
The first argument to the object's constructor is the scope of the object and essentially identifies the target machine. The second argument is the WMI class name, and the third argument is an object that contains any optional information to use when retrieving the WMI class.
We use the Create method to set up the scheduled test automation. The first parameter is a string that is the command to execute. The second parameter is an object that specifies when to run the task. The third parameter is a Boolean value that specifies if the task is to run more than once or not. The next two parameters specify when multiple jobs should run. The sixth parameter is a Boolean that specifies if the job should run in interactive mode (in other words, visible) or non-interactive mode.
No script can arbitrarily set up a scheduled task on a remote/guest machine without the cooperation of the guest machine. The details of allowing remote job scheduling vary quite a bit depending upon the operating system being used. In the case of the example shown in Figure 1, the guest machine required four modifications. First, the Windows Firewall on the Windows XP guest must be disabled to allow remote WMI access. Additionally, the Local Security Policy on the guest required three changes in the Security Options area of the Local Policies section of the Security Settings item. The "DCOM: Machine Access Restrictions in Security Descriptor Definition Language syntax" settings must be modified to "Allow for Anonymous Login and Everyone users." The "Network Access: Let Everyone permissions apply to anonymous users" setting must be enabled. And the "Network Access: Sharing and security model for local accounts" setting must be modified to "Classic—local users authenticate as themselves".
In short, if you want to use a Virtual Server host machine to schedule test automation on a guest machine, you need to make several security changes to the underlying .vhd file. Of course, there's no requirement that you schedule tasks from your host machine; an alternative is simply to configure the guest .vhd file with recurring test automation tasks built in.
With our test automation scheduled, we can wait for the tests to run and then shut down our guest machine:
$dummy = read-host -prompt "'nPress <Enter> to continue"
write-host "'nPreparing to turn off guest machine"
write-host "Creating main COM automation object"
$vs = new-object -com "VirtualServer.Application"
if ($vs -eq $null) {
  throw "Failed to create main COM automation object"
}
SetImpersonation $vs
We use the read-host cmdlet to halt our script execution so that we can observe our test automation using the Virtual Server Web-based administration page and determine when all tests are complete. This technique is useful when developing your automation, but in a production environment you will likely want to use some other mechanism to determine when all your tests are finished. For example, one approach is to use a delay loop and use the test-path cmdlet to probe for a test results file.
Our test automation script now has the ability to connect to the virtual machine:
write-host "Looking for virtual machine"
$vm = $vs.FindVirtualMachine("VirtualMachine-Test")
if ($vm -eq $null) {
  throw "Failed to find virtual machine"
}
else {
  write-host "Found virtual machine"
}
SetImpersonation $vm
We use the FindVirtualMachine method to locate the virtual machine, which was created by our createVM.ps1 script and, we assume, is running as a guest. Notice that we do not use the .vmc extension on the name of the target virtual machine.
After setting the impersonation level on the virtual machine object, we can turn off the virtual machine:
write-host "'nTurning virtual machine off"
$offTask = $vm.TurnOff()
SetImpersonation $offTask
$offTask.WaitForCompletion(5000)
write-host "'nDiscarding undo disk"
$vm.DiscardUndoDisks()
The TurnOff method returns a VMTask object that we can use to wait until the virtual machine shuts down or exceeds a timeout value. In this situation, because our virtual hard disks are undoable, we discard the Undo disk that holds changes made to the state of the virtual machine so that the next time we use the machine's .vhd file, we will be starting with a clean state.

Last Word
There are several ways you can adapt and extend the Virtual Server automation techniques we've described in this column. In order to demonstrate as many key principles as possible, we organized our examples into two relatively large, un-parameterized scripts. With the explanations we've presented, you should be able to modify and reorganize our two scripts to suit your own needs and parameterize some or all of the hardcoded values in the script as needed.
Because our scripts are primarily intended for demonstration purposes, we removed some of the error-checking code that you'd normally put into production code. However, our examples have included several error traps, so you shouldn't have too much trouble making your automation robust enough for production scenarios.
Although the new Microsoft Hyper-V virtualization technology may someday replace Virtual Server, Virtual Server is going to be useful for many years to come. Additionally, the Virtual Server automation principles and techniques we've presented here, in particular, the use of Windows PowerShell, will provide you with a nice transition to Hyper-V.

Send your questions and comments to testrun@microsoft.com .

Dr. James McCaffrey works for Volt Information Sciences, Inc., where he manages technical training for software engineers working for Microsoft in Redmond. He has worked on several Microsoft products including Internet Explorer and MSN Search. James is the author of .NET Test Automation Recipes (Apress, 2006). James can be reached at jmccaffrey@volt.com or v-jammc@microsoft.com .

Paul Despe is a Program Manager on the Hyper-V team. Paul has worked as a Software Design Engineer in Test on both the Virtual PC and Virtual Server products. Before joining Microsoft, Paul worked in Japan and at Connectix, a virtualization software company acquired by Microsoft in 2003. Paul can be reached at paulde@microsoft.com .

Ejecución de prueba
Configuración de prueba con Virtual Server, parte 2
Dr. James McCaffrey and Paul Despe
Hay varias formas puede realizar pruebas de configuración de software. Un enfoque eficaz para determinados escenarios consiste en utilizar Microsoft Virtual Server para crear una biblioteca de virtual machines. Gracias a que el servidor virtual está integrado en un conjunto de módulos de COM, puede automatizar completamente el proceso de creación y ejerce máquinas virtuales.
Aunque tareas de automatización de servidor virtual individuales están documentadas bastante bien, en discusiones con nuestros compañeros pruebas hemos aprendido que es necesario para obtener un ejemplo completo de extremo a extremo que coloca juntos todos los elementos de automatizar el servidor virtual para pruebas de configuración de software. Además, casi todas las referencias de automatización de servidor virtual actuales utilice el lenguaje VBScript antiguo en vez de la Windows PowerShell mucho más eficaz.
En la columna de este mes, vamos a le guiará en el proceso de automatizar la configuración de software de pruebas con el servidor virtual y PowerShell.We de Windows se suponga que tiene una básica familiaridad con la ellos, pero debe podrá seguir nuestros ejemplos incluso si estas tecnologías son nuevas en.
(Si desea obtener más información, puede leer sobre Windows PowerShell en la entrega de mayo de 2007 de ejecutar pruebas " Ligero pruebas con Windows PowerShell " y en " Los CMDLETS: Extender Windows PowerShell con comandos personalizados "por Jim Truher. Trata también configuración pruebas con el servidor virtual en mi septiembre de 2008 columna prueba, ejecute " Configuración de prueba con Virtual Server, parte 1 .")
La captura de pantalla en la figura 1 ofrece una buena idea de que se está puntas en esta columna. Nuestro equipo host físico ejecuta Windows Server 2008; sin embargo, todas las técnicas describen aquí también trabajan con Windows Server 2003. El equipo host tiene Virtual Server 2005 R2 SP1 instalado. La captura de pantalla en la figura 1 muestra un equipo de invitado virtual denominado VitualMachine de prueba que se ejecuta el Service Pack 2 de Windows XP. Lo que no es evidente de la captura de pantalla es que el equipo virtual se ha creado mediante una secuencia de comandos de Windows PowerShell 1.0. En el fondo de la captura de pantalla, puede ver que una secuencia de comandos de Windows PowerShell segundo configuró una tarea programada en el equipo virtual. Esta tarea es la automatización de prueba, que se ejecuta en el shell de cmd.exe en primer plano de la máquina virtual.
La figura 1 ejemplo de configuración de prueba (haga clic en la imagen de una vista más grande)
En las secciones de esta columna siguientes, explicaremos en detalle la secuencia de comandos que creó el equipo virtual que se muestra en Figura 1 y el script que configura y ejecuta la automatización de prueba. Pensamos que encontrará las técnicas aquí presentamos una útil adición a los conjuntos de habilidades software las pruebas, desarrollo y administración.

Automatización de servidor virtual con Windows PowerShell
Servidor virtual se basa en un conjunto de objetos que utilizan la tecnología DCOM clásico. Puede automatizar mediante cualquier lenguaje COM tenga en cuenta, incluidos VBScript, JavaScript, C#, Visual Basic .NET y Windows PowerShell de servidor virtual. Nuestro enfoque preferido es usar Windows PowerShell. Sus ventajas incluyen su capacidad para llamar directamente a Microsoft .NET Framework y sus capacidades línea de comandos, que permiten desarrollar secuencias de comandos de forma interactiva. Dado que VBScript y JavaScript son lenguajes de secuencias de comandos de Win32 nativos, directamente pueden crear y utilizar objetos de automatización de servidor virtual. Pero puesto que Windows PowerShell es un lenguaje compatible con .NET, para utilizar objetos remotos de automatización de servidor virtual con Windows PowerShell debe habilitar la representación de cada objeto.
Hay varios enfoques que puede utilizar. El enfoque más sencillo es utilizar Visual Studio y C# para crear una DLL personalizada que contiene un método que permite establecer un nivel de suplantación en un objeto nativo de Win32 y, a continuación, llamar a ese método personalizado desde Windows PowerShell. Utilizamos una cierta de medida sorprender enfoque alternativo, que consiste en escribir y compilar un archivo DLL personalizada sobre la marcha uso de Windows PowerShell.
La secuencia de comandos en la figura 2 crea una función de Windows PowerShell denominada SetImpersonation, que se puede utilizar para establecer el nivel de suplantación en objetos de automatización de servidor virtual. Decidimos colocar nuestro código en el script de inicio perfil especial de Windows PowerShell $. Comenzamos con dos escritura nuestra secuencia de comandos de los mensajes de host a los usuarios de alertas que una secuencia de comandos de inicio personalizado esté agregando una funcionalidad adicional a Windows PowerShell. A continuación, establecemos las referencias al compilador de C# y un archivo temporal:
$csc = (join-path ($env:windir) Microsoft.NET\Framework\v2.0.50727\csc.exe)
$tempFile = [IO.Path]::GetTempFileName() + ".cs"
write-host "Executing custom startup script"
write-host "Creating custom SetImpersonation function'n"
set-location D:\

$csc = (join-path ($env:windir) Microsoft.NET\Framework\v2.0.50727\csc.exe)

$tempFile = [IO.Path]::GetTempFileName() + ".cs"

$source = @"
  using System;
  using System.Runtime.InteropServices;

  public class MySecurity
  {
    [DllImport("Ole32.dll", CharSet=CharSet.Auto)]
    public static extern int CoSetProxyBlanket(IntPtr pProxy,
      uint dwAuthnSvc,
      uint dwAuthzSvcp2,
      IntPtr pServerPrincName,
      uint dwAuthnLevel,
      uint dwImpLevel,
      IntPtr pAuthInfo,
      uint dwCapabilities);

    public static int EnableImpersonation(object objDCOM) {
      return CoSetProxyBlanket(Marshal.GetIDispatchForObject(objDCOM), 
      10, 0, IntPtr.Zero, 0, 3, IntPtr.Zero, 0);
    }
  }
"@

set-content $tempFile $source
$targetDLL = [IO.Path]::GetTempFileName() + ".dll"

invoke-expression "$csc /nologo /target:library /out:$targetDLL $tempFile"
[System.Reflection.Assembly]::LoadFrom($targetDLL) | out-null

function SetImpersonation($obj)
{
  [MySecurity]::EnableImpersonation($obj) | out-null
}
Se usa el cmdlet combinación de ruta de acceso para crear una ruta de acceso completa al compilador de C# .NET Framework 2.0, que suponemos que está en nuestro equipo host. A continuación, nos llamar a directamente en el espacio de nombres de System.IO y utilice el estático método de GetTempFileName de la clase de ruta de acceso para generar un nombre de archivo único con una extensión cs, ubicado en la carpeta de plantilla de sistema de nuestro equipo host.
A continuación establecemos código fuente de C# para crear nuestra función representación personalizada:
$source = @"
  using System;
  using System.Runtime.InteropServices;

  public class MySecurity
  {
    . . . 
  }
"@
Utilizamos la función aquí, la cadena de Windows PowerShell para crear, en esencia, un completo programa de C# como una cadena única. La idea es utilizar el mecanismo de C# P/Invoke para crear un método contenedor administrado denominado EnablePersonation alrededor de la función de Win32 CoSetProxyBlanket. Los detalles de la función CoSetProxyBlanket están fuera del ámbito de esta columna, pero la conclusión es que nos crear un método que permite que los objetos suplantar el contexto de seguridad del cliente en sistemas remotos.
Con nuestro código fuente de C# creada, a continuación realmente creamos una DLL administrada sobre la marcha:
set-content $tempFile $source
$targetDLL = [IO.Path]::GetTempFileName() + ".dll"
invoke-expression "$csc /nologo /target:library /out:$targetDLL $tempFile"
[System.Reflection.Assembly]::LoadFrom($targetDLL) | out-null
Se utiliza el cmdlet set-contenido para colocar el código de origen de C# en nuestro archivo temporal. A continuación, utilizamos la GetTempFileName para crear un nombre único para nuestro archivo DLL. Hemos podría dejado este paso, porque el compilador de C# de forma predeterminada se crea un nombre de archivo DLL basado en el nombre del archivo de código fuente. A continuación, se usa el cmdlet expresión de invocación para ejecutar el compilador de C#.
Dado que nuestro argumento de expresión de invocación es una cadena entre comillas dobles, se evaluarán los objetos dentro de la cadena. Después de que el compilador de C# genera nuestro DLL personalizada con el método EnableImpersonation, utilizamos el método LoadFrom para cargar la DLL en nuestro entorno de ejecución actual de Windows PowerShell.
Nuestra secuencia de comandos, a continuación, finalice mediante la creación de una función de contenedor de Windows PowerShell denominada SetImpersonation alrededor del método EnableImpersonation de C#:
function SetImpersonation($obj) 
{
  [MySecurity]::EnableImpersonation($obj) | out-null
}
Nos canalizar nuestra llamada a EnableImpersonation para el out-null cmdlet simplemente para suprimir la impresión de información de diagnóstico cuando se llama a la función. Mediante la colocación de esta secuencia de comandos en el programa de inicio de Windows PowerShell, cada vez que iniciar una nueva instancia de Windows PowerShell, se ejecutará la secuencia de comandos y se creará una nueva DLL personalizada con una función SetImpersonation.
El inconveniente de este enfoque es que se vuelva a crear la misma DLL cada vez que se inicia una instancia de Windows PowerShell. O bien, puede colocar este código directamente en el principio de cualquier secuencia de comandos de Windows PowerShell que necesita tener acceso a objetos COM de Virtual Server. Pero se no se recomienda que simplemente crear el archivo personalizada DLL una vez (con C# o sobre la marcha con Windows PowerShell) y, a continuación, coloque esa DLL en algún lugar en el equipo host.
Secuencias de comandos de automatización a menudo tienen una forma de almacenar mucho más que espera inicialmente. No desea estar en una posición meses o incluso años más tarde que ha perdido el archivo DLL y no tiene idea cómo volver a crearla.

Creación de una máquina virtual
La captura de pantalla en la figura 3 resume los pasos que realizar al utilizar automatización para crear un equipo de virtual del servidor virtual. Comenzamos comprobando que nos haber nuestra función de SetImpersonation personalizada disponible. A continuación se comprueba si nuestros archivos de configuración de máquina virtual ya existen entre una automatización anterior ejecutar. Si los archivos de configuración existen, detener el servicio de servidor virtual de fondo y eliminar los archivos y después reinicie el servicio.
La figura 3 crear una máquina virtual (haga clic en la imagen de una vista más grande)
A continuación crear nos instancias del objeto automatización principal servidor virtual, que contiene toda la funcionalidad de automatización. A continuación, creamos una máquina virtual y suministrar su memoria RAM, unidad de disco duro (con el sistema operativo), unidad de CD o DVD y adaptador de red. Nos fin iniciando la máquina virtual, determinar al sistema operativo de la máquina virtual es hacia arriba y ejecutar.
Vamos a guiará en la secuencia de comandos de Windows PowerShell de createVM.ps1 mostrada ejecutando en la figura 3 . Nuestra secuencia de comandos comienza buscando la función SetImpersonation descrita en la sección anterior:
write-host "'nChecking to see if SetImpersonation function exists"
$funcs = get-childitem function:\*
$found = $false
foreach ($f in $funcs) {
  if ($f.name -eq "SetImpersonation") {
    $found = $true
  }
}
Se usa el cmdlet get-childitem para recuperar una colección de todas las funciones sabe que nuestro entorno actual de Windows PowerShell. Podríamos también haber usado el cmdlet get-comando con un "-función commandtype " argumento. Nos iterar a través de cada elemento de la colección busca una función con una propiedad de nombre SetImpersonation. Como alternativa, hemos podría utilizado la instrucción break Windows PowerShell para salir de nuestro bucle de iteración antes si se encuentra SetImpersonation.
Después de examinar la función: \ colección, podemos usar nuestro valor de tipo Boolean $ se encuentra la variable para determinar si la función SetImpersonation está en nuestro entorno actual:
if ($found) {
 write-host "Found custom SetImpersonation function"
}
else {
 throw "No SetImpersonation function found"
}
Aquí se decide producirá una excepción en el caso no se encuentra la función SetImpersonation, en el ya que el resto de nuestro automatización ciertamente, genera un error. La siguiente fase de nuestro automatización se prepara para eliminar un archivo de configuración de máquina virtual si ya existe. Para eliminar un archivo de configuración de máquina virtual mediante Windows PowerShell, debe detener primero el fondo de servicio de servidor virtual, por lo que crear una función de utilidad StopService para hacerlo.
La función auxiliar comienza mediante la recuperación de información acerca del servicio de servidor virtual:
function StopService
{
  write-host "'nStopping Virtual Server service"
  $s = get-service "Virtual Server"

  if ($s.status -ne "Stopped") {
    $s.stop()
  }
  . . .
}
Se usa el útil cmdlet de get-service para recuperar un objeto que contiene una propiedad Status y, a continuación, llame al método Stop del objeto. En este punto que se puede pausar nuestra automatización para conceder a la hora de servicio de servidor virtual a detener, pero el problema es que tenemos ninguna forma de saber cuánto para hacer una pausa.
Un mucho mejor enfoque es entran en un bucle de retraso:
$ct = 0
while ( ((get-service "Virtual Server").status -ne "Stopped")
          -and ($ct -lt 100) ) {
  $ct++
  start-sleep -m 200
  write-host "Waiting for Virtual Server service to stop . . ."
}
Se inicializa un contador para realizar un seguimiento de cuántas veces se pausa nuestra automatización. A continuación, vamos en un bucle de retraso que se cerrará tan pronto como el servicio de estado es igual a detenido o si se superan retrasos 100. Dentro de nuestro bucle retraso, utilizamos el cmdlet start-modo de suspensión para hacer una pausa de 200 milisegundos, por lo que se retrasará en más de 20.000 milisegundos (20 segundos). Después de que nuestro bucle retraso finalice, comprobamos cómo terminado:
if ( (get-service "Virtual Server").status -ne "Stopped" ) {
  throw "Failed to stop Virtual Server service in allowed time"
}
Si el estado del servidor virtual no es, esto significa que el bucle terminado porque supera 100 iteraciones, por lo que se produzca una excepción. Definimos una función auxiliar StartService, que utiliza la misma lógica como la función StopService:
. . .
if ($s.status -ne "Running") {
  $s.start()
}
. . .
while ( ((get-service "Virtual Server").status -ne "Running")
        -and ($ct -lt 100) ) {
. . .
}
if ( (get-service "Virtual Server").status -ne "Running" ) {
  throw "Failed to start Virtual Server service in allowed time"
Una alternativa al colocar las funciones de auxiliar StopService y StartService directamente en la secuencia de comandos de máquina virtual creación consiste en colocar estas funciones auxiliares dentro de la secuencia de comandos de inicio de Windows PowerShell $ perfil. Con nuestros dos funciones auxiliares definidos, ahora puede determinar si necesitamos eliminar los archivos de configuración de máquina virtual deja de automatización anterior:
$vmcFile = test-path "D:\VMs\VirtualMachine-Test.vmc"
$lnkFile = test-path "D:\Users\All Users\Microsoft\Virtual Server\Virtual Machines\VirtualMachine-Test.lnk"
Se usa el cmdlet test-ruta de acceso para devolver un valor VERDADERO o FALSO dependiendo de si argumento del archivo el cmdlet existe o no.
Cuando se crea una máquina virtual, puede colocar el archivo de configuración .vmc en cualquier lugar en el equipo host. Servidor virtual crea también un archivo de .lnk de método abreviado para el archivo .vmc en una ubicación estándar, que depende el sistema operativo host, por ejemplo, %SystemRoot%\Users\All Users\Microsoft\Virtual Server\Virtual Machines\ en la ubicación de Windows Server 2008.
Para crear una máquina virtual con un nombre en particular, deben eliminar tanto el .vmc y archivos .lnk correspondientes o servidor virtual se se quejan que el equipo virtual ya existe. Observe que nuestra nombre de máquina virtual se codifica en nuestra secuencia de comandos para mayor claridad; en un entorno de producción, se es probable que desea parametrizar este valor y pasarla a la secuencia de comandos. Una vez que haya determinado si existen las máquinas virtuales antiguos se puede eliminarlos, como se haría en la figura 4 .
if ($vmcFile -or $lnkFile) {
  write-host "'nFound existing .vmc and/or .lnk file"
  StopService
  if ($vmcFile) {
    write-host "Deleting .vmc file"
    remove-item "D:\VMs\VirtualMachine-Test.vmc"
  }
  if ($lnkFile) {
    write-host "Deleting .lnk file"
    remove-item "D:\Users\All Users\Microsoft\Virtual Server\Virtual
      Machines\VirtualMachine-Test.lnk"
  }
  StartService
}
else {
 write-host "'nDid not find existing .vmc or .lnk files"
}
Si bien existen nuestros archivos de .vmc o .lnk de destino, llamamos nuestra función auxiliar de StopService para detener el servicio de servidor virtual de fondo. A continuación, se usa el cmdlet remove-elemento para eliminar los archivos adecuados y, a continuación, reinicie el servicio.
En este momento en nuestra secuencia de comandos, estamos preparados para crear el principal objeto de automatización COM de Virtual Server:
if ((get-service "Virtual Server").status -ne "Running") {
  StartService
}
$vs = new-object -com "VirtualServer.Application"
if ($vs -eq $null) {
  throw "Failed to create main COM automation object"
}
SetImpersonation $vs
En primer lugar, asegúrese de que el servicio de servidor virtual está está ejecutando. A continuación, utilizamos el cmdlet new-objeto para crear una instancia de la biblioteca de automatización Virtual Server, que tiene un ProgID of VirtualServer.Application. Y si se produce un error que creación, se produce una excepción porque está abocada nuestro automatización. De lo contrario, se invocar nuestra función SetImpersonation personalizado en el objeto de automatización para que el objeto puede suplantar contexto de seguridad del cliente mediante mecanismos de transporte del servidor virtual de DCOM subyacentes.
Crear una máquina virtual es casi demasiado fácil:
write-host "'nCreating virtual machine"
$vm = $vs.CreateVirtualMachine("VirtualMachine-Test", "D:\VMs")
if ($vs -eq $null) {
  throw "Failed to create virtual machine object"
}
SetImpersonation $vm
Nos basta con llamar al método CreateVirtualMachine y pasar el nombre de la máquina virtual como una cadena y la ubicación de los host en la que desea el archivo .vmc resultante para residir, en este caso un directorio de D:\VMs\ codificado. Dado que se necesitan manipular el objeto de equipo virtual, se establece su nivel de suplantación.
En este punto en el proceso, puede comenzamos aprovisionamiento nuestro equipo virtual recién creado:
write-host "Assigning 256 MB memory"
$vm.Memory = 256 

write-host "'nAdding existing virtual hard disk drive"
write-host "Copying .vhd file from library to working directory"
copy-item "D:\VirtualMachinesLibrary\VirtualMachine-WinXP-SP2.vhd"
  "D:\VirtualMachines\"
En primer lugar, asignamos memoria RAM para el equipo de invitado, teniendo en cuenta el hecho de que la cantidad total de memoria RAM disponible para todos los equipos virtuales que se está ejecutando más el equipo host está limitada por la cantidad de memoria RAM en el equipo host. A continuación se prepare adjuntar un archivo de disco duro virtual .vhd a nuestro equipo virtual. Se copie un archivo .vhd que contiene el sistema OPERATIVO y otro software clave desde una ubicación de copia de seguridad a un directorio de trabajo. La idea es que desean asegurarse de que si el archivo .vhd resulta dañado durante las pruebas de configuración, todavía tenemos una versión original de nuestro archivo .vhd para usarla más adelante.
Porque hay archivos de .vhd normalmente va 2.0GB en tamaño, esta operación de copia de archivos normalmente tiene un par de minutos. Cuando se crea un archivo .vhd con el servidor virtual, el archivo .vhd se coloca en el mismo directorio que el archivo .vmc asociado. Sin embargo, al adjuntar un archivo .vhd existente a un nuevo equipo virtual, los archivos VHD y .vmc pueden ser en directorios diferentes, como estamos haciendo aquí. Y una vez que tenemos nuestro archivo .vhd de trabajo, se puede adjuntar a nuestra máquina virtual:
$loc = "D:\VirtualMachines\VirtualMachine-WinXP-SP2.vhd"
if (test-path $loc) {
  write-host "Found the .vhd file"
  $hd = $vm.AddHardDiskConnection("D:\VirtualMachines\VirtualMachine-WinXP-SP2.vhd", 0, 0, 0) 
}
else {
  throw "Failed to find .vhd file"
}
En primer lugar se utiliza el cmdlet test-ruta de acceso para asegurarse de que existe nuestro copia de trabajo del archivo de destino .vhd. A continuación utilizamos el método AddHardDiskConnection para adjuntar el disco duro virtual. El primer parámetro al método AddHardDiskConnection es el tipo de bus de unidad de disco duro, que es una enumeración que vmDriveBusType_IDE = 0 y vmDriveBusType_SCSI = 1.
Curiosamente, incluso si el equipo host tiene sólo las unidades IDE, puede especificar una unidad SCSI para el equipo virtual para mejorar el rendimiento. El segundo parámetro es el número de bus donde el valor puede ser 0 o 1 para las unidades IDE y 0 hasta el número de controladores de SCSCI para unidades SCSI. El tercer parámetro es el número de dispositivo donde el valor puede ser 0 o 1 para las unidades IDE y 0 a 6 para SCI unidades.
Después de asociar un disco duro virtual .vhd, son capaces de realizar esa unidad Undoable:
write-host "Making .vhd disks undoable"
$vm.undoable = $true
De forma predeterminada, el servidor virtual automáticamente guarda todos los cambios realizados en una máquina virtual mientras se está ejecutando el equipo. Pero si configura los discos de deshacer, a continuación, todos los cambios realizados en la máquina virtual mientras se está ejecutando el equipo se guardarán en un archivo de disco virtual deshacer .vud. Y cuando apaga el equipo virtual, obtener la opción para descartar el disco Deshacer (y volver al disco original del .vhd) o confirmar el disco Deshacer (y guardar los cambios en el archivo .vhd.)
Ahora, con el esquema se utiliza aquí, copiar un archivo .vhd de configuración básica de un directorio de trabajo, no importa si el disco duro es que se pueden deshacer o no. Pero se muestra esta técnica por si necesita para su escenario particular.
Ahora nos suministrar la unidad de CD o DVD:
write-host "'nAdding CD/DVD drive"
$dvd = $vm.AddDVDROMDrive(0,1,0)
if ($dvd -eq $null) {
  write-host "Failed to add CD/DVD drive"
}
SetImpersonation $dvd

write-host "Attaching CD/DVD drive to F:"
$dvd.AttachHostDrive("F")
Cuando se crea una máquina virtual mediante la interfaz de interfaz de usuario Virtual Server, servidor virtual automáticamente agrega y adjunta una unidad de CD o DVD a su equipo invitado si el equipo host ya tiene una unidad de CD o DVD. Pero cuando se crea una máquina virtual con la automatización, debe configurar explícitamente una unidad de CD o DVD.
El primer parámetro al método AddDVDROMDrive es el tipo de bus, que debe ser 0 para una unidad de CD o DVD. El segundo parámetro es el número de bus, que debe ser 0 o 1. El tercer parámetro es el número de dispositivo, que también debe ser 0 o 1. A continuación, utilice el método AttachHostDrive para asociar la unidad de DVD/CD virtual en el equipo de invitado a la unidad física en el equipo host. Observe se ha codificado "f:" en nuestra secuencia de comandos para mayor claridad, pero quizás desee parametrizar este valor y pasarla a la secuencia de comandos. Ahora estamos listos configurar nuestros adaptadores de red:
$vn = $vs.FindVirtualNetwork('External Network (Broadcom 802.11g Network
   Adapter)')
if ($vn -eq $null) {
  write-host "Failed to find virtual network adapter"
}
SetImpersonation $vn
Se utiliza el método FindVirtualNetwork para encontrar un adaptador de red existente. Observe que el servidor virtual con frecuencia utiliza la red de términos y el adaptador de red más o menos indistintamente. Cuando se instala el servidor virtual, el programa de instalación examina el equipo host para todos los adaptadores de activos y, a continuación, nombres de los adaptadores colocando el nombre del adaptador en paréntesis, precedidos por la cadena "red externo". Sin embargo, si agrega un adaptador de red tras la instalación de servidor virtual, puede denominar el adaptador de todo lo que desee.
Después de establecer el nivel de suplantación en el objeto de adaptador, nos adjuntar nuestro adaptador virtual a nuestra red virtual:
$adapters = $vm.NetworkAdapters
SetImpersonation $adapters
SetImpersonation $adapters.Item(1)
$adapters.Item(1).AttachToVirtualNetwork($vn)
Utilizamos la propiedad de NetworkAdapters para recuperar una colección de todos los adaptadores. Se debe establecer suplantación el objeto de colección como cada adaptador de la colección para tener acceso a un adaptador con Windows PowerShell. El primer adaptador, Item(0), está el adaptador de "red interna" integrado, por lo que Item(1) es el primer adaptador externo. Usamos el método AttachToVirtualNetwork para conectar el adaptador a la red. Nuestra configuración ahora es completa, y se puede iniciar nuestro equipo virtual:
write-host "'nStarting Virtual Machine"
$startTask = $vm.StartUp()
SetImpersonation $startTask
$startTask.WaitForCompletion(60000) # wait up to 1 minute
El método de inicio devuelve objeto VMTask que puede utilizarse para supervisar el progreso del método. El objeto de tarea tiene un método cómodo de WaitForCompletion que acepta un valor máximo por vez, en milisegundos, que esperar a la tarea se complete.
A continuación se debe esperar hasta que se inicia el sistema operativo en el equipo de invitado virtual:
   write-host "'nWaiting for guest virtu al machine to boot                up'n"
   $ct = 0
   while ( ((test-path "\\VM-017\Public") -eq $false) -and $ct 
          -lt 100 )
   {
     $ct++
     write-host "Waiting . . . $ct" 
     start-sleep -s 10
   }
El enfoque que tomamos es utilizar un bucle de retraso. En cada iteración a través de la demora, se detenga durante 10 segundos. Nuestro while bucle las sondas de condición en el equipo virtual con ruta de acceso de prueba y sale si se descubre un directorio que se sabe que existe en el invitado o si se superan un número máximo de intentos de sondeo. Nuestra secuencia de comandos termina al determinar cómo terminado nuestro bucle de retraso:
if ((test-path "\\VM-017\Public") -eq $false) {
  throw "'nFailed to discover guest machine"
}
else {
  write-host "'nVirtual machine is up and running"
}
Si se no se puede detectar el equipo del invitado virtual, producir una excepción. En caso contrario, se muestra un mensaje correcto.

Las pruebas de configuración en un equipo virtual
La sección anterior describe cómo crear y aprovisionar un equipo virtual con una secuencia de comandos. En este momento puede realizar pruebas manuales pero, echemos un vistazo a las técnicas claves para automatizar el proceso de pruebas de configuración. Si observa la captura de pantalla en la figura 5 , puede ver que tenemos una secuencia de comandos de Windows PowerShell denominada exerciseVM.ps1. Nuestra secuencia de comandos comprueba primero que la máquina virtual de destino se está ejecutando y copia, a continuación, un archivo de relacionadas con pruebas desde el equipo host en el equipo de invitado. A continuación nuestra secuencia de comandos crea una tarea de Windows en el equipo invitado que se desencadenará fuera una prueba automatizada a una hora especificada. La secuencia de comandos termina si se desactivan la máquina virtual y descartar Deshacer discos el equipo.
La figura 5 ubicar la automatización de prueba en una máquina virtual (haga clic en la imagen de una vista más grande)
La secuencia de comandos exerciseVM.ps1 se comienza con el cmdlet test-ruta de acceso para buscar el equipo virtual de destino:
if ((test-path "\\VM-017\C$") -eq $false) {
  throw "'nFailed to discover guest machine"
}
else {
  write-host "Found virtual guest machine"
}
Si no encontramos la raíz del sistema de la máquina virtual, se produce una excepción para terminar la secuencia de comandos. A continuación, comprobar ese archivo de automatización de prueba existe en el host:
if (test-path "D:\Public\testHarness.js") {
  write-host "Found testHarness.js file to copy"
}
else {
  throw "Did NOT find testHarness.js file to copy"
}
A continuación, se compruebe que el invitado tiene un recurso compartido público para recibir nuestro automatización de prueba. Este enfoque, se supone que el archivo .vhd de la máquina virtual se ha configurado con un recurso compartido que permite a los usuarios copiar archivos en ese recurso compartido. No es fácil para una secuencia de comandos que se ejecutan en un equipo host para realizar dicha configuración de seguridad en un invitado.
if (test-path "\\VM-017\Public") {
  write-host "Found \\VM-017\Public directory to copy to"
}
else {
  throw "Did NOT find \\VM-017\Public directory to copy to"
}
copy-item "D:\Public\testHarness.js" "\\VM-017\Public"
Se usa simplemente el cmdlet de copia de artículo para copiar nuestro archivo único de la automatización de host a invitado. Ahora establecemos una tarea de Windows para ejecutar la automatización de prueba en el equipo del invitado virtual:
$dt = new-object -com WbemScripting.SWbemDateTime
$now = [DateTime]::now 
$later = $now.addSeconds(90)
write-host "'nTest automation testHarness.js schedeuled to run at " $later
$later.ToUniversalTime() | out-null
$dt.SetVarDate($later)
Se están mediante Instrumental de administración de Windows (WMI) /Create tecnología de modelo de información común (CIM), por lo que primero se un objeto SWbemDateTime (a diferencia a un objeto .NET DateTime). Utilizamos la propiedad de ahora estática de .NET para recuperar la fecha y hora actuales en el host. A continuación, utilizamos el método AddSeconds para crear un objeto de tiempo de Date de .NET que es de 90 segundos más tarde. A continuación, convertir el tiempo de ejecución de .NET en un objeto de hora universal coordinada (UTC). A continuación, utilizamos la SetVarDate para configurar el objeto de fecha-hora SWbemDateTime.
Ahora nos programar nuestra tarea de automatización de pruebas:
[System.Management.ManagementClass] $mc =
  new-object System.Management.ManagementClass("\\VM-017\root\cimv2",
    "Win32_ScheduledJob", $null) 
$mc.Create("cscript.exe //nologo C:\Public\testHarness.js",
           $dt.value, $false, 0, 0, $true) | out-null
En lugar de wrestling directamente con la tecnología WMI mediante el cmdlet get-wmiobject Windows PowerShell, se utilice el hecho de que Windows PowerShell puede llamar directamente a .NET Framework y utilizamos el ManagementClass del espacio de nombres de System.Management. De esta clase se puede considerar como un contenedor descriptivo de .NET alrededor de las funciones WMI.
El primer argumento al constructor el objeto es el ámbito del objeto y básicamente, identifica el equipo de destino. El segundo argumento es el nombre de clase WMI, y el tercer argumento es un objeto que contiene cualquier información opcional que se utilizarán al recuperar la clase WMI.
Se utilice el método Create para configurar la automatización de prueba programado. El primer parámetro es una cadena que es el comando que se va a ejecutar. El segundo parámetro es un objeto que especifica cuándo se debe ejecutar la tarea. El tercer parámetro es un valor de tipo Boolean que especifica si la tarea es ejecutar más de una vez o no. Los parámetros siguientes dos especifican cuándo deben ejecutarse varios trabajos. El parámetro sexto es un valor de tipo Boolean que especifica si se debe ejecutar el trabajo en el modo interactivo (es decir, visible) o en modo no interactivo.
Secuencia de comandos arbitrariamente no puede configurar una tarea programada en un equipo remoto y invitado sin la cooperación del equipo invitado. Los detalles de permitir la programación de trabajo remoto varían bastante un poco según el sistema operativo está utilizando. En el caso del ejemplo que se muestra en la figura 1 , el equipo del invitado requiere cuatro modificaciones. En primer lugar, el Firewall de Windows en el invitado de Windows XP debe estar deshabilitado para permitir el acceso remoto de WMI. Además, la directiva de seguridad local en el invitado requiere tres cambios el área de opciones de seguridad de la sección directivas locales del elemento de configuración de seguridad de. El " DCOM: restricciones de acceso del equipo en la sintaxis de lenguaje de definición de descriptores de seguridad de " configuración debe modificarse para "Permitir inicio de sesión anónimo y todos los usuarios." El " acceso A la red: permisos aplicación de todos a los usuarios anónimos que " se debe habilitar la configuración. Y el " acceso A la red: modelo de uso compartido y seguridad para local cuentas "configuración debe modificarse para" clásico, autenticar a los usuarios locales como a sí mismos ".
En resumen, si desea utilizar un equipo host de Virtual Server para programar la automatización de prueba en un equipo de invitado, necesita realizar varios cambios de seguridad en el archivo .vhd subyacente. Por supuesto, no hay ningún requisito que programar las tareas desde el equipo host; una alternativa consiste simplemente en configurar el archivo .vhd invitado con tareas repetitivas de automatización de prueba integradas en.
Con la nuestra automatización de prueba programada, se puede esperar para que las pruebas ejecutar y, a continuación, apagar nuestro equipo invitado:
$dummy = read-host -prompt "'nPress <Enter> to continue"
write-host "'nPreparing to turn off guest machine"
write-host "Creating main COM automation object"
$vs = new-object -com "VirtualServer.Application"
if ($vs -eq $null) {
  throw "Failed to create main COM automation object"
}
SetImpersonation $vs
Utilizamos la lectura: host cmdlet para detener la ejecución de nuestra secuencia de comandos para que se pueda observar nuestro automatización de prueba mediante la página de administración basada en Virtual Server Web y determinar cuando comprueba todos los se hayan completado. Esta técnica es útil al desarrollar la automatización, pero en un entorno de producción probablemente deseará utilizar algún otro mecanismo para determinar cuándo se terminen todas las pruebas. Por ejemplo, un enfoque es que utilice un bucle de retraso y que utilice el cmdlet test-ruta de acceso para buscar un archivo de resultados de prueba.
Nuestra secuencia de comandos de automatización de prueba ahora tiene la capacidad de conectarse a la máquina virtual:
write-host "Looking for virtual machine"
$vm = $vs.FindVirtualMachine("VirtualMachine-Test")
if ($vm -eq $null) {
  throw "Failed to find virtual machine"
}
else {
  write-host "Found virtual machine"
}
SetImpersonation $vm
Se utiliza el método FindVirtualMachine para localizar el equipo virtual, que se creó mediante nuestra secuencia de comandos createVM.ps1 y suponemos, se ejecuta como un invitado. Observe que se no se utilizan la extensión .vmc en el nombre de la máquina virtual de destino.
Después de establecer el nivel de suplantación en el objeto de equipo virtual, se puede desactivar el equipo virtual:
write-host "'nTurning virtual machine off"
$offTask = $vm.TurnOff()
SetImpersonation $offTask
$offTask.WaitForCompletion(5000)
write-host "'nDiscarding undo disk"
$vm.DiscardUndoDisks()
El método TurnOff devuelve un objeto VMTask que se puede utilizar para esperar hasta que el equipo virtual se apaga o supera un valor de tiempo de espera. En esta situación, dado que nuestro discos duros virtuales son que se pueden deshacer, nos descartar el disco de deshacer que contiene los cambios realizados en el estado de la máquina virtual para que la próxima vez que utilice el archivo .vhd de la máquina, se se comenzando por un estado limpio.

Última Word
Hay varias formas puede adaptar y ampliar las técnicas de automatización de servidor virtual que hemos descrito en esta columna. Para demostrar los principios claves tantos como sea posible, se organizan nuestros ejemplos en secuencias de comandos relativamente grandes, un-parameterized dos. Con las explicaciones que nos ha presentado, debe poder modificar y reorganizar nuestras secuencias de comandos dos para sus propias necesidades y parametrizar algunos o todos los valores codificados en la secuencia de comandos según sea necesario.
Puesto que nuestras secuencias de comandos están concebidos especialmente para fines de la demostración, se suprimen parte del código de comprobación de errores que normalmente se desea colocar en código de producción. Sin embargo, nuestros ejemplos han incluyen varias capturas de errores, por lo que no tiene demasiada problemas al realizar la automatización sólida para escenarios de producción.
Aunque la nueva tecnología de virtualización de Microsoft Hyper-V día puede reemplazar el servidor virtual, servidor virtual será útil durante muchos años por llegar. Además, los principios de automatización de servidor virtual y las técnicas que nos ha presentado aquí, en particular, el uso de Windows PowerShell, le proporcionará una transición interesante para Hyper-V.

Envíe sus preguntas y comentarios a testrun@Microsoft.com .

Dr. James McCaffrey funciona para información biológicas volt, Inc., que administra el formación técnica para ingenieros de software trabajar para Microsoft en Redmond. Ha trabajado en varios productos de Microsoft como Internet Explorer y MSN Search. James es el autor de .NET Test Automation Recipes (Apress, 2006). James puede ser contactado en jmccaffrey@volt.com o v-jammc@microsoft.com .

Paul Despe es un administrador de programas en el equipo de Hyper-V. Paul ha trabajado como ingeniero de diseño de software de prueba en los productos tanto el equipo virtual y virtual Server. Antes de unirse a Microsoft, Paul trabajó en Japón y en Connectix, una compañía de software de virtualización adquirida por Microsoft en 2003. Paul puede ponerse en paulde@Microsoft.com .

Page view tracker