Parallels Virtualization SDK Programmer's Guide
Parallels Virtualization SDK Programmer's Guide
Parallels Virtualization SDK Programmer's Guide
Programmer's Guide
v18
Parallels International GmbH
Vordergasse 59
8200 Schaffhausen
Switzerland
Tel: + 41 52 672 20 30
www.parallels.com
© 2022 Parallels International GmbH. All rights reserved. Parallels and the Parallels logo are trademarks or registered
trademarks of Parallels International GmbH in Canada, the U.S., and/or elsewhere.
Apple, Safari, iPad, iPhone, Mac, macOS, iPadOS are trademarks of Apple Inc. Google, Chrome, Chrome OS, and
Chromebook are trademarks of Google LLC.
All other company, product and service names, logos, brands and any registered or unregistered trademarks mentioned
are used for identification purposes only and remain the exclusive property of their respective owners. Use of any brands,
names, logos or any other information, imagery or materials pertaining to a third party does not imply endorsement. We
disclaim any proprietary interest in such third-party information, imagery, materials, marks and names of others. For all
notices and information about patents please visit https://www.parallels.com/about/legal/
Contents
Getting Started .......................................................................................................... 6
Overview .........................................................................................................................6
System Requirements ..................................................................................................... 6
Parallels C API Concepts .......................................................................................... 8
Compiling Client Applications .......................................................................................... 8
Compiling with SdkWrap ..........................................................................................................9
Compiling with Framework .................................................................................................... 14
Handles ........................................................................................................................ 18
Synchronous Functions ................................................................................................. 20
Asynchronous Functions ............................................................................................... 20
Strings as Return Values ............................................................................................... 25
Error Handling ............................................................................................................... 26
Parallels C API by Example ..................................................................................... 29
Obtaining Server Handle and Logging In ........................................................................ 30
Host Operations ............................................................................................................ 34
Retrieving Host Configuration Information .............................................................................. 34
Managing Parallels Service Preferences ................................................................................ 36
Searching for Parallels Servers .............................................................................................. 40
Managing Parallels Service Users .......................................................................................... 42
Managing Files In The Host OS ............................................................................................. 47
Managing Licenses................................................................................................................ 50
Obtaining a Problem Report .................................................................................................. 52
Virtual Machine Operations ............................................................................................ 54
Obtaining the Virtual Machines List ........................................................................................ 54
Searching for Virtual Machine by Name ................................................................................. 56
Obtaining Virtual Machine Configuration Information .............................................................. 58
Determining Virtual Machine State ......................................................................................... 60
Starting, Stopping, Resetting a Virtual Machine ..................................................................... 62
Suspending and Pausing a Virtual Machine ........................................................................... 63
Creating a New Virtual Machine ............................................................................................. 65
Searching for Virtual Machines .............................................................................................. 68
Contents
Getting Started
In This Chapter
Overview ................................................................................................................ 6
System Requirements ............................................................................................ 6
Overview
Parallels® Virtualization SDK is a development kit that can be used to create and integrate custom
software solutions with Parallels Desktop for Mac.
System Requirements
To develop applications on Mac computers using the Parallels Virtualization SDK, the following
requirements must be met.
Hardware
• Mac computer with Apple M1 chip, Intel Core 2 Duo, Core i3, Core i5, Core i7, Core i9, Intel
Core M or Xeon processor.
• At least 4GB of RAM (16GB recommended).
macOS
• macOS Monterey 12 (when released).
• macOS Big Sur 11.1 or later.
Getting Started
Python
• Python 2.7 or 3.0 to develop applications in Python.
7
CHAPTER 2
This chapter describes the basics of the Parallels C API. It contains information on how to compile
client applications that use the API and explains basic API concepts.
In This Chapter
Compiling Client Applications.................................................................................. 8
Handles ................................................................................................................. 18
Synchronous Functions .......................................................................................... 20
Asynchronous Functions ........................................................................................ 20
Strings as Return Values......................................................................................... 25
Error Handling ........................................................................................................ 26
/Library/Frameworks/ParallelsVirtualizationSDK.framework
You can use the framework just like any other Apple framework when creating development
projects and compiling applications. Alternately, you can compile and build your applications
without using the framework. In such a case, you will have to specify all the necessary paths to the
SDK source files manually.
When using the framework, the dynamic library, which is supplied with the SDK, will be directly
linked to the application. If you would like to load the dynamic library at runtime, the Parallels
Virtualization SDK includes a convenient dlopen wrapper for this purpose called SdkWrap. Using
the wrapper, you can load and unload the library symbols at any time with one simple call. Please
note that in order to use SdkWrap, you must compile your application without using the framework.
The wrapper source files are located in the Helpers/SdkWrap directory, which is located in the
main SDK installation directory.
The following subsections describe various compilation scenarios in detail and provide code
samples.
Parallels C API Concepts
• The #include "SdkWrap.h" directive. This header file defines the wrapper functions.
• The #define SDK_LIB_NAME "libprl_sdk.dylib" directive. This is the name of the
dynamic library included in the SDK.
• The SdkWrap_Load(SDK_LIB_NAME) function call that will load the dynamic library symbols.
• The SdkWrap_Unload() function call that will unload the dynamic library when it is no longer
needed.
To compile a program, the following compiler options and instructions must be used:
Using Makefile
The following is a sample Makefile that demonstrates the implementation of the requirements
described above. To compile a program and to build an executable, type make in the Terminal
window. To clean up the project, type make clean. Please note that the SOURCE variable must
contain the name of your source file name.
# Source file name.
# Substitute the file name with your own.
SOURCE = HelloWorld
9
Parallels C API Concepts
all : $(TARGET)
$(TARGET) : $(OBJS)
$(CXX) -o $@ $(LDFLAGS) $(OBJS)
$(SOURCE).o : $(SOURCE).cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE).cpp
SdkWrap.o : $(SDK_PATH)/$(SDK_WRAP_PATH)/SdkWrap.cpp
$(CXX) -c -o $@ $(CXXFLAGS) $(SDK_PATH)/$(SDK_WRAP_PATH)/SdkWrap.cpp
clean:
@rm -f $(OBJS) $(TARGET)
If you are using the Xcode IDE, follow these steps to set up your project:
Example
The following is a complete sample program that demonstrates the usage of the SdkWrap
wrapper. The program loads the dynamic library, initializes the API, and then logs in to the local
Parallels Service. You can copy the entire program into a file on your Mac and try building and then
running it. The program uses a cross-platform approach, so it can also be compiled on Windows
and Linux machines.
#include "SdkWrap.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN_
#include <windows.h>
#else
#include <unistd.h>
#endif
/////////////////////////////////////////////////////////////////////
10
Parallels C API Concepts
// Log off.
err = LogOff(hServer);
exit(0);
}
if (PRL_FAILED(err))
{
11
Parallels C API Concepts
12
Parallels C API Concepts
SdkWrap_Unload();
return -1;
}
else
{
printf( "Login was successful.\n" );
}
return 0;
}
// Log off.
hJob = PrlSrv_Logoff(hServer);
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
13
Parallels C API Concepts
}
else
{
printf( "Logoff was successful.\n" );
}
return 0;
}
#include "ParallelsVirtualizationSDK/Parallels.h"
Parallels.h is the main SDK header file. Please note the framework name in front of the SDK
header file name. This is a common requirement when using a framework.
Note: The difference between the SdkWrap scenario (described in the previous subsection) and the
framework scenario is that Parallels.h must be included when using the framework, while
SdkWrap.h must be included when using SdkWrap. The two files must never be included together.
Please also note that you don't have to load the dynamic library manually in your program when using the
framework.
The only compiler option that must be specified when using the framework is:
-framework ParallelsVirtualizationSDK
Using Makefile
The following sample Makefile can be used to compile a program using the
ParallelsVirtualizationSDK framework:
# Source file name.
# Substitute the file name with your own.
SOURCE = HelloWorld
CXX = g++
LDFLAGS = -framework ParallelsVirtualizationSDK
all : $(TARGET)
14
Parallels C API Concepts
$(TARGET) : $(OBJS)
$(CXX) -o $@ $(LDFLAGS) $(OBJS)
$(SOURCE).o : $(SOURCE).cpp
$(CXX) -c -o $@ $(SOURCE).cpp
clean:
@rm -f $(OBJS) $(TARGET)
When setting up an Xcode project, the only thing that you have to do is add the
ParallelsVirtualizationSDK framework to the project. No other project modifications are necessary.
Sample
The following is a complete sample program that demonstrates the usage of the
ParallelsVirtualizationSDK framework.
#include "ParallelsVirtualizationSDK/Parallels.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN_
#include <windows.h>
#else
#include <unistd.h>
#endif
/////////////////////////////////////////////////////////////////////
// Log in.
err = LoginLocal(hServer);
// Log off
err = LogOff(hServer);
exit(0);
}
15
Parallels C API Concepts
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n",
prl_result_to_string(err));
PrlApi_Deinit();
return -1;
}
16
Parallels C API Concepts
{
fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
return 0;
}
// Log off.
hJob = PrlSrv_Logoff(hServer);
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
17
Parallels C API Concepts
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
return 0;
}
Handles
The Parallels C API is a set of functions that operate on objects. Objects are not accessed directly.
Instead, references to these objects are used. These references are known as handles.
Handle Types
PRL_HANDLE is the only handle type used in the C API. It is a pointer to an integer and it is defined
in PrlTypes.h.
PRL_HANDLE can reference any type of object within the API. The type of object that
PRL_HANDLE references determines the PRL_HANDLE type. A list of handle types can be found
in the PRL_HANDLE_TYPE enumeration in PrlEnums.h.
18
Parallels C API Concepts
Obtaining a Handle
A handle is usually obtained by calling a function belonging to another handle, which we may call a
"parent". For example, a virtual machine handle is obtained by calling a function that operates on
the Server handle. A virtual device handle is obtained by calling a function that operates on the
virtual machine handle, and so forth. The Parallels C API Reference guide contains a description
of every available handle and explains how each particular handle type can be obtained. The
examples in this guide also demonstrate how to obtain handles of different types.
Freeing a Handle
Parallels API handles are reference counted. Each handle contains a count of the number of
references to it held by other objects. A handle stays in memory for as long as the reference count
is greater than zero. A program is responsible for freeing any handles that are no longer needed. A
handle can be freed using the PrlHandle_Free function. The function decreases the reference
count by one. When the count reaches zero, the object is destroyed. Failing to free a handle after it
has been used will result in a memory leak.
Multithreading
Parallels API handles are thread safe. They can be used in multiple threads at the same time. To
maintain the proper reference counting, the count should be increased each time a handle is
passed to another thread by calling the PrlHandle_AddRef function. If this is not done, freeing a
handle in one thread may destroy it while other threads are still using it.
Example
The following code snippet demonstrates how to obtain a handle, how to determine its type, and
how to free it when it's no longer needed. The code is a part of the bigger example that
demonstrates how to log in to a Parallels Service (the full example is provided later in this guide).
PRL_HANDLE hServer = PRL_INVALID_HANDLE;
PRL_RESULT ret;
ret = PrlSrv_Create(&hServer);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlSvr_Create failed, error: %s",
prl_result_to_string(ret));
return PRL_ERR_FAILURE;
}
19
Parallels C API Concepts
Synchronous Functions
The Parallels C API provides synchronous and asynchronous functions. Synchronous functions run
in the same thread as the caller. When a synchronous function is called it completes executing
before returning control to the caller. Synchronous functions return PRL_RESULT, which is a
integer indicating success or failure of the operation. Consider the PrlSrv_Create function. The
purpose of this function is to obtain a handle of type PHT_SERVER. The handle is required to
access most of the functionality within the Parallels C API. The syntax of PrlSrv_Create is as
follows:
PRL_RESULT PrlSrv_Create(
PRL_HANDLE_PTR handle
);
Asynchronous Functions
An asynchronous operation is executed in its own thread. An asynchronous function that started
the operation returns to the caller immediately without waiting for the operation to complete. The
results of the operation can be verified later when needed. Asynchronous functions return
PRL_HANDLE, which is a pointer to an integer and is a handle of type PHT_JOB. The handle is
used as a reference to the asynchronous job executed in the background. The general procedure
for calling an asynchronous function is as follows:
20
Parallels C API Concepts
Asynchronous functions return data to the caller by means of an event handler (or callback
function). The callback function could be called at any time, depending on how long the
asynchronous function takes to complete. The callback function must have a specific signature.
The prototype can be found in PrlApi.h and is as follows:
typedef PRL_METHOD_PTR(PRL_EVENT_HANDLER_PTR) (
PRL_HANDLE hEvent,
PRL_VOID_PTR data
);
A handle received by the callback function can be of type PHT_EVENT or PHT_JOB. The type can
be determined using the PrlHandle_GetType function. The PHT_EVENT type indicates that the
callback was called by a system event. If the type is PHT_JOB then the callback was called by an
asynchronous job started by the program.
1 Get the job type using PrlJob_GetType. A job type can be used to identify the function that
started the job and to determine the type of the result it contains. For example, a job of type
PJOC_SRV_GET_VM_LIST is started by PrlSrv_GetVmList function call, which returns a
list of virtual machines.
2 Examine the job type. If it is relevant, proceed to the next step.
3 Get the job return code using PrlJob_GetRetCode. If it doesn't contain an error, proceed to
the next step.
4 Get the result (a handle of type PHT_RESULT) from the job handle using
PrlJob_GetResult.
21
Parallels C API Concepts
5 Get a handle to the result using PrlResult_GetParam. Note that some functions return a list
(ie. there can be more than a single parameter in the result). For example,
PrlSrv_GetVmList returns a list of available virtual machines. In such cases, use
PrlResult_GetParamCount and PrlResult_GetParamByIndex.
6 Implement code to use the handle obtained in step 5.
Note: You must always free the handle that was passed to the callback function before exiting,
regardless of whether you actually used it or not. Failure to do so will result in a memory leak.
The following skeleton code demonstrates implementation of the above steps. In this example, the
objective is to handle events of type PET_DSP_EVT_HOST_STATISTICS_UPDATED that are
generated by a call to function PrlSrv_SubscribeToHostStatistics, and to obtain the
result from a job of type PJOC_SRV_GET_VM_LIST.
static PRL_RESULT OurCallbackFunction(PRL_HANDLE hHandle, PRL_VOID_PTR pUserData)
{
PRL_JOB_OPERATION_CODE nJobType = PJOC_UNKNOWN; // job type
PRL_HANDLE_TYPE nHandleType = PHT_ERROR; // handle type
PRL_HANDLE hVm = PRL_INVALID_HANDLE; // virtual machine handle
PRL_HANDLE hParam = PRL_INVALID_HANDLE; // event parameter
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result
PRL_UINT32 nParamsCount = -1; // parameter count
PRL_UINT32 nParamIndex = -1; // parameter index
PRL_RESULT err = PRL_ERR_UNINITIALIZED; // error
PrlHandle_Free(hServerStatistics);
PrlHandle_Free(hEventParameters);
}
}
else if (nHandleType == PHT_JOB) // Job handle
{
// Get the job type.
PrlJob_GetOpCode(hHandle, &nJobType);
if (nJobType == PJOC_SRV_GET_VM_LIST)
{
// Check the job return code.
PRL_RESULT nJobRetCode;
PrlJob_GetRetCode(hHandle, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "[B]%.8X: %s\n", nJobRetCode,
prl_result_to_string(nJobRetCode));
PrlHandle_Free(hHandle);
return nJobRetCode;
}
PrlHandle_Free(hHandle);
return PRL_ERR_SUCCESS;
}
Note: When an event handler is registered, it will receive all of the events/jobs regardless of their origin. It
is the responsibility of the program to identify the type of the event and to handle each one accordingly.
// Register an event handler.
ReturnDataClass rd; // some user-defined class.
PrlSrv_RegEventHandler(hServer, OurCallbackFunction, &rd);
hJob = PrlSrv_GetVmList(hServer);
PrlHandle_Free(hJob);
23
Parallels C API Concepts
24
Parallels C API Concepts
1 Calling the same function twice: first, to obtain the required buffer size, and second, to receive
the actual string value. To get the required buffer size, call the function passing a null pointer as
a value of the output parameter, and pass 0 (zero) as a value of the variable that is used to
specify the buffer size. The function will calculate the required size and will populate the variable
with the correct value, which you can use to initialize a variable that will receive the string. You
can then call the function again to get the actual string value.
2 It is also possible to use a static buffer. If the length of the buffer is large enough, you will simply
receive the result. If the length is too small, a function will fail with the
PRL_ERR_BUFFER_OVERRUN error but it will populate the "buffer_size" variable with the
required size value. You can then allocate the memory using the received value and call the
function again to get the results.
Consider the following function:
PRL_RESULT PrlVmCfg_GetName(
PRL_HANDLE hVmCfg,
PRL_STR sVmName,
PRL_UINT32_PTR pnVmNameBufLength
);
The PrlVmCfg_GetName function above is a typical Parallels API function that returns a string
value (in this case, the name of a virtual machine). The hVmCfg parameter is a handle to an object
containing the virtual machine configuration information. The sVmName parameter is a char
pointer. It is used as output that receives the virtual machine name. The variable must be initialized
on the program side with enough memory allocated for the expected string. The size of the buffer
must be specified using the pnVmNameBufLength variable.
The following example demonstrates how to call the function using the first approach:
PRL_RESULT ret;
PRL_UINT32 nBufSize = 0;
25
Parallels C API Concepts
The following example uses the second approach. To test the buffer-overrun scenario, set the
sVmName array size to some small number.
#define MY_STR_BUF_SIZE 1024
PRL_RESULT ret;
char sVmName[MY_STR_BUF_SIZE];
PRL_UINT32 nBufSize = MY_STR_BUF_SIZE;
PRL_UINT32 nSize = 0;
PRL_STR pBuf;
Error Handling
Synchronous Functions
All synchronous Parallels C API functions return PRL_RESULT, which is an integer indicating
success or failure of the operation.
26
Parallels C API Concepts
All asynchronous functions return PRL_HANDLE. The error code (return value) in this case can be
extracted with PrlJob_GetRetCode after the asynchronous job has finished.
Parallels C API provides the following macros to work with error codes:
PRL_FAILED Returns True if the return value indicates failure, or False if the
return value indicates success.
PRL_SUCCEEDED Returns True if the return value indicates success, or False if
the return value indicates failure.
prl_result_to_string Returns a string representation of the error code.
The following code snippet attempts to create a directory on the host and analyzes the return value
(error code) of asynchronous function PrlSrv_CreateDir.
// Attempt to create directory /tmp/TestDir on the host.
char *szRemoteDir = "/tmp/TestDir";
hJob = PrlSrv_FsCreateDir(hServer, szRemoteDir);
PrlHandle_Free( hJob );
printf( "Remote directory %s was successfully created.\n", szRemoteDir );
27
Parallels C API Concepts
Descriptive error messages can sometimes be obtained using the PrlJob_GetError function.
This function will return a handle to an object of type PHT_EVENT. In cases where
PrlJob_GetError is unable to return error information, PrlApi_GetResultDescription
can be used. Although it is possible to avoid using PrlJob_GetError and use
PrlJob_GetResultDescription instead, it is recommended to first use PrlJob_GetError,
and if this doesn't return additional descriptive error information then use
PrlApi_GetResultDescription. The reason is that sometimes errors contain dynamic
parameters. The following example demonstrates how to obtain descriptive error information:
PrlJob_GetRetCode(hJob, &nJobResult);
PRL_CHAR szErrBuff[1024];
PRL_UINT32 nErrBuffSize = sizeof(szErrBuff);
PRL_HANDLE hError = PRL_INVALID_HANDLE;
PRL_RESULT ret = PrlJob_GetError(hJob, &hError);
28
CHAPTER 3
This chapter contains examples of using the Parallels C API. The examples include tasks such as
performing general Parallels Desktop tasks, creating and managing virtual machines, handling
event, collecting performance data, and others.
In This Chapter
Obtaining Server Handle and Logging In ................................................................. 30
Host Operations ..................................................................................................... 34
Virtual Machine Operations ..................................................................................... 54
Events.................................................................................................................... 101
Performance Statistics............................................................................................ 111
Encryption Plug-in .................................................................................................. 120
CHAPTER 4
1 Load the Parallels Virtualization SDK library using the SdkWrap_Load function.
2 Initialize the API using the PrlApi_InitEx function. The API must be initialized properly for
the given Parallels product, such as Parallels Server, Parallels Desktop. The initialization mode is
determined by the value of the nAppMode parameter passed to the PrlApi_InitEx
function. The value must be one of the enumerators from the PRL_APPLICATION_MODE
enumeration.
3 Create a server handle using the PrlSrv_Create function.
4 Call PrlSrv_LoginLocal or PrlSrv_Login to login to the Parallels Virtualization Service
(or simply Parallels Service). Parallels Service is a combination of processes running on the host
machine that comprise the Parallels virtualization product. The first function is used when the
program and the target Parallels Service are running on the same host. The second function
(PrlSrv_Login) is used to log in to a remote Parallels Service. Please note that remote login
is supported in Parallels Server-based virtualization products only.
If the above steps are executed without errors, the handle created in step 3 will reference a Server
object (a handle of type PHT_SERVER) identifying the Parallels Service. A handle to a valid Server
object is required to access most of the functionality within the Parallels C API. The
PrlSrv_LoginLocal function (step 4) establishes a connection with a specified Parallels Service
and performs a login operation using the specified credentials. The function operates on the Server
object created in step 3. Upon successful login, the object can be used to perform other
operations.
To end the session with the Parallels Service, the following steps must be performed before exiting
the application:
Example
The following sample functions demonstrates how to perform the steps described above.
// Intializes the SDK library and
// logs in to the local Parallels Service.
// Obtains a handle of type PHT_SERVER identifying
// the Parallels Service.
Parallels C API by Example
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n",
prl_result_to_string(err));
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
31
Parallels C API by Example
return 0;
}
32
Parallels C API by Example
// Log off.
hJob = PrlSrv_Logoff(hServer);
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
return 0;
}
33
CHAPTER 5
Host Operations
This chapter describes the common tasks that can be performed on the host.
In This Chapter
Retrieving Host Configuration Information ............................................................... 34
Managing Parallels Service Preferences .................................................................. 36
Searching for Parallels Servers ................................................................................ 40
Managing Parallels Service Users............................................................................ 42
Managing Files In The Host OS ............................................................................... 47
Managing Licenses................................................................................................. 50
Obtaining a Problem Report ................................................................................... 52
To retrieve this information, first obtain a handle of type PHT_SERVER_CONFIG and then use its
functions to get information about a particular resource. The following sample function
demonstrates how it is accomplished. The function accepts the hServer parameter which is a
server handle. For the example on how to obtain a server handle, see Obtaining Server Handle
and Logging In (p. 30).
PRL_RESULT GetHostConfig(PRL_HANDLE hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hHostConfig = PRL_INVALID_HANDLE;
hJob = PrlSrv_GetSrvConfig(hServer);
35
Parallels C API by Example
PrlHandle_Free(hHostConfig);
return 0;
}
36
Parallels C API by Example
Parallels Service preferences are managed using the PHT_DISP_CONFIG handle which is obtained
using the PrlSrv_GetCommonPrefs function. For the complete list of functions provided by the
PHT_DISP_CONFIG object, see the Parallels C API Reference guide.
The following sample function demonstrates how to obtain a handle of type PHT_DISP_CONFIG
and how to use its functions to retrieve and modify some of the Parallels Service preferences. The
function accepts the hServer parameter which is a server handle. For the example on how to
obtain a server handle, see Obtaining Server Handle and Logging In (p. 30).
PRL_RESULT GetSetServicePrefs(PRL_HANDLE hServer)
{
// Variables for handles.
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hDispConfig = PRL_INVALID_HANDLE;
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
if (PRL_FAILED(ret))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hDispConfig);
return -1;
}
38
Parallels C API by Example
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
PrlHandle_Free(hDispConfig);
return -1;
}
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hDispConfig);
return -1;
}
PrlHandle_Free(hJob);
39
Parallels C API by Example
PrlHandle_Free(hDispConfig);
return 0;
}
If you have multiple Parallels Servers running on your network and don't know their exact locations
and/or connection parameters, you can search for them using the
PrlSrv_LookupParallelsServers function. The function returns the information as a list of
handles of type PHT_SERVER_INFO, each containing the information about an individual Parallels
Server. The information includes host name, port number, version of the OS that a host is running,
Parallels Server version number, and the global ID (UUID). This information can then be used to
establish a connection with the Parallels Server of interest (you will have to know the correct user
name and password in addition to the returned parameters).
The following sample functions demonstrate how to search local network for Parallels Servers. The
first sample function calls the PrlSrv_LookupParallelsServers function synchronously. The
second function takes an asynchronous approach.
PRL_RESULT SearchServersSynch()
{
// Variables for handles.
PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result
PRL_HANDLE hHostConfig = PRL_INVALID_HANDLE; // PHT_SERVER_CONFIG
40
Parallels C API by Example
hJob = PrlSrv_LookupParallelsServers(
1000, // timeout
NULL, // callback function (not used)
NULL // user object pointer (not used)
);
PRL_CHAR sBuf[1024];
PRL_UINT32 nBufSize = sizeof(sBuf);
if (PRL_SUCCEEDED(ret))
{
printf("Found Parallels Server: %s\n", sBuf);
41
Parallels C API by Example
}
else
{
fprintf(stderr, "Error: %s \n",
prl_result_to_string(ret));
}
PrlHandle_Free(hParam);
}
}
// Get the other server properties and process them here, if needed...
Parallels Service doesn't have its own user database. It performs user authentication against the
host operating system user database. However, it has a user registry where the user information
that relates to Parallels Service operations is kept. The information includes user UUID (Universally
Unique ID), user name, the name and path of the virtual machine directory for the user, and two
flags indicating if a user is allowed to modify server preferences and use management console
application. A new user record is created in the registry for every user as soon as he/she logs in to
a Parallels Service for the very first time.
There are two API handles that are used to obtain information about Parallels Service users and to
modify some of the user profile parameters. These handles are PHT_USER_INFO and
PHT_USER_PROFILE. Both handles are containers that contain information about a user. The
difference between the two is PHT_USER_PROFILE is used to obtain information about currently
logged in user while PHT_USER_INFO is used to obtain information about a specified user. There
are also some differences in the type of the information provided.
42
Parallels C API by Example
The information about the currently logged in user can be retrieved using functions of the
PHT_USER_PROFILE handle. The following sample demonstrates how to obtain the handle and
how to use its functions to retrieve user information. The sample also shows how to set up a
default virtual machine directory for the user. Parallels Service automatically assigns a default virtual
machine directory (the directory where new virtual machines are created) for every new user. If
needed, a user can specify a different directory for his/her virtual machines. At the time of this
writing, this is the only property of the Parallels Service user profile that can be modified. Every user
profile modification must begin with the PrlSrv_UserProfileBeginEdit function call and end
with the PrlSrv_UserProfileCommit call. These two functions are used to prevent collisions
with other clients trying to modify the same user profile at the same time.
PRL_RESULT UserProfileSample(const PRL_HANDLE &hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hUserProfile = PRL_INVALID_HANDLE;
43
Parallels C API by Example
44
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
return -1;
}
// Set the new virtual machine folder.
// The folder must already exist on the server.
ret = PrlUsrCfg_SetDefaultVmFolder(hUserProfile, "/Users/Shared/Parallels/JDoe");
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hUserProfile);
return -1;
}
// Finally, commit the changes to the server.
hJob = PrlSrv_UserProfileCommit(hServer, hUserProfile);
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Analyze the result of PrlSrv_UserProfileCommit.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
return -1;
}
PrlHandle_Free(hUserProfile);
}
The information about a particular Parallels Service user can be obtained using the functions of the
PHT_USER_INFO handle. The handle can be obtain using one of the following functions:
PrlSrv_GetUserInfo or PrlSrv_GetUserInfoList. The first function takes the user UUID
as an input parameter and returns a single handle of type PHT_USER_INFO containing the user
information. The second function returns information about all users that exist in the Parallels
Service user registry. The information is returned as a list of handles of type PHT_USER_INFO.
45
Parallels C API by Example
The following sample uses the PrlSrv_GetUserInfoList function to obtain information about
all users in the Parallels Service user registry. It then iterates through the returned list of
PHT_USER_INFO handles and retrieves information about individual users.
PRL_RESULT UserInfoSample(const PRL_HANDLE &hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hUserInfo = PRL_INVALID_HANDLE;
46
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
PrlHandle_Free(hUserInfo);
}
PrlHandle_Free(hJobResult);
}
The file management functionality can be accessed through the PHT_SERVER handle. The file
management functions are prefixed with "PrlSrv_Fs".
47
Parallels C API by Example
The directory listing is obtained using the PrlSrv_FsGetDirEntries function. The function
returns a handle of type PHT_REMOTE_FILESYSTEM_INFO containing the information about the
specified file system entry and its immediate child entries (if any). The child entries are returned as a
list of handles of type PHT_REMOTE_FILESYSTEM_ENTRY which is included in the
PHT_REMOTE_FILESYSTEM_INFO object. The sample function below demonstrates how to
obtain a listing for the specified directory. On initial call, the function obtains a list of child entries
(files and sub-directories) for the specified directory and is then called recursively for each file
system entry returned. On completion, the entire directory tree will be displayed on the screen.
// Obtains the entire directory tree in the host OS
// starting at the specified path.
// The "levels" parameter specifies how many levels should the
// function traverse down the directory tree.
PRL_RESULT GetHostDirList(PRL_HANDLE hServer, PRL_CONST_STR path, int levels)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hParentDirectory = PRL_INVALID_HANDLE;
PRL_HANDLE hChildElement = PRL_INVALID_HANDLE;
48
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
printf("%s\n", sBuf);
PrlHandle_Free(hChildElement);
49
Parallels C API by Example
}
PrlHandle_Free(hParentDirectory);
PrlHandle_Free(hJob);
return PRL_ERR_SUCCESS;
}
Managing Licenses
The Parallels license information can be retrieved using the PrlSrv_GetLicenseInfo function.
The function returns a handle of type PHT_LICENSE containing the license details. The handle
provides a set of functions to retrieve the details. To install or update a license, use the
PrlSrv_UpdateLicense function.
The following sample function demonstrates how to obtain license information and how to install a
new license.
PRL_RESULT UpdateLicenseSample(PRL_HANDLE hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hLicense = PRL_INVALID_HANDLE;
PRL_HANDLE hUpdateLicense = PRL_INVALID_HANDLE;
50
Parallels C API by Example
PrlHandle_Free(hLicense);
51
Parallels C API by Example
PrlHandle_Free(hUpdateLicense);
}
52
Parallels C API by Example
return -1;
}
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJobResult);
return ret;
}
printf("%s", sReportData);
PrlHandle_Free(hJobResult);
free(sReportData);
return 0;
}
53
CHAPTER 6
In This Chapter
Obtaining the Virtual Machines List ......................................................................... 54
Searching for Virtual Machine by Name ................................................................... 56
Obtaining Virtual Machine Configuration Information ................................................ 58
Determining Virtual Machine State........................................................................... 60
Starting, Stopping, Resetting a Virtual Machine ....................................................... 62
Suspending and Pausing a Virtual Machine ............................................................. 63
Creating a New Virtual Machine .............................................................................. 65
Searching for Virtual Machines ................................................................................ 68
Adding an Existing Virtual Machine.......................................................................... 72
Cloning a Virtual Machine ....................................................................................... 74
Deleting a Virtual Machine....................................................................................... 76
Modifying Virtual Machine Configuration .................................................................. 78
Managing User Access Rights ................................................................................ 90
Working with Virtual Machine Templates ................................................................. 93
The steps that must be performed to obtain the virtual machine list are:
1 Log in to the Parallels Service and obtain a handle of type PHT_SERVER. See Obtaining
Server Handle and Logging In (p. 30) for more info and code samples.
2 Call PrlSrv_GetVmList. This is an asynchronous function that returns a handle of type
PHT_JOB.
3 Call PrlJob_GetResults passing the PHT_JOB object obtained in step 2. This function
returns a handle of type PHT_RESULT containing the virtual machine list.
4 Free the job handle using PrlHandle_Free as it is no longer needed.
5 Call PrlResult_GetParamsCount to determine the number of virtual machines contained in
the PHT_RESULT object.
Parallels C API by Example
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "PrlSrv_GetVmList returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
55
Parallels C API by Example
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
printf("\nVirtual Machines:\n");
if (PRL_FAILED(ret))
{
printf("PrlVmCfg_GetName returned with error (%s)\n",
prl_result_to_string(ret));
}
else
{
printf(" (%d) %s\n\n", i+1, szVmNameReturned);
}
return PRL_ERR_SUCCESS;
}
56
Parallels C API by Example
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
57
Parallels C API by Example
return PRL_ERR_FAILURE;
}
// It's not the virtual machine being searched for, so free the handle to it.
PrlHandle_Free(hVm);
}
return PRL_ERR_NO_DATA;
}
if (PRL_FAILED(ret))
{
// Handle the error...
return ret;
}
58
Parallels C API by Example
free(szVmName);
char* sOsTypeName;
switch (nOsType)
{
case PVS_GUEST_TYPE_WINDOWS:
sOsTypeName = "Windows";
printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_WINDOWS);
break;
case PVS_GUEST_TYPE_LINUX:
printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_LINUX);
break;
case PVS_GUEST_TYPE_MACOS:
printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_MACOS);
break;
case PVS_GUEST_TYPE_FREEBSD:
printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_FREEBSD);
break;
default:
printf("OS Type: %s: %d\n", "Other OS Type: ", nOsType);
}
return PRL_ERR_SUCCESS;
}
59
Parallels C API by Example
The following example demonstrates how obtain state/transition information for the specified virtual
machine.
PRL_RESULT GetVMstate(PRL_HANDLE hVm)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hVmInfo = PRL_INVALID_HANDLE;
60
Parallels C API by Example
61
Parallels C API by Example
PrlHandle_Free(hVmInfo);
return -1;
}
printf("Status: ");
switch (vm_state) {
case VMS_UNKNOWN:
printf("Unknown state\n");
break;
case VMS_STOPPED:
printf("Stopped\n");
break;
case VMS_STARTING:
printf("Starting...\n");
break;
case VMS_RESTORING:
printf("Restoring...\n");
break;
case VMS_RUNNING:
printf("Running\n");
break;
case VMS_PAUSED:
printf("Paused\n");
break;
case VMS_SUSPENDING:
printf("Suspending...\n");
break;
case VMS_STOPPING:
printf("Stopping...\n");
break;
case VMS_COMPACTING:
printf("Compacting...\n");
break;
case VMS_SUSPENDED:
printf("Suspended\n");
break;
default:
printf("Unknown state\n");
}
printf("\n");
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hVmInfo);
return 0;
}
Stopping a virtual machine is not the same as performing a guest operating system shutdown operation.
When a virtual machine is stopped, it is a cold stop (i.e. it is the same as turning off the power to a
physical computer). Any unsaved data will be lost. However, if the OS in the virtual machine supports
ACPI (Advanced Configuration and Power Interface) then you can set the second parameter of the
PrlVm_Stop function to PRL_FALSE in which case, the ACPI will be used and the machine will be
properly shut down.
62
Parallels C API by Example
Resetting a virtual machine is not the same as performing a guest operating system restart operation. It
is the same as pressing the "Reset" button on a physical box. Any unsaved data will be lost.
The following sample function demonstrates how start, stop, and reset a virtual machine.
PRL_RESULT StartStopResetVm(PRL_HANDLE hVm, VIRTUAL_MACHINE_STATE action)
{
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
if (action == VMS_RUNNING)
{
// Start the virtual machine.
hJob = PrlVm_Start(hVm);
printf("Starting the virtual machine... \n");
}
else if (action == VMS_STOPPED)
{
// Stop the virtual machine.
hJob = PrlVm_Stop(hVm, PRL_TRUE);
printf("Stopping the virtual machine... \n");
}
else if (action == VMS_RESETTING)
{
// Reset the virtual machine.
hJob = PrlVm_Reset(hVm);
printf("Resetting the virtual machine... \n");
}
else
{
printf ("Invalid action type specified \n");
return PRL_ERR_FAILURE;
}
PrlJob_Wait(hJob, 10000);
PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(nJobReturnCode))
{
printf ("Error: %s\n", prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
return PRL_ERR_FAILURE;
}
return PRL_ERR_SUCCESS;
}
When a virtual machine is suspended, the information about its state is stored in non-volatile
memory. A suspended virtual machine can resume operating in the same state it was in at the point
it was placed into a suspended state. Resuming a virtual machine from a suspended state is
quicker than starting a virtual machine from a stopped state.
63
Parallels C API by Example
To suspend a virtual machine, obtain a handle to the virtual machine, then call PrlVm_Suspend.
The following example will suspend a virtual machine called Windows XP - 01.
const char *szVmName = "Windows XP - 01";
PRL_RESULT nJobResult;
PRL_HANDLE hJob = PrlVm_Suspend(hVm);
PRL_RESULT ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAIL(ret))
{
fprintf(stderr, "PrlJob_Wait for PrlVm_Suspend failed. Error: %s",
prl_result_to_string(ret));
PrlHandle_Free(hServer);
PrlHandle_Free(hJob);
PrlApi_Deinit();
SdkWrap_Unload();
exit(-1);
}
PrlJob_GetRetCode(hJob, &nJobResult);
if (PRL_FAILED(nJobResult))
{
fprintf(stderr, "PrlVm_Suspend failed with error: %s\n",
prl_result_to_string(nJobResult));
PrlHandle_Free(hVm);
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
A suspended virtual machine can be stopped completely (placed into a "stopped" state) using the
PrlVm_DropSuspendedState function.
Pausing a virtual machine will pause execution of the virtual machine. This can be achieved using
PrlVm_Pause. PrlVm_Pause takes two parameters: a handle to the virtual machine, and a
boolean value indicating if ACPI should be used. The above example could be modified to pause a
virtual machine by replacing the line:
PRL_HANDLE hJob = PrlVm_Suspend(hVm);
with:
PRL_HANDLE hJob = PrlVm_Pause(hVm, PRL_FALSE);
64
Parallels C API by Example
In this section, we will discuss how to create a typical virtual machine for a particular OS type using
a sample configuration. By using this approach, you can easily create a virtual machine without
knowing all of the little details about configuring a virtual machine for a particular operating system
type.
65
Parallels C API by Example
4 Choose a name for the new virtual machine and set it using the PrlVmCfg_SetName function.
5 Modify some of the default configuration parameters if needed. For example, you may want to
modify the hard disk image type and size, the amount of memory available to the machine, and
the networking options. You will have to obtain an appropriate handle for the type of the
parameter that you would like to modify and call one of its functions to perform the modification.
The code sample below shows how to modify some of the default values.
6 Create and register the new machine using the PrlVm_Reg function. This step will create the
necessary virtual machine files on the host and register the machine with the Parallels Service.
The directory containing the virtual machine files will have the same name as the virtual machine
name. The directory will be created in the default location for this Parallels Service. If you would
like to create the virtual machine directory in a different location, you may specify the desired
parent directory name and path.
The following sample demonstrates how to create a new virtual machine. The sample assumes that
the client program has already obtained a server object handle (hServer) and performed the login
operation.
PRL_HANDLE hVm = PRL_INVALID_HANDLE;
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
PRL_HANDLE hResult = PRL_INVALID_HANDLE;
PRL_RESULT nJobRetCode;
PRL_RESULT ret;
66
Parallels C API by Example
// we can set the default configuration for the new virtual machine.
ret = PrlVm_GetConfig(hVm, &hVmCfg);
ret = PrlVmCfg_SetDefaultConfig(
hVmCfg, // VM config handle.
hSrvCfg, // Host config data.
PVS_GUEST_VER_WIN_2003, // Target OS version.
PRL_TRUE); // Create and connect devices.
if (PRL_FAILED(ret))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(ret));
PrlHandle_Free(hSrvCfg);
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hVm);
return ret;
}
PrlHandle_Free(hSrvCfg);
// Delete handles.
PrlHandle_Free(hJob);
67
Parallels C API by Example
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hVm);
Since the search operation may take a long time (depending on the size of the specified directory
tree), the PrlSrv_StartSearchVms function should be executed using the callback functionality
(p. 20). The callback function will be called for every virtual machine found and a single instance of
the PHT_FOUND_VM_INFO handle will be passed to it. As we discussed earlier in this guide (p. 20),
a callback function can receive two types of objects: jobs (PHT_JOB) and events (PHT_EVENT). In
this instance, the information is passed to the callback function as an event of type
PET_DSP_EVT_FOUND_LOST_VM_CONFIG. To following steps are involved in processing the
event inside the callback function:
PrlHandle_GetType(hEvent, &nHandleType);
PrlHandle_Free(hEvent);
return 0;
}
// Check the event type. If it's what we are looking for, process it.
if (eventType == PET_DSP_EVT_FOUND_LOST_VM_CONFIG)
{
PRL_UINT32 nParamsCount = 0;
PrlHandle_Free(hFoundVmInfo);
PrlHandle_Free(hEvent);
return 0;
}
// The received event handler MUST be freed.
PrlHandle_Free(hEvent);
}
To begin the search operation, place the following code into your main program:
69
Parallels C API by Example
In order for the callback function to be called, your program should have a global loop (the program
never exits on its own). The callback function will be called as soon as the first virtual machine is
found. If there are no unregistered virtual machines in the specified directory tree, then the function
will never be called as a PET_DSP_EVT_FOUND_LOST_VM_CONFIG event (it will still be called at
least once as a result of the started job and will receive the job object but this and possibly other
callback invocations are irrelevant in the context of this example).
It is also possible to use this function synchronously using the PrlJob_Wait function (p. 20). In
this case, the information is returned as a list of PHT_FOUND_VM_INFO objects contained in the
job object returned by the PrlSrv_StartSearchVms function. The following example
demonstrates how to call the function and to process results synchronously.
PRL_RESULT SearchVMsSample(PRL_HANDLE hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hFoundVmInfo = PRL_INVALID_HANDLE;
PrlHandle_Free(hFoundVmInfo);
}
PrlHandle_Free(hJobResult);
PrlHandle_Free(hStringList);
}
71
Parallels C API by Example
Note: The PrlSrv_RegisterVm function does NOT generate new MAC addresses for the virtual
network adapters that already exist in the virtual machine. If the machine is a copy of another virtual
machine then you should set new MAC addresses for its network adapters after you register it. The
example at the end of this section demonstrates how this can be accomplished. For more information on
modifying an existing virtual machine, please see the Modifying Virtual Machine Configuration section
(p. 78).
The following sample function demonstrates how to add an existing virtual machine to the Parallels
Service. The function takes a handle of type PHT_SERVER and a string specifying the name and
path of the virtual machine directory. It registers the virtual machine with the Service and then
modifies the MAC address of every virtual network adapter installed in it.
PRL_RESULT RegisterExistingVM(PRL_HANDLE hServer, PRL_CONST_STR sVmDirectory)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
return ret;
}
PrlHandle_Free(hJob);
PrlHandle_Free(hJobResult);
// The following code will generate and set a new MAC address
// for every virtual network adapter that exists in the virtual machine.
// This step is optional and should normally be performed when the virtual
// machine is a copy of another virtual machine.
73
Parallels C API by Example
{
// Handle the error...
return ret;
}
PrlHandle_Free(hNetAdapter);
PrlHandle_Free(hVm);
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hJobCommit);
PrlHandle_Free(hJobBeginEdit);
return 0;
}
1 A valid handle of type PHT_VIRTUAL_MACHINE containing information about the source virtual
machine.
2 A unique name for the new virtual machine (the name is NOT generated automatically).
3 The name of the directory where the virtual machine files should be created (or an empty string
to create the files in the default directory).
4 A boolean value specifying whether to create the new machine as a valid virtual machine or as a
template. PRL_TRUE indicates to create a template. PRL_FALSE indicates to create a virtual
machine. See the Working with Virtual Machine Templates (p. 93) section for more virtual
machine info and examples.
74
Parallels C API by Example
The source virtual machine must be registered with the Parallels Service before it can be cloned.
The following sample function demonstrates how to clone an existing virtual machine. When testing
a function, the hVm parameter must contain a valid handle of type PHT_VIRTUAL_MACHINE (the
source virtual machine to clone). On completion, the new virtual machine should appear in the list
of registered virtual machines.
PRL_RESULT CloneVmSample(PRL_HANDLE hVm)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hVmCfg);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
printf("Error: (%s)\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hVmCfg);
return -1;
}
PrlHandle_Free(hJob);
PrlHandle_Free(hVmCfg);
return 0;
}
1 Un-register the virtual machine using PrlVm_Unreg. This will remove the virtual machine from
the list of the virtual machines registered with the Service. Once a virtual machine has been
unregistered it is not possible to use it. The directory containing the virtual machine files will
remain on the hard drive of the host computer, and the virtual machine can later be re-
registered and used.
2 Delete the virtual machine using PrlVm_Delete. The virtual machine will be unregistered, and
the directory (or just some of its files that you can specify) will be deleted.
The following example demonstrates un-registering a virtual machine. Note that this example
makes use of a function called GetVmByName that can be found in the Obtaining a List of Virtual
Machines section.
const char *szVmName = "Windows XP - 02";
prl_result_to_string(ret));
PrlHandle_Free(hVm);
PrlHandle_Free(hJob);
return -1;
}
PrlJob_GetRetCode(hJob, &nJobResult);
if (PRL_FAILED(nJobResult))
{
printf("PrlVm_Unreg failed. Error returned: %s\n",
prl_result_to_string(nJobResult));
PrlHandle_Free(hVm);
PrlHandle_Free(hJob);
return -1;
}
The following example demonstrates deleting a virtual machine and deleting config.pvs within
the virtual machine directory:
// Delete a virtual machine and a specified file.
PRL_HANDLE hDeviceList = PRL_INVALID_HANDLE;
PrlApi_CreateStringsList(&hDeviceList);
PrlStrList_AddItem(hDeviceList, "/Users/Shared/Parallels/WinXP02/config.pvs");
hJob = PrlVm_Delete(hVm, hDeviceList);
PrlHandle_Free(hDeviceList);
ret = PrlJob_Wait(hJob, 10000);
if (PRL_FAILED(ret))
{
printf("PrlJob_Wait failed for PrlVm_Unreg. Error returned: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hVm);
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
PrlJob_GetRetCode(hJob, &nJobResult);
if (PRL_FAILED(nJobResult))
{
printf("PrlVm_Delete failed. Error returned: %s\n",
prl_result_to_string(nJobResult));
PrlHandle_Free(hVm);
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
To delete the virtual machine and the virtual machine directory (all files belonging to the virtual
machine), omit the line:
PrlStrList_AddItem(hDeviceList, "/Users/Shared/Parallels/WinXP02/config.pvs");
77
Parallels C API by Example
Note: All operations on virtual machine devices (adding, modifying, removing) must be performed on a
stopped virtual machine. An attempt to modify the device configuration on a running machine will result in
error.
When you are done making the changes, you must call the PrlVm_Commit function. The first
thing that the function will do is verify that the virtual machine configuration has not been modified
by other client(s) since you called the PrlVm_BeginEdit function. If it has been, your changes
will be rejected and PrlVm_Commit will return with error. In such a case, you will have to reapply
your changes. In order to do that, you will have to get the latest configuration using the
PrlVm_GetConfig function, compare your changes with the latest changes, and make a
decision about merging them. Please note that PrlVm_GetConfig function will update the
configuration data in your current virtual machine object and will overwrite all existing data,
including the changes that you've made to it. Furthermore, the PrlVm_BeginEdit function will
also overwrite all existing data (see above). If you don't want to loose your data, save it locally
before calling PrlVm_GetConfig or PrlVm_BeginEdit.
The following example demonstrates how to use the PrlVm_BeginEdit and PrlVm_Commit
functions:
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE;
PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
78
Parallels C API by Example
Once you have the handle, you can use its functions to manipulate the virtual machine configuration
settings. As usual, don't forget to free the handle when it is no longer needed.
// Modify VM description.
ret = PrlVmCfg_SetDescription(hVmCfg, "My updated VM");
To modify the boot options (boot device priority), first make the PrlVmCfg_GetBootDevCount
call to determine the number of the available devices. Then obtain a handle to each device by
making the PrlVmCfg_GetBootDev call in a loop. To place a device at the specified position in
the boot device priority list, use the PrlBootDev_SetSequenceIndex function passing the
device handle and the index (0 - first boot device, 1 - second boot device, and so forth).
79
Parallels C API by Example
// Modify VM name.
ret = PrlVmCfg_SetName(hVmCfg, "New Name1");
// Modify VM description.
ret = PrlVmCfg_SetDescription(hVmCfg, "My updated VM");
// Device count.
PRL_UINT32 nDevCount;
// Device type.
PRL_DEVICE_TYPE devType;
if (devType == PDE_OPTICAL_DISK)
{
PrlBootDev_SetSequenceIndex(hDevice, 0);
}
if (devType == PDE_HARD_DISK)
{
PrlBootDev_SetSequenceIndex(hDevice, 1);
}
else if (devType == PDE_GENERIC_NETWORK_ADAPTER)
80
Parallels C API by Example
{
PrlBootDev_SetSequenceIndex(hDevice, 2);
}
else if (devType == PDE_FLOPPY_DISK)
{
PrlBootDev_SetSequenceIndex(hDevice, 3);
}
else
{
PrlBootDev_Remove(hDevice);
}
}
RAM Size
The size of the memory available to the virtual machine is performed using the
PrlVmCfg_SetRamSize function. The first parameter is the virtual machine handle and the
second parameter is the new RAM size in megabytes:
PrlVmCfg_SetRamSize(hVmCfg, 512);
Hard Disks
A virtual machine may have more than one virtual hard disk. To select a disk that you would like to
modify, first retrieve the list of the available disks, as shown in the following example:
PRL_HANDLE hHDD = PRL_INVALID_HANDLE;
PRL_UINT32 nCount;
81
Parallels C API by Example
In this example, we will add a hard disk to a virtual machine. The following options are available:
• You may create new or use an existing image file for your new disk.
• Creating a dynamically expanding or a fixed-size disk. The expanding drive image will be initially
created with a size of zero. The space for it will be allocated dynamically on as-needed basis.
The space for the fixed-size disk will be allocated fully at the time of creation.
• Choosing the maximum disk size.
Creating a new image file
In the first example, we will create a new disk image and will add it to a virtual machine.
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE;
PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
PRL_RESULT nJobRetCode = PRL_INVALID_HANDLE;
// Choose and set the name for the new image file.
// We must set both the "friendly" name and the "system" name.
// For a virtual device, use the name of the new image file in both
// functions. By default, the file will be
// created in the virtual machine directory. You may specify a
// full path if you want to place the file in a different
// directory.
//
82
Parallels C API by Example
In the next example, we will use an existing image file to add a virtual hard disk to a virtual machine.
The procedure is similar to the one described above, except that you don't have to specify the disk
parameters and you don't have to create an image file.
// Timestamp the beginning of the configuration changes operation.
// The hVm specifies the virtual machine that we'll be editing.
//
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
If the commit operation is successful, a hard disk will be added to the virtual machine and will
appear in the list of the available devices.
Network Adapters
When adding a network adapter to a virtual machine, you first have to choose a networking mode
for it. The following options are available:
• Host-only networking. A virtual machine can communicate with the host and other virtual
machines, but not with external networks.
• Shared networking. Uses the NAT feature. A virtual machine shares the IP address with the
host.
• Bridged networking. A virtual adapter in the VM is bound to a network adapter on the host. The
virtual machine appears as a standalone computer on the network.
The following sample function illustrates how to add virtual network adapters using the host-only
and shared networking (both types are created similarly). The steps are:
1 Call the PrlVm_BeginEdit function to mark the beginning of the virtual machine editing
operation. This step is required when modifying any of the virtual machine configuration
parameters.
2 Obtain a handle of type PHT_VM_CONFIGURATION containing the virtual machine
configuration information.
3 Create a new virtual device handle of type PHT_VIRTUAL_DEV_NET_ADAPTER (virtual
network adapter) using the PrlVmCfg_CreateVmDev function.
4 Set the desired device emulation type (host or shared) using the
PrlVmDev_SetEmulatedType function. Virtual network adapter emulation types are defined
in the PRL_VM_DEV_EMULATION_TYPE enumeration.
5 The MAC address for the adapter will be generated automatically. If needed, you can set the
address manually using the PrlVmDevNet_SetMacAddress function.
6 Call the PrlVm_Commit function to finalize the changes.
PRL_RESULT AddNetAdapterHostOrShared(PRL_HANDLE hVm)
{
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE;
PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
84
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error.
}
85
Parallels C API by Example
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
// Handle the error.
}
return PRL_ERR_SUCCESS;
}
Bridged Networking
Compared to host-only and shared network adapters, adding an adapter using bridged networking
involves additional steps. In a bridged networking mode, you are binding the virtual adapter inside a
virtual machine to an adapter on the host machine. Therefore, you first have to retrieve the list of
adapters from the host and select the one you would like to use. The complete procedure of
creating an adapter using bridged networking is as follows:
1 Obtain a list of network adapters installed on the host. This steps is preformed using the
PrlSrvCfg_GetNetAdaptersCount, PrlSrvCfg_GetNetAdapter, and
PrlSrvCfgDev_GetName functions.
2 Begin the virtual machine editing operation and create a new network adapter handle as
described in the Host-only and Shared Networking section (p. 84).
3 Bind the new virtual network adapter to the desired host machine adapter using the
PrlVmDevNet_SetBoundAdapterName function.
4 Finalize the changes by calling the PrlVm_Commit function.
You can also bind a virtual network adapter to the default adapter on the host machine. In this
case, you don't have to obtain the list of adapters from the host, so you can skip step 1 (above). In
step 3, instead of setting the adapter name, set its index as -1 using the
PrlVmDevNet_SetBoundAdapterIndex function.
The following are two sample functions that show the implementation of the steps described
above. The two functions are similar except that the first one shows how to bind a virtual network
adapter to a specific adapter on the host, whereas the second function shows how to bind the
virtual adapter to the default host network adapter.
Example 1:
The function accepts a server handle and a virtual machine handle. The server handle will be used
to obtain the list of network adapters from the host.
PRL_RESULT AddNetAdapterBridged(PRL_HANDLE hServer, PRL_HANDLE hVm)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE;
86
Parallels C API by Example
PrlJob_GetRetCode(hJob, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
// Handle the error.
}
// counter.
PRL_UINT32 nCount = PRL_INVALID_HANDLE;
87
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error.
}
88
Parallels C API by Example
return PRL_ERR_SUCCESS;
}
Example 2:
This function shows how to add a virtual network adapter to a virtual machine and how to bind it to
the default adapter on the host.
PRL_RESULT AddNetAdapterBridgedDefault(PRL_HANDLE hVm)
{
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE;
PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
PRL_RESULT nJobRetCode = PRL_INVALID_HANDLE;
PRL_UINT32 ret = PRL_ERR_UNINITIALIZED;
if (PRL_FAILED(ret))
{
// Handle the error.
}
89
Parallels C API by Example
{
// Handle the error.
}
return PRL_ERR_SUCCESS;
}
User authorization (determining user access rights) is performed using OS-level file access
permissions. Essentially, a virtual machine is a set of files that a user can read, write, and execute.
When determining access rights of a user for a particular virtual machine, Parallels Service looks at
the rights the user has on the virtual machine files and uses this information to allow or deny
privileges. The Parallels Server Administration Guide has a section that describes the Parallels
Server tasks in relation to the file access rights. Using this information, you can determine the tasks
that a user is allowed to perform based on the file access rights the user has. The same goal can
also be accomplished programmatically through Parallels C API.
The Parallels C API contains a PHT_ACCESS_RIGHTS object that is used to manage user access
rights. A handle to it is obtained using the PrlVmCfg_GetAccessRights or the
PrlVmInfo_GetAccessRights function. The difference between the two function is that
PrlVmInfo_GetAccessRights takes an additional step: obtaining a handle of type
PHT_VM_INFO which will also contain the virtual machine state information. If user access rights is
all you need, you can use the PrlVmCfg_GetAccessRights function.
90
Parallels C API by Example
The PHT_ACCESS_RIGHTS object provides an easy way of determining access rights for the
currently logged in user with the PrlAcl_IsAllowed function. The function allows to specify one
of the available virtual machine tasks (defined in the PRL_ALLOWED_VM_COMMAND enumeration)
and returns a boolean value indicating whether the user is allowed to perform the task or not. The
security is enforced on the server side, so if a user tries to perform a tasks that he/she is not
authorized to perform, the access will be denied. You can still use the functionality described here
to determine user access rights in advance and use it in accordance with your client application
logic.
An administrator of the host has full access rights to all virtual machines. A non-administrative user
has full rights to the machines created by him/her and no rights to any other virtual machines by
default (these machines will not even be included in the result set when the user requests a list of
virtual machines from the host). The host administrator can grant virtual machine access privileges
to other users when needed. Currently, the privileges can be granted to all existing users only. It is
not possible to set access rights for an individual user through the API. The
PrlAcl_SetAccessForOthers function is used to set access rights. The function takes the
PHT_ACCESS_RIGHTS object identifying the virtual machine and one of the enumerators from the
PRL_VM_ACCESS_FOR_OTHERS enumerations identifying the access level, which includes view,
view and run, full access, and no access. Once again, the function sets access rights for all existing
users (the users currently present in the Parallels Service user registry (p. 42)). To determine the
current access level for other users on a particular virtual machine, use the
PrlAcl_GetAccessForOthers function. For the complete set of user access management
functions, see the PHT_ACCESS_RIGHTS object description in the Parallels C API Reference
guide.
The following sample function demonstrates how to set virtual machine access rights and how to
determine access rights on the specified virtual machine for the currently logged in user. The
function accepts a virtual machine handle and operates on the referenced virtual machine.
PRL_RESULT AccessRightsSample(PRL_HANDLE hVm)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hAccessRights = PRL_INVALID_HANDLE;
PrlHandle_Free(hVmCfg);
91
Parallels C API by Example
92
Parallels C API by Example
PrlHandle_Free(hAccessRights);
return 0;
}
The following subsections describes each operation in detail and provide code examples.
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "PrlSrv_GetVmList returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
94
Parallels C API by Example
PrlHandle_Free(hVm);
PrlHandle_Free(hVmCfg);
}
return PRL_ERR_SUCCESS;
}
The following example illustrates how to create a virtual machine template. For simplicity reasons,
we only set a template name in this example. The rest of the configuration parameters are omitted.
As a result, a blank template will be created. It still can be used to create new virtual machines from
it but you will not be able to run them until you configure them properly. Once again, the Creating a
New Virtual Machine section (p. 65) provides all the necessary information and code samples
needed to properly configure a virtual machine or a template.
PRL_RESULT CreateTemplateFromScratch(PRL_HANDLE hServer)
{
PRL_HANDLE hVm = PRL_INVALID_HANDLE;
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
95
Parallels C API by Example
96
Parallels C API by Example
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hVm);
return -1;
}
PrlHandle_Free(hJob);
PrlHandle_Free(hVm);
return 0;
}
The following code example illustrates how to convert a regular virtual machine to a template. Note
that any of the virtual machine (or a template) configuration changes must begin with the
PrlVm_BeginEdit and end with the PrlVm_BeginCommit function call. You should already
know that these two functions are used to prevent collisions with other clients trying to modify the
configuration of the same virtual machine or template at the same time.
PRL_RESULT ConvertVMtoTemplate(PRL_HANDLE hVm)
{
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE;
PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
97
Parallels C API by Example
return 0;
}
98
Parallels C API by Example
99
Parallels C API by Example
PrlHandle_Free(hJob);
return -1;
}
100
CHAPTER 7
Events
In This Chapter
Receiving and Handling Events ............................................................................... 101
Responding to Parallels Service Questions .............................................................. 103
In order to receive an event notification, a client program needs an event handler. An event handler
(also called callback) is a function that you have to implement yourself. We've already discussed
event handlers and provided code samples in the Asynchronous Functions section (p. 20). If you
haven't read it yet, please do so now. To subscribe to event notifications, you must register your
event handler with the Service. This is accomplished using the PrlSrv_RegEventHandler
function. Once this is done, the event handler (callback) function will be called automatically by the
background thread every time it receives an event notification from the Service. The code inside the
event handler can then handle the event according to the application logic.
The following describes the general steps involved in handling an event in a callback function:
1 Determine if the notification received is an event (not a job, because event handlers are also
called when an asynchronous job begins). This can be accomplished using the
PrlHandle_GetType function (determines the type of the handle received) and then
checking if the handle is of type PHT_EVENT (not PHT_JOB).
2 Determine the type of the event using the PrlEvent_GetType function. Check the event type
against the PRL_EVENT_TYPE enumeration. If it is relevant, continue to the next step.
Parallels C API by Example
102
Parallels C API by Example
Handling of the event involves the following steps (we skip the general event handling steps
described in the previous section):
1 Obtaining a string containing the question. This is accomplished by making the
PrlEvent_GetErrString function call.
2 Obtaining the list of possible answers. Answers are included as event parameters,
therefore they are retrieved using PrlEvent_GetParamsCount and PrlEvent_GetParam
functions as described in the previous section.
3 Selecting an answer. Every available answer has its own unique code which is included in the
corresponding event parameter.
4 Sending a response containing the answer back to the Service. This is performed in two steps:
first, the PrlEvent_CreateAnswerEvent function is used to properly format the answer;
second, the answer is sent to the Service using the PrlSrv_SendAnswer function.
The following is a complete example that demonstrates how to handle events of type
PET_DSP_EVT_VM_QUESTION and how to answer Service questions. In the example, we create
a blank virtual machine and try to add a virtual hard drive to it with the size larger than the free disk
space available on the physical drive. This will trigger an event on the server side and a question will
be sent to the client asking if we really want to create a drive like that. The virtual machine creation
operation will not continue unless we send an answer to the Service. We then send an answer and
the operation continues normally.
#include "Parallels.h"
#include "Wrappers/SdkWrap/SdkWrap.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef _WIN_
#include <windows.h>
#else
#include <unistd.h>
#endif
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#elif defined(_LIN_)
#define SDK_LIB_NAME "libprl_sdk.so"
#elif defined(_MAC_)
#define SDK_LIB_NAME "libprl_sdk.dylib"
#endif
// Log in.
hJob = PrlSrv_Login(
hServer, // Server handle
"10.30.22.82", // Server IP address
"jdoe", // User
"secret", // Password
0, // Previous session ID
0, // Port number
0, // Timeout
PSL_NORMAL_SECURITY); // Security
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Login returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
105
Parallels C API by Example
//
// At this point, the background thread should call the
// callback function.
//
////////////////////////////////////////////////////////////////////////////////
// The callback function implementation.
// The event handling is demonstrated here.
//
static PRL_RESULT callback(PRL_HANDLE hEvent, PRL_VOID_PTR pUserData)
106
Parallels C API by Example
{
PRL_HANDLE_TYPE nHandleType;
PrlHandle_GetType(hEvent, &nHandleType);
107
Parallels C API by Example
PrlHandle_Free(hParam);
PrlHandle_Free(hEvent);
return err;
}
// Send the handle containing the answer data to the Parallels Service.
PrlSrv_SendAnswer(hServer, hAnswer);
free(choices);
PrlHandle_Free(hServer);
PrlHandle_Free(hAnswer);
}
else // other event type
{
PrlHandle_Free(hEvent);
}
108
Parallels C API by Example
return PRL_ERR_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////
// Choose and set the name for the new image file.
err = PrlVmDev_SetFriendlyName(hHDD, "harddisk4.hdd");
err = PrlVmDev_SetSysName(hHDD, "harddisk4.hdd");
109
Parallels C API by Example
110
CHAPTER 8
Performance Statistics
Statistics about the CPU(s), memory, disk drives, processes, user session, system uptime, network
packets, etc. for a host or a virtual machine are available using the Parallels C API. There are two
main methods for obtaining statistics:
In This Chapter
Obtaining Performance Statistics ............................................................................ 111
Performance Monitoring ......................................................................................... 115
Functions that can be used to extract statistics data from a PHT_SYSTEM_STATISTICS handle
can be found in the C API Reference under the following sections:
C API Reference Section Description
PHT_SYSTEM_STATISTICS Functions to drill deeper into specific system
statistics. As an example, to use functions that
return CPU statistics, a handle of type
PHT_SYSTEM_STATISTICS_CPU will be
required. This handle can be obtained using
PrlStat_GetCpuStat.
Functions that return memory statistics are also
grouped here.
Parallels C API by Example
The following code example will display CPU usage, used RAM, free RAM, used disk space, and
free disk space using the first method (PrlSrv_GetStatistics):
// Obtain host statistics (PHT_SYSTEM_STATISTICS handle), and wait for a
// maximum of 10 seconds for the asynchronous call PrlSrv_GetStatistics to complete.
// Note: PrlVm_GetStatistics(hVm) could be used instead of
// PrlSrv_GetStatistics(hServer) if statistics are required for a
// virtual machine that is running.
PRL_HANDLE hServerStatisticsJob = PrlSrv_GetStatistics(hServer);
PRL_RESULT nServerStatistics = PrlJob_Wait(hServerStatisticsJob, 10000);
if (PRL_FAILED(nServerStatistics))
{
printf("PrlSrv_GetStatistics returned error: %s\n",
prl_result_to_string(nServerStatistics));
PrlHandle_Free(hServerStatisticsJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
112
Parallels C API by Example
PrlHandle_Free(hDiskStatistics);
PrlHandle_Free(hCpuStatistics);
PrlHandle_Free(hResult);
PrlHandle_Free(hServerStatistics);
PrlHandle_Free(hServerStatisticsJob);
113
Parallels C API by Example
Example
The following example shows how to obtain the virtual machine disk I/O statistics.
PRL_HANDLE hVm, hJob, hResult, hEvent, hPerfCounter;
114
Parallels C API by Example
Performance Monitoring
To monitor the host or a virtual machine performance on a periodic basis, an event handler
(callback function) is required. Within the event handler, first check the type of event. Events of type
PET_DSP_EVT_HOST_STATISTICS_UPDATED indicate an event containing statistics data. To
access the statistics handle (a handle of type PHT_SYSTEM_STATISTICS), first extract the event
parameter using PrlEvent_GetParam, then convert the result (which will be a handle to an
object of type PHT_EVENT_PARAMETER) to a handle using PrlEvtPrm_ToHandle. The
functions that operate on PHT_SYSTEM_STATISTICS references can then be used to obtain
statistics data.
The following is a complete example that demonstrates how to obtain statistics data
asynchronously using PrlSrv_SubscribeToHostStatistics. Note that the same code could
be used to receive statistics data for a virtual machine, instead of the host computer, by using
PrlVm_SubscribeToGuestStatistics instead of
PrlSrv_SubscribeToHostStatistics, and passing it a handle to a virtual machine that is
running. This would also require using PrlVm_UnsubscribeFromGuestStatistics to stop
receiving statistics data for the virtual machine.
#include "Parallels.h"
#include "Wrappers/SdkWrap/SdkWrap.h"
#include <stdio.h>
#ifdef _WIN_
#include <windows.h>
#else
#include <unistd.h>
#endif
// -------------------------------------------------------------------------
// Event handler.
// -------------------------------------------------------------------------
// 1. Check for events of type PET_DSP_EVT_HOST_STATISTICS_UPDATES.
// 2. Display a header if first call to this event handler.
// 3. Get the event param (PHT_EVENT_PARAMETER) from the PHT_EVENT handle.
// 4. Convert event param to a handle (will be type PHT_SYSTEM_STATISTICS).
// 5. Use PHT_SYSTEM_STATISTICS handle to obtain CPU usage, memory usage,
// and disk usage data.
// -------------------------------------------------------------------------
static PRL_RESULT OurCallback(PRL_HANDLE handle, void *pData)
{
115
Parallels C API by Example
PRL_HANDLE_TYPE nHandleType;
PRL_RESULT ret = PrlHandle_GetType(handle, &nHandleType);
// Check for PrlHandle_GetType error here.
if (nHandleType == PHT_EVENT)
{
PRL_EVENT_TYPE EventType;
PrlEvent_GetType(handle, &EventType);
PrlHandle_Free(hDiskStatistics);
PrlHandle_Free(hCpuStatistics);
PrlHandle_Free(hServerStatistics);
PrlHandle_Free(hEventParameters);
}
}
116
Parallels C API by Example
PrlHandle_Free(handle);
return PRL_ERR_SUCCESS;
}
// --------------------------------------------------------------------------
// Program entry point.
// --------------------------------------------------------------------------
// 1. Call SdkWrap_Load(SDK_LIB_NAME).
// 2. Call PrlApi_InitEx().
// 3. Create a PRL_SERVER handle using PrlSrv_Create.
// 4. Log in using PrlSrv_Login.
// 5. Register our event handler (OurCallback function).
// 6. Subscribe to host statistics events.
// 7. Keep receiving events until user presses <enter> key.
// 8. Unsubscribe from host statistics events.
// 9. Un-register our event handler.
// 10. Logoff using PrlSrv_Logoff.
// 11. Call PrlApi_Uninit.
// 12. Call SdkWrap_Unload.
// --------------------------------------------------------------------------
int main(int argc, char* argv[])
{
PRL_HANDLE hServer = PRL_INVALID_HANDLE;
PRL_RESULT ret;
117
Parallels C API by Example
// ----------------------------------------------------------------------------
// 1. Register our event handler (OurCallback function).
// 2. Subscribe to host statistics events.
// 3. Keep receiving events until user presses <enter> key.
// 4. Unsubscribe from host statistics events.
// 5. Un-register out event handler.
// ----------------------------------------------------------------------------
118
Parallels C API by Example
// ----------------------------------------------------------------------------
// Log off.
hJob = PrlSrv_Logoff(hServer);
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
return 0;
}
119
Parallels C API by Example
Encryption Plug-in
A Parallels virtual machine can be encrypted to make its files unreadable to anyone except those
authorized to do so. When configuring a virtual machine, you have an option to encrypt its files if
desired. When this option is selected, the user is asked to enter a password and then the virtual
machine files are encrypted using an encryption algorithm. Once this is done, the virtual machine
cannot be started without providing the correct password and its files cannot be read using
standard Parallels or third party utilities. An encryption module is provided by Parallels and is built
into the Parallels Desktop. The built-in module uses a sufficiently strong and fast encryption
algorithm. If, however, you would like to use your own (or third-party) encryption module, the
Parallels Virtualization SDK provides an API that allows to custom built an encryption plug-in and
then use it instead of the built-in one. The following sub-sections describes how to build and use
such a plug-in.
All of the steps above are discussed in detail in the following sub-sections.
Please note that developing an encryption plug-in is different than developing applications described
earlier in this guide. All you need to know about developing an encryption plug-in is contained in this
portion of the guide.
120
Parallels C API by Example
Constants
PRL_CLS_NULL is a constant that defines the NULL GUID used as a terminator in the interfaces
array (the array is used to hold the list of the interfaces supported by the component and is
declared and populated in the plug-in implementation).
static const PRL_GUID PRL_CLS_NULL = { 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 } };
PRL_CLS_BASE is a constant that defines the GUID of the base interface. The base interface
provides access to the base plug-in functionality, such as creating objects, getting the plug-in info,
and memory management. The base interface name is PrlPlugin and it's described later in this
section.
static const PRL_GUID PRL_CLS_BASE = { 0x823067ea, 0x8e15, 0x474f,
{ 0xa3, 0xae, 0xc6, 0xa0, 0x68, 0x46, 0x12, 0x56 } };
#define GUID_CLS_BASE_STR "{823067ea-8e15-474f-a3ae-c6a068461256}"
PRL_CLS_ENCRYPTION is a constant that defines the encryption interface GUID. The encryption
interface provides access to the data encryption functionality. The encryption component name is
PrlCrypt and it's described later in this section.
static const PRL_GUID PRL_CLS_ENCRYPTION = { 0x564820fc, 0xe265, 0x4d69,
{ 0x9f, 0xb0, 0x3d, 0x18, 0x39, 0x6f, 0x1f, 0x8d } };
#define GUID_CLS_ENCRYPTION_STR "{564820fc-e265-4d69-9fb0-3d18396f1f8d}"
Structures
ICryptInfo is a structure that's used to hold the plug-in info (PluginInfo) together with the key
and block size values used for data encryption.
typedef struct _ICryptInfo
{
IPluginInfo PluginInfo;
PRL_UINT32 KeySize;
PRL_UINT32 BlockSize;
} PRL_STRUCT(ICryptInfo);
typedef ICryptInfo* ICryptInfoPtr;
Interfaces
PrlPlugin is an interface that provides access to the base plug-in functionality, such as creating
objects, getting the plug-in info, and memory management.
121
Parallels C API by Example
} PRL_STRUCT( PrlCrypt );
Exported functions
PrlInitPlugin is a function that is called on plug-in loading and should contain the code to do some
global plug-in initialization. This function is optional.
PRL_RESULT PRL_CALL PrlInitPlugin()
PrlFiniPlugin is a function that is called on plug-in unloading and should contain the clean-up
code. This function is optional.
122
Parallels C API by Example
PrlGetObjectInfo is a function that is called after plug-in initialization in order to enumerate the
plug-in interfaces. The InterfacesList parameter is used as output and should contain the list
of GUIDs of public interfaces provided by the plug-in.
PRL_RESULT PRL_CALL PrlGetObjectInfo(PRL_UINT32 Number, PRL_GUID* Uid, PRL_GUID**
InterfacesList)
PrlCreateObject is a function that is called in order to instantiate the corresponding plug-in object.
The function should contain a code that will properly initialize and populate the PrlPlugin
structure a reference to which is passed back using the Out parameter. The Parallels Service will
then use the returned reference to request the necessary interfaces (e.g. PrlCrypt) using the
PrlPlugin::QueryInterface() call.
PRL_RESULT PRL_CALL PrlCreateObject(PRL_GUID* Uid, PrlPlugin** Out)
Implementing a Plug-in
CC = g++
RM = rm -f
CCFLAGS = -c -Wall -arch i386 -arch x86_64 -O2 -gdwarf-2 -fvisibility=hidden -O2 -fPIC -mmacosx-
version-min=10.5
INCPATH = -I../Library/Frameworks/ParallelsVirtualizationSDK.framework/Headers
LIBRARY = libsample_plugin.dylib
SRCFILE = Plugin.cpp
OBJFILE = $(SRCFILE:.cpp=.o)
.cpp.o:
$(CC) $(CCFLAGS) $(INCPATH) $<
$(LIBRARY): $(OBJFILE)
$(CC) $(LDFLAGS) $(OBJFILE) -o $@
all: $(LIBRARY)
clean:
$(RM) *.o *.dylib
123
Parallels C API by Example
Before you can use the plug-in, you have to turn on third-party plug-in support in Parallels Desktop
preferences. Select Preferences from the Parallels Desktop menu. On the Preferences window go
to Advanced and select the Allow third-party plug-ins option. When this option is selected,
Parallels Desktop will scan the plug-in directory and will load the new plug-in (if you have more than
one plug-in, it will load all of them).
To encrypt a virtual machine using the encryption plug-in, open the virtual machine configuration
and go to Options/Security. Press the Encryption Turn On... button. Select the encryption plug-in
in the Encryption Engine list. Choose and type a password and click OK.
124
CHAPTER 9
Parallels Python API is a wrapper of the C API described earlier in this guide. While it is based on
the same essential principles as the C API, there are some notable differences. They are
summarized below.
• Handles are not directly visible in the Python API. Instead, Python classes are used. You don't
obtain a handle in Python, you obtain an instance of a class.
• Instead of calling a C function passing a handle to it, you use a Python class and call a method
of that class.
• Memory management is automatic. This means that you don't have to free a handle (destroy an
object) when it is no longer needed.
• No callbacks! Callback functionality does not exist in the Parallels Python API. This means a few
things. First, it is impossible to receive asynchronous method results via callbacks, which
essentially means that these methods are not truly asynchronous in the Python API. Second,
you cannot receive system event notifications in Python. Finally, you cannot automatically
receive periodic performance reports (you can still obtain the reports via synchronous calls).
• Error handling is implemented using exceptions.
• Strings are handled as objects (not as char arrays compared to the C API), which makes it
much easier to work with strings as input and output parameters.
In This Chapter
Package and Modules ............................................................................................ 125
Classes .................................................................................................................. 126
Class Methods........................................................................................................ 126
Error Handling......................................................................................................... 130
The Parallels Python package is installed automatically during the Parallels Virtualization SDK
installation and is placed into the default directory for Python site-packages.
Classes
Compared to the Parallels C API, a Python class is an equivalent of a C handle. In most cases, an
instance of a class must be obtained using a method of another class. Instances of particular
classes are obtained in a certain order. A typical program must first obtain an instance of the
prlsdkapi.Server class identifying the Parallels Service. If the intention is to work with a virtual
machine, an instance of the prlsdkapi.Vm class identifying the virtual machine must then be
obtained using the corresponding methods of the prlsdkapi.Server class.To view or modify
the virtual machine configuration setting, an instance of the prlsdkapi.VmConfig class must be
obtained using a method of the prlsdkapi.Vm class, and so forth. The examples in this guide
provide information on how to obtain the most important and commonly used objects (server,
virtual machine, devices, etc.). In general, an instance of a class is obtained using a method of a
class to which the first class logically belongs. For example, a virtual machine belongs to a server,
so the Server class must be used to obtain the virtual machine object. A virtual device belongs to a
virtual machine, so the virtual machine object must be used to obtain a device object, and so on. In
some cases an object must be created manually, but these cases are rare. The most notable one is
the prlsdkapi.Server class, which is created using the server = prlsdkapi.Server()
statement in the very beginning of a typical program.
Class Methods
There are two basic types of method invocations in the Parallels Python API: synchronous and
asynchronous. A synchronous method completes executing before returning to the caller. An
asynchronous method starts a job in the background and returns to the caller immediately without
waiting for the operation to finish. The following subsections describe both method types in detail.
126
Parallels Python API Concepts
Synchronous Methods
A typical synchronous method returns the result directly to the caller as soon as it completes
executing. In the following example the vm_config.get_name method obtains the name of a
virtual machine and returns it to the caller:
vm_name = vm_config.get_name()
Synchronous methods in the Parallels Python API are usually used to extract data from local
objects that were populated earlier in the program. The data can be extracted as objects or native
Python data types. Examples include obtaining virtual machine properties, such as name and OS
version, virtual hard disk type and size, network interface emulation type or MAC address, etc. In
contrast, objects that are populated with the data from the Parallels Service side are obtained using
asynchronous methods, which are described in the following section.
Asynchronous Methods
All asynchronous methods in the Parallels Python API return an instance of the prlsdkapi.Job
class. A Job object is a reference to the background job that the asynchronous method has
started. A job is executed in the background and may take some time to finish. In other languages,
asynchronous jobs are usually handled using callbacks (event handlers). Unfortunately, callbacks
are not available in the Parallels Python API. You have two ways of handling asynchronous jobs in
your application. The first one consists of implementing a loop and checking the status of the
asynchronous job in every iteration. The second approach involves the main thread suspending
itself until the job is finished (essentially emulating a synchronous operation). The following
describes each approach in detail.
The prlsdkapi.Job class provides the get_status method that allows to determine whether
the job is finished or not. The method returns one of the following constants:
127
Parallels Python API Concepts
The scope of the loop in the example above doesn't have to be local of course. You can check the
job status in the main program loop (if you have one) or in any other loop, which can be a part of
your application design. You can have as many jobs running at the same time as you like and you
can check the status of each one of them in the order of your choice.
The prlsdkapi.Job class provides the wait method that can be used to suspend the execution
of the main thread until the job is finished. The method can be invoked as soon as the Job object is
returned by the original asynchronous method or at any time later. The following code snippet
illustrates how it is accomplished.
# Start the virtual machine. This is an asynchronous call.
job = vm.start()
# Suspend the main thread and wait for the job to complete.
result = job.wait()
You can also execute both the original asynchronous method and the Job.wait method on the
same line without obtaining a reference to the Job object, as shown in the following example.
Please note that if you do that, you will not be able to use any of the other methods of the Job
class that can be useful in certain situations. The reason is, this type of method invocation returns
the prlsdkapi.Result object containing the results of the operation, not the Job object (the
Result class is described in the Obtaining the job result subsection below). It it still a perfectly
valid usage that simplifies the program and reduces the number of lines in it.
# Start a virtual machine, wait for the job to complete.
vm.start().wait()
128
Parallels Python API Concepts
Asynchronous methods that are used to perform actions of some sort (e.g. start or stop a virtual
machine) don't usually return any data to the caller. Other asynchronous methods are used to
obtain data from the Parallels Service side. The data is usually returned as an object or a list of
objects. A good example would be a prlsdkapi.Vm object (virtual machine), a list of which is
returned by the prlsdkapi.Server.get_vm_list asynchronous method. The data is not
returned to the caller directly. It is contained in the prlsdkapi.Result object, which must be
obtained from the Job object using the Job.get_result method. The prlsdkapi.Result
class is a container that can contain one or more objects or strings depending on the operation that
populated it. To determine the number of objects that it contains, the
Result.get_params_count method must be used. To obtain an individual object, use the
get_param_by_index method passing an index as a parameter (from 0 to count - 1).When an
asynchronous operation returns a single object, the get_param method can be used. Strings are
obtained in the similar manner using the corresponding methods of the Result class
(get_param_by_index_as_string, get_param_as_string).
The following code snippet shows how to execute an asynchronous operation and then obtain the
data from the Job object. In this example, we are obtaining the list of virtual machines registered
with the Parallels Service.
# Obtain the virtual machine list.
# get_vm_list is an asynchronous method that returns
# a prlsdkapi.Result object containing the list of virtual machines.
job = server.get_vm_list()
job.wait()
result = job.get_result()
129
Parallels Python API Concepts
cancel -- attempts to cancel a job that is still in progress. Please note that not all jobs can be
canceled.
Error Handling
Error handling in the Parallels Python API is done through the use of exceptions that are caught in
try blocks and handled in except blocks. All methods in the API throw the
prlsdkapi.PrlSDKError exception. The PrlSDKError object itself contains the error
message. To obtain the error code, examine the prlsdkapi.PrlSDKError.error_code
argument. The error code can be evaluated against standard Parallels API errors, which can be
found in the prlsdkapi.prlsdk.errors module. The most common error codes are
PRL_ERR_SUCCESS, PRL_ERR_INVALID_ARG, PRL_ERR_OUT_OF_MEMORY. For the complete
list of errors, see the prlsdkapi.prlsdk.errors module documentation or the Parallels
Python API Reference guide.
130
CHAPTER 10
This chapter provides code samples and descriptions of how to perform the most common tasks
using the Parallels Python API. Each sample is provided as a complete function that can be used in
your own program. Each sample function accepts a parameter -- usually an instance of the
prlsdkaspi.Server class identifying the Parallels Service or an instance of the prlsdkapi.Vm
class identifying a virtual machine. The Creating a Basic Application section (p. 131) shows how
to create and initialize the prlsdkapi.Server object and contains a skeleton program that can
be used to run individual examples provided later in this chapter. To run the examples, simply paste
a sample function into the program and then call it from main() passing the correct object and/or
other required values.
In This Chapter
Creating a Basic Application ................................................................................... 131
Connecting to Parallels Service and Logging In ....................................................... 133
Host Operations ..................................................................................................... 135
Virtual Machine Operations ..................................................................................... 138
Remote Desktop Access ........................................................................................ 156
1 Import the prlsdkapi package. This is the main Parallels Python API package containing the
majority of the classes and additional modules.
2 Initialize the API using the prlsdkapi.init_desktop_sdk() function. To verify that the
API was initialized successfully, use the prlsdkapi.is_sdk_initialized() function.
3 Create an instance of the prlsdkapi.Server class. The Server class provides methods for
logging in and for obtaining other object references (a virtual_machine object in particular).
4 Perform the login operation using the Server.login_local() method.
1 Log off using the Server.logoff() method. The method does not accept any parameters
and simply ends the client session.
2 Deinitialize the API using the prlsdkapi.deinit_sdk() function.
Parallels Python API by Example
Example
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Example of prlsdkapi usage.
#
# Define constants for easy referencing of the Parallels Python API modules.
consts = prlsdkapi.prlsdk.consts
"""
Parallels Service login.
try:
# The call returns a prlsdkapi.Result object on success.
result = server.login_local('', 0, security_level).wait()
except prlsdkapi.PrlSDKError, e:
print "Login error: %s" % e
raise Halt
print""
print "Login successful"
print""
print "Parallels Desktop version: " + product_version
print "Host OS verions: " + host_os_version
print "Host UUID: " + host_uuid
print ""
132
Parallels Python API by Example
###################################################################################
def main():
if __name__ == "__main__":
try:
sys.exit(main())
except Halt:
pass
Parallels Server accepts both local and remote connections. This means that your program can run
anywhere on the network and connect to the Parallels Server remotely. Parallels Server allows
multiple connections. If running a program locally, you have an option to login as the current user or
as a specific user. If a program is running on a remote machine, you always have to specify the
user information.
All other Parallels virtualization products accept local connections only. This means that you can
only run your program on the same computer that hosts the Parallels Virtualization Service. Multiple
connections are not allowed, so the only option available is to connect as the current user.
The prlsdkapi.Server class provides two login methods: login_local and login.
The login_local method is used to establish a local connection as a current user. It can be
used with Parallels Server and other Parallels products.
The login() method is used to establish a local or a remote connection as a specified user. It
can be used with Parallels Server only.
133
Parallels Python API by Example
The following tables describe the parameters of the two login methods.
prlsdkapi.Server.login_local
Parameter Type Description
prlsdkapi.Server.login
Parameter Type Description
host string The IP address or name of the host.
user string User name.
passwd string User password.
sPrevSessionUuid string [optional] Previous session ID. This parameter
can be used in recovering from a lost connection.
The ID will be used to restore references to
asynchronous jobs that were started in the
previous session and are still running on the
server. If you are not restoring a connection, omit
134
Parallels Python API by Example
Host Operations
Retrieving Host Configuration Info
The Parallels Python API provides a set of methods to retrieve detailed information about a host
machine. This includes:
135
Parallels Python API by Example
Example
"""
This function demonstrates how to obtain the
host computer configuration information.
@param server: An instance of prlsdkapi.Server identifying the
Parallels Service.
"""
def get_host_configuration_info(server):
print ""
print "Host Configuration Information"
print "====================================="
srv_config = result.get_param()
for i in range(srv_config.get_net_adapters_count()):
hw_net_adapter = srv_config.get_net_adapter(i)
adapter_type = hw_net_adapter.get_net_adapter_type()
if adapter_type == consts.PHI_REAL_NET_ADAPTER:
adapter_type = "Physical adapter"
elif adapter_type == consts.PHI_VIRTUAL_NET_ADAPTER:
136
Parallels Python API by Example
if hw_net_adapter.is_enabled():
status = "enabled"
else:
status = "disabled"
Example
"""
This function shows how to view and modify Parallels Service preferences.
@param server: An instance of prlsdkapi.Server identifying the
Parallels Service.
"""
def srv_preferences_management(server):
print ""
print "Parallels Service Preferences Management"
print "------------------------------------------"
137
Parallels Python API by Example
result = server.get_common_prefs().wait()
except prlsdkapi.PrlSDKError, e:
print "Error: %s" % e
return
disp_config = result.get_param()
if security_level == consts.PSL_LOW_SECURITY:
security_level = "Low"
elif security_level == consts.PSL_NORMAL_SECURITY:
security_level = "Normal"
elif security_level == consts.PSL_HIGH_SECURITY:
security_level = "High"
138
Parallels Python API by Example
The following example shows how obtain the virtual machine list. The sample functions accepts a
prldsdkapi.Server object. Before passing it to the function, the object must be properly
created, the API library must be initialized, and a connection with the Parallels Service must be
established. Please see the Creating a Basic Application section (p. 131) for more information
and code samples.
Example
"""
Obtain a list of the existing virtual machines and print it
on the screen.
@param server: An instance of prlsdkapi.Server
identifying the Parallels Service.
"""
def get_vm_list(server):
for i in range(result.get_params_count()):
vm = result.get_param_by_index(i)
print "--------------------------------------------------"
140
Parallels Python API by Example
Example
# Obtain a Vm object for the virtual machine specified by its name.
# @param vm_to_find: Name of the virtual machine to find.
# Can be a partial name (starts with the specified string).
def search_vm(server, vm_to_find):
try:
result = server.get_vm_list().wait()
except prlsdkapi.PrlSDKError, e:
print "Error: %s" % e
return
for i in range(result.get_params_count()):
vm = result.get_param_by_index(i)
vm_name = vm.get_name()
if vm_name.startswith(vm_to_find):
return vm
The following code demonstrates how the function can be called from the main() function.
# Search for a virtual machine specified by name.
search_name = "Windows"
print ""
print "Searching for '" + search_name + "%'"
vm = search_vm(server, search_name)
if isinstance(vm, prlsdkapi.Vm):
print "Found virtual machine " + vm.get_name()
Please note that powering off a virtual machine is not the same as performing an operating system
shutdown. When a virtual machine is stopped, it is a cold stop (i.e. it is the same as turning off the
power to a computer). Any unsaved data will be lost. However, if the OS in the virtual machine
supports ACPI (Advanced Configuration and Power Interface) then it can be used to shut down the
virtual machine properly. ACPI is currently supported only with "stop" and "pause" operations.
Corresponding methods have an additional parameter that can be used to instruct them to use
ACPI.
The following code snippets demonstrate how to perform each of the power operations.
Examples
# Stop the virtual machine.
# The boolean parameter (True) specifies to use ACPI.
try:
vm.stop(True).wait()
except prlsdkapi.PrlSDKError, e:
141
Parallels Python API by Example
In this section, we will discuss how to create a typical virtual machine for a particular OS type using
a sample configuration. By using this approach, you can easily create a virtual machine without
knowing all of the little details about configuring a virtual machine for a particular operating system
type.
142
Parallels Python API by Example
2 Obtain a new prlsdkapi.Vm object that will identify the new virtual machine. This must be
performed using the prlsdkapi.Server.create_vm method.
3 Obtain an instance of the prlsdkapi.VmConfig object that will contain the new virtual
machine configuration information. This step must be performed using the
prlsdkapi.Vm.get_config method.
4 Set the default configuration based on the version of the OS that you will later install in the
machine. This step is performed using the
prlsdkapi.VmConfig.set_default_config() method. You supply the version of the
target OS and the method will generate the appropriate configuration parameters automatically.
The OS version is specified using predefined constants that have the PVS_GUEST_VER_ prefix
in their names.
5 Choose a name for the virtual machine and set it using the VmConfing.set_name method.
6 Modify the default configuration parameters if needed. For example, you may want to modify
the hard disk image type and size, the amount of memory available to the machine, and the
networking options. When modifying a device, an object identifying it must first be obtained and
then its methods and properties can be used to make the modifications. The code sample
provided in this section shows how to modify some of the default configuration values.
7 Create and register the new machine using the Vm.reg() method. This step will create the
necessary virtual machine files on the host and register the machine with Parallels Service. The
virtual machine directory will have the same name as the name you've chosen for your virtual
machine and will be created in the default location for this Parallels Service. You may specify a
different virtual machine directory name and path if you wish.
Sample
"""
Create a new virtual machine.
"""
def create_vm(server):
srv_config = result.get_param()
All sample functions below accept a single parameter -- an instance of prlsdkapi.Vm class. To
obtain the object, you can use the helper function search_vm() that we've created in the
Searching for a Virtual Machine section (p. 140).
144
Parallels Python API by Example
print ""
print ""
print "Optical Drives:"
print "-------------------"
145
Parallels Python API by Example
device = vm_config.get_optical_disk(i)
if emulated_type == consts.PDT_USE_REAL_DEVICE:
print "Uses physical device"
elif emulated_type == consts.PDT_USE_IMAGE_FILE:
print "Uses image file " + '"' + device.get_image_path() + '"'
else:
print "Unknown emulation type"
if device.is_enabled():
print "Enabled"
else:
print "Disabled"
if device.is_connected():
print "Connected"
else:
print "Disconnected"
print ""
print "Virtual Hard Disks:"
print "-------------------"
count = vm_config.get_hard_disks_count()
for i in range(count):
print ""
print "Disk " + str(i)
hdd = vm_config.get_hard_disk(i)
emulated_type = hdd.get_emulated_type()
if emulated_type == consts.PDT_USE_REAL_DEVICE:
print "Uses Boot Camp: Disk " + hdd.get_friendly_name()
elif emulated_type == consts.PDT_USE_IMAGE_FILE:
print "Uses image file " + '"' + hdd.get_image_path() + '"'
if hdd.get_disk_type() == consts.PHD_EXPANDING_HARD_DISK:
print "Expanding disk"
elif hdd.get_disk_type() == consts.PHD_PLAIN_HARD_DISK:
print "Plain disk"
146
Parallels Python API by Example
count = vm_config.get_net_adapters_count()
for i in range(count):
print ""
print "Adapter " + str(i)
net_adapter = vm_config.get_net_adapter(i)
emulated_type = net_adapter.get_emulated_type()
if emulated_type == consts.PNA_HOST_ONLY:
print "Uses host-only networking"
elif emulated_type == consts.PNA_SHARED:
print "Uses shared networking"
elif emulated_type == consts.PNA_BRIDGED_ETHERNET:
print "Uses bridged ethernet (bound to " +
net_adapter.get_bound_adapter_name() + ")"
147
Parallels Python API by Example
The Vm.begin_edit method timestamps the beginning of the editing operation. It does not lock
the machine, so other programs can still make changes to the same virtual machine. The method
will also automatically update your local virtual machine object with the current virtual machine
configuration information. This is done in order to ensure that the local object contains the changes
that might have happened since you obtained the virtual machine object. When you are done
making the changes, you must invoke the Vm.commit method. The first thing that the method will
do is verify that the virtual machine configuration has not been modified by other programs since
you began making your changes. If a collision is detected, your changes will be rejected and
Vm.commit will throw an exception. In such a case, you will have to reapply the changes. In order
to do that, you will have to get the latest configuration using the Vm.refresh_config method
and re-evaluate it. Please note that Vm.refresh_config method will update the configuration
data in your local virtual machine object and will overwrite all existing data, including the changes
that you've made so far. Furthermore, the Vm.begin_edit method will also overwrite all existing
data (see above). If you don't want to loose your data, save it locally before invoking any of the two
methods.
Example
"""
Modify the vrtual machine name, RAM size, and boot options.
"""
def vm_edit(vm):
148
Parallels Python API by Example
vm.set_name(vm.get_name() + "_modified")
vm.set_ram_size(256)
vm.set_description("SDK Test Machine")
if dev_type == consts.PDE_OPTICAL_DISK:
boot_dev.set_sequence_index(0)
elif dev_type == consts.PDE_HARD_DISK:
boot_dev.set_sequence_index(1)
elif dev_type == consts.PDE_GENERIC_NETWORK_ADAPTER:
boot_dev.set_sequence_index(2)
elif dev_type == consts.PDE_FLOPPY_DISK:
boot_dev.set_sequence_index(3)
else:
boot_dev.remove()
150
Parallels Python API by Example
151
Parallels Python API by Example
srv_config = result.get_param()
Note: When adding an existing virtual machine, the MAC addresses of its virtual network adapters are
kept unchanged. If the machine is a copy of another virtual machine, then you should set new MAC
addresses for its network adapters after you register it. The example below demonstrates how this can
be accomplished.
Example:
The following sample function demonstrates how to register an existing virtual machine. The
function takes a Server object identifying the Parallels Service and a string specifying the name
and path of the virtual machine directory (on Mac OS X it is the name of a bundle). It registers the
virtual machine and then modifies the MAC address of every virtual network adapter installed in it.
"""
Add an existing virtual machine.
@param path: Name and path of the virtual machine directory or bundle.
The name normally has the .PVM extension.
"""
152
Parallels Python API by Example
try:
result = server.register_vm(path, False).wait()
except prlsdkapi.PrlSDKError, e:
print "Error: %s" % e
return
vm = result.get_param()
vm_config = vm.get_config()
print vm_config.get_name() + " was registered."
The following sample function illustrates how to implement both options. The function takes a Vm
object identifying the virtual machine and a boolean value indicating whether the virtual machine
files should be deleted from the host computer.
"""
Remove an existing virtual machine.
@param vm: An instance of prlsdkapi.Vm class identifying
the virtual machine.
@param delete: A boolean value indicating whether the
virtual machine files should be permanently deleted
from the host.
153
Parallels Python API by Example
"""
def remove_vm(vm, delete):
if delete == False:
# Unregister the virtual machine but don't delete its files.
try:
vm.unreg()
except prlsdkapi.PrlSDKError, e:
print "Error: %s" % e
return
else:
# Unregister the machine and delete its files from the hard drive.
try:
vm.delete()
except prlsdkapi.PrlSDKError, e:
print "Error: %s" % e
return
Sample
"""
Clone a virtual machine.
@param vm: An instance of the prlsdkapi.Vm class identifying
the source virtual machine.
"""
def clone_vm(vm):
try:
new_name = "Clone_2 of " + vm.get_config().get_name()
print "Clonning is in progress..."
# Second parameter - create a new machine in the
# default directory.
# Third parameter - create a virtual machine (not a template).
vm.clone(new_name, "", False).wait()
except prlsdkapi.PrlSDKError, e:
print "Error: %s" % e
return
print "Cloning was successful. New virtual machine name: " + new_name
154
Parallels Python API by Example
The following sample program demonstrates how to execute a batch file (C:\\123.bat) in Windows
running in a virtual machine. To test the program, create a batch file with a simple instruction (e.g.
creating a directory on the C: drive) and see if after executing the program, the directory is actually
created.
"""
Executes a batch file in a Windows virtual machine.
@param vm: An instance of the prlsdkapi.Vm class identifying
the source virtual machine.
"""
def exec_batch(vm):
# Uncomment the desired "g_login = " and "g_password = " lines below.
155
Parallels Python API by Example
#g_password = "your_password"
# Create a StringList object to hold the program input parameters and populate it.
hArgsList = prlsdkapi.StringList()
hArgsList.add_item("cmd.exe")
hArgsList.add_item("/C")
hArgsList.add_item("C:\\123.bat")
The functionality is supported in both Parallels C and Python APIs. C API provides additional
functions that can be used to create remote desktop applications with graphical user interfaces.
Python API contains a simplified version of the C API functionality and is best suited for writing
automation scripts.
The Remote Desktop Access functionality is provided by the prlsdkapi.VmIO class. There are
three groups of methods in the class:
156
Parallels Python API by Example
• Primary display capture. These functions allow to capture the primary display of the remote
virtual machine. In scripts, you can take a snapshot of a screen of interest in advance and save
it to a file. At runtime, you capture virtual machine screens after every interaction with it and
perform a bit-by-bit comparison of the saved screen and a snapshot that you take each time. If
the comparison operation determines that the screen currently displayed on the virtual machine
desktop is the screen of interest, you can interact with user interface controls that it contains
(keystrokes on text-based screens; visual controls on GUI screens) by sending mouse or
keyboard commands to the virtual machine. The assumption here is that the desktop
background is static, individual windows always open at the same coordinates, have a fixed
size, and contain the same number of controls and data in the same exact default state.
• Mouse control. These functions provide mouse control in a virtual machine. You can change
the position of the mouse pointer, press and release mouse buttons, and use a scroll wheel.
• Keyboard control. These functions send a key/action code combination to the virtual machine.
In scripts, you can use these functions to interact with controls on a window opened inside a
virtual machine (pressing buttons, selecting options, etc.). In a typical GUI application, essential
visual controls usually have keyboard shortcuts (accelerator keys) assigned to them. For
example, to click a button, you can send an accelerator key combination to the virtual machine;
to select/deselect a check-box you similarly send a keyboard shortcut assigned to it, and so
forth. If a control doesn't have a shortcut, then you will have to use mouse control functions to
position a mouse pointer over it and clicking the mouse button (you will have to determine the
control's coordinates in advance to properly position the mouse pointer).
The use of the this functionality is not limited to the tasks described above. You can use it for
anything that requires taking screenshots of a virtual machine desktop and controlling its keyboard
and mouse input.
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).
157
Parallels Python API by Example
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.
158
Parallels Python API by Example
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()
# 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()
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
159
Parallels Python API by Example
if found == False:
print "Specified virtual machine not found."
exit()
160
Parallels Python API by Example
161
Index
Index
Events - 101
A
Executing a Program in a Virtual Machine -
Adding a Hard Disk Drive - 149 155
Adding a Network Adapter - 151
G
Adding an Existing Virtual Machine - 72, 152
Asynchronous Functions - 20 Getting Started - 6
Asynchronous Methods - 127
H
B
Handles - 18
Bridged Networking - 86 Hard Disks - 81
Building the Dynamic Library - 123 Host Operations - 34, 135
Host-only and Shared Networking - 84
C
I
Class Methods - 126
Classes - 126 Implementing a Plug-in - 123
Cloning a Virtual Machine - 74, 154
M
Compiling Client Applications - 8
Compiling with Framework - 14 Managing Files In The Host OS - 47
Compiling with SdkWrap - 9 Managing Licenses - 50
Connecting to Parallels Service and Logging Managing Parallels Service Preferences - 36,
In - 133 137
Converting a Regular Virtual Machine to a Managing Parallels Service Users - 42
Template - 97 Managing User Access Rights - 90
Converting a Template to a Regular Virtual Modifying Virtual Machine Configuration - 78,
Machine - 98 147
Creating a Basic Application - 131
N
Creating a New Virtual Machine - 65, 142
Creating a New Virtual Machine From a Name, Description, Boot Options - 79
Template. - 99 Name, Description, Boot Options, RAM size -
Creating a Simple OS Installation Program - 148
157 Network Adapters - 84
Creating a Template From Scratch - 95
O
D
Obtaining a List of Templates - 93
Deleting a Virtual Machine - 76 Obtaining a PHT_VM_CONFIGURATION
Determining Virtual Machine State - 60 handle - 79
Obtaining a Problem Report - 52
E
Obtaining Disk I/O Statistics - 113
Encryption Plug-in - 120 Obtaining Performance Statistics - 111
Encryption Plug-in Basics - 120 Obtaining Server Handle and Logging In - 30
Error Handling - 26, 130 Obtaining the Virtual Machine List - 139
Index