Previous page

Locate page in Contents

Creating a Simple OS Installation Program

In this section, we will write a simple program that can be used to automatically install a hypothetical operating system inside a brand new virtual machine.

Step 1 - Preparation

First, we have to capture all screens that the OS installation wizard displays to the user.

  1. Create a blank virtual machine, mount a CD drive in it, insert the OS installation disk (or mount an ISO image of the disk), and start the virtual machine.
  2. Using the Parallels API, programmatically connect to the virtual machine and begin a Remote Desktop Access session with it.
  3. At this point, the first OS installation screen should be displayed, waiting for user interaction. Using the Remote Desktop Access API, capture this screen to a file.
  4. Go back to the virtual machine console and manually make the appropriate selections on it (for example, press the Continue button). Write down the accelerator key assigned to the control (Alt-N for instance, or Enter if this is a default button, or the appropriate keystroke if the screen is in a text mode).
  5. When the next screen opens, capture it to a file using the API the same way you captured the first screen. Write down the controls that advance you to the next installation screen the same way we did in the previous step.
  6. Repeat for all of the installation screens until the operating system is fully installed, or until a desired point in the installation process is reached.
  7. In the end, you should have a collection of files containing images of installation screens and instructions for each screens describing the actions that should be taken on each one of them.

Step 2 - Writing the automated OS installation program

Now that we have screenshots and interaction instructions, we can write the program that will automatically install the OS on any blank virtual machine.

  1. Every remote desktop access session must begin with the VmIO.connect_to_vm method invocation and end with VmIO.disconnect_from_vm. These step are necessary to properly initialize/deinitialize the remote desktop access library and protocol.
  2. Capture the current virtual machine desktop screen to a file.
  3. Make a bit-by-bit comparison of the screen that you've just captured to every file that you saved in Step 1 - Preparation (above). Once you find the matching screen, continue to the next step (note: for bit-by-bit comparison to work, a lossless compression or no compression must be used when saving captured screen data).
  4. Read the interaction instructions for the screen you've just found and send the appropriate keyboard (or mouse if necessary) commands to the virtual machine. For example, if the instruction says "press Enter on the keyboard", send the "enter key pressed" command (the actual key and mouse command codes are explained in the Parallels Python API Reference guide and some examples are provided below).
  5. Wait a while for the next screen on the virtual machine desktop to open and repeat the capture and user interaction procedure the same exact way as explained in the step above.
  6. Repeat until all saved screens are found and processed. Your operating system is installed.

Bit-by-bit comparison notes

In theory, the screen comparison procedure described above (comparing two full screens) should work. In reality, it is virtually impossible to achieve a state when an individual screen remains absolutely static. Such things as blinking cursor, messages from the program vendor, and other animations will make each capture different from another. Therefore, instead of comparing an entire screen, the following approach can be used:

  1. In the Preparation step described above, capture an entire virtual machine desktop screen and then select a rectangular region on it that is guaranteed to be absolutely static and is unique enough to be used to identify the screen. This can be the screen name (such as "Step 1" or similar), a unique static picture or text displayed on it, and so forth. Copy the region from the image using an image editor of your choice and save it to a file.
  2. When determining the identity of a screen captured at runtime, start at the beginning of the full screen image and see if a screen region that you saved earlier matches the region on the screen at that position. Since you know that the region never changes, you can safely use a bit-bit-comparison. If the two regions don't match, move one pixel forward and compare again. Repeat until the match is found or until the end of file is reached.

Example

The following sample program illustrates the implementation of the steps above. Please note that this is not a complete working program. The program does not include the implementation of the algorithm described in the Bit-by-bit comparison subsection (above). An implementation of the algorithm depends on the image format used and in any case should be simple and straightforward. Some of the steps in the program are simplified, specifically the keys array contains only three keys that are used to demonstrate how to send the key commands to the virtual machine. The main steps concerning the API usage are included and can be used as a starting point for your own implementation.

import sys

import prlsdkapi

  

consts = prlsdkapi.prlsdk.consts

  

if len(sys.argv) != 3:

    print "Usage: install_os <VM_name> <path_to_iso>"

    exit()

  

# Initialize the Parallels API library.

prlsdkapi.init_server_sdk()

  

# Create a server object.

server = prlsdkapi.Server()

  

# Log in.

try:

    "10.30.18.99", "root", "qawsed", consts.PSL_NORMAL_SECURITY

    result = server.login("10.30.18.99", "root", "qawsed", '', 0, 0, consts.PSL_HIGH_SECURITY).wait()

except prlsdkapi.prlsdk.PrlSDKError, e:

    print "Login error: %s" % e

    exit()

  

# Get a list of virtual machines.

# Find the specified virtual machine and

# obtain an object identifying it.

try:

    result = server.get_vm_list().wait()

except prlsdkapi.PrlSDKError, e:

    print "Error: %s" % e

    exit()

    

found = False

for i in range(result.get_params_count()):

    VM = result.get_param_by_index(i)

    if VM.get_name() == sys.argv[1]:

        found = True

        break

  

if found == False:

    print "Specified virtual machine not found."

    exit()

  

# Obtain an object identifying the

# CD/DVD drive

cdrom = VM.get_optical_disk(0)

  

# Begin the virtual machine editing operation.

VM.begin_edit()

  

# Mount the OS installation ISO image.

cdrom.set_emulated_type(consts.PDT_USE_IMAGE_FILE)

cdrom.set_sys_name(sys.argv[2])

cdrom.set_image_path(sys.argv[2])

  

# Commit the changes to the virtual machine.

VM.commit()

  

# Start the virtual machine.

VM.start().wait()

  

# Instantiate the prlsdkapi.VmIO class.

vm_io = prlsdkapi.VmIO()

  

# Begin a Remote Desktop Access session.

try:

    vm_io.connect_to_vm(VM).wait()

except prlsdkapi.PrlSDKError, e:

    print "Error: %s" % e

    exit()  

  

# Define the name of the file to save the

# captured screen data to.

current_screen = "current.bmp"

  

# Set the reference screenshot count to 0.

ref_count = 0

  

# Define the names of files containing

# reference screenshots.

ref_files = ['1.bmp', '2.bmp', '3.bmp']

  

# Define the keyboard keys that will be used

# to interact with the remote desktop.

keys = ['enter', 'f1', 'enter']

  

# Capture the virtual machine screen, find the matching

# screen in the list of reference files and send the

# appropriate keyboard command to the virtual machine.

# Repeat for all screens.

while True:

    # Get a reference file name.

    ref_screen = ref_files[ref_count]

  

    # Capture the current virtual machine desktop screen and save it

    # into a file as a BMP image.

    # The parameters are:

    #   Target file name

    #   X coordinate

    #   Y coordinate

    #   Width (-1 for full screen)

    #   Height (-1 for full screen)

    #   Image format

    vm_io.sync_capture_screen_region_to_file(VM, current_screen, \

                                             consts.PIF_BMP)

  

    # Do a bit-by-bit comparison of the captured screen and

    # the reference screen for this iteration.

    # The actual comparison procedure depends on the data format used.

    # The bb_cmp() function DOES NOT exist in this sample program.

    # You will have to implement it yourself.

    if bb_cmp(current_screen, ref_screen):

        print "%d screen valid" % ref_count

  

        # Press the appropriate key.

        press = consts.PKE_PRESS

        release = consts.PKE_RELEASE

        key = keys[ref_count]

        key = key.upper()

  

        # Determine the key scan code based on its name.

        # The codes are defined in the ScanCodesList constant.

        # For the complete list of codes, start Python from the command line,

        # import the prlsdkapi module, and issue the

        # "print prlsdkapi.prlsdk.consts.ScanCodesList" statement.

        scan_code =  consts.ScanCodesList[key]

  

        # Send the key command to the virtual machine.

        vm_io.send_key_event(VM, scan_code, press)

        vm_io.send_key_event(VM, scan_code, release)

  

        # If still have reference files to process, continue, otherwise, exit.

        if ref_count < (len(ref_files) - 1):

            ref_count = ref_count + 1

        else:

            print "Os is installed."

            break

  

# End the Remote Desktop Access session.

vm_io.disconnect_from_vm(VM)

  

# Stop the virtual machine.

VM.stop().wait()

  

# Logoff and deinitialize the library.

server.logoff()

prlsdkapi.deinit_sdk()