Chapter 2. Using the Software Development Kit
This section describes how to use the software development kit for Version 4.
2.1. Packages
The following modules are most frequently used by the Python SDK:
- ovirtsdk4
This is the top level module. It most important element is the
Connection
class, which is the mechanism to connect to the server and to obtain the reference to the root of the services tree.The
Error
class is the base exception class that the SDK will raise when it needs to report an error.For certain kinds of errors, there are specific error classes, which extend the base error class:
-
AuthError
- Raised when authentication or authorization fails. -
ConnectionError
- Raised when the name of the server cannot be resolved or the server is unreachable. -
NotFoundError
- Raised when the requested object does not exist. -
TimeoutError
- Raised when an operation times out.
-
- ovirtsdk4.types
This module contains the classes that implement the types used in the API. For example, the
ovirtsdk4.types.Vm
class is the implementation of the virtual machine type. These classes are data containers and do not contain any logic.Instances of these classes are used as parameters and return values of service methods. The conversion to or from the underlying representation is handled transparently by the SDK.
- ovirtsdk4.services
This module contains the classes that implement the services supported by the API. For example, the
ovirtsdk4.services.VmsService
class is the implementation of the service that manages the collection of virtual machines of the system.Instances of these classes are automatically created by the SDK when a service is located. For example, a new instance of the
VmsService
class is automatically created by the SDK when doing the following:vms_service = connection.system_service().vms_service()
It is best to avoid creating instances of these classes manually, as the parameters of the constructors and, in general, all the methods except the service locators and service methods, may change in the future.
There are other modules, like
ovirtsdk4.http
,ovirtsdk4.readers
, andovirtsdk4.writers
. These are used to implement the HTTP communication and for XML parsing and rendering. Avoid using them, because they are internal implementation details that may change in the future; backwards compatibility is not guaranteed.
2.2. Connecting to the Server
To connect to the server, import the ovirtsdk4
module, which contains the Connection
class. This is the entry point of the SDK, and provides access to the root of the tree of services of the API:
import ovirtsdk4 as sdk connection = sdk.Connection( url='https://engine.example.com/ovirt-engine/api', username='admin@internal', password='password', ca_file='ca.pem', )
The connection holds critical resources, including a pool of HTTP connections to the server and an authentication token. It is very important to free these resources when they are no longer in use:
connection.close()
Once a connection is closed, it cannot be reused.
The ca.pem
file is required when connecting to a server protected with TLS. In a normal installation, it is located in /etc/pki/ovirt-engine/ on the Manager machine. If you do not specify the ca_file
, the system-wide CA certificate store will be used. For more information on obtaining the ca.pem
file, see the REST API Guide.
If the connection is not successful, the SDK will raise an ovirtsdk4.Error
exception containing the details.
2.3. Using Types
The classes in the ovirtsdk4.types
module are pure data containers. They do not have any logic or operations. Instances of types can be created and modified at will.
Creating or modifying an instance does not affect the server side, unless the change is explicitly passed with a call to one of the service methods described below. Changes on the server side are not automatically reflected in the instances that already exist in memory.
The constructors of these classes have multiple optional arguments, one for each attribute of the type. This is intended to simplify creation of objects using nested calls to multiple constructors. This example creates an instance of a virtual machine, specifying its cluster name, template, and memory, in bytes:
from ovirtsdk4 import types vm = types.Vm( name='vm1', cluster=types.Cluster( name='Default' ), template=types.Template( name='mytemplate' ), memory=1073741824 )
Using the constructors in this way is recommended, but not mandatory. You can also create the instance with no arguments in the call to the constructor and populate the object step by step, using the setters, or by using a mix of both approaches:
vm = types.Vm() vm.name = 'vm1' vm.cluster = types.Cluster(name='Default') vm.template = types.Template(name='mytemplate') vm.memory=1073741824
Attributes that are defined as lists of objects in the specification of the API are implemented as Python lists. For example, the custom_properties
attributes of the Vm
type are defined as a list of objects of type CustomProperty
. When the attributes are used in the SDK, they are a Python list:
vm = types.Vm( name='vm1', custom_properties=[ types.CustomProperty(...), types.CustomProperty(...), ... ] )
Attributes that are defined as enumerated values in API are implemented as enum
in Python, using the native support for enums
in Python 3 and the enum34
package in Python 2.7. In this example, the status attribute of the Vm
type is defined using the VmStatus enum
:
if vm.status == types.VmStatus.DOWN: ... elif vm.status == types.VmStatus.IMAGE_LOCKED: ....
In the API specification, the values of enum
types appear in lower case, because that is what is used for XML and JSON. The Python convention, however, is to capitalize enum
values.
Reading the attributes of instances of types is done using the corresponding properties:
print("vm.name: %s" % vm.name) print("vm.memory: %s" % vm.memory) for custom_property in vm.custom_properties: ...
2.4. Using Links
Some attributes of types are defined by the API as links. This convention indicates that the values are not normally populated when retrieving the representation of that object. Rather, a link is returned instead. For example, when retrieving a virtual machine, the XML response from the server includes the <link>
attribute:
<vm id="123" href="/ovirt-engine/api/vms/123"> <name>vm1</name> <link rel="diskattachments" href="/ovirt-engine/api/vms/123/diskattachments/> ... </vm>
The link to vm.diskattachments
does not contain the actual disk attachments. To obtain the data, the Connection
class provides a follow_link
method that uses the value of the href
XML attribute to retrieve the actual data. For example, to retrieve the details of the disks of the virtual machine, you follow the link to the disk attachments, and then to each of the disks:
# Retrieve the virtual machine: vm = vm_service.get() # Follow the link to the disk attachments, and then to the disks: attachments = connection.follow_link(vm.disk_attachments) for attachment in attachments: disk = connection.follow_link(attachment.disk) print("disk.alias: " % disk.alias)
2.5. Locating Services
The API provides a set of services, each associated with a path within the URL space of the server. For example, the service that manages the collection of virtual machines of the system is located in /vms, and the service that manages the virtual machine with identifier 123
is located in /vms/123.
In the SDK, the root of that tree of services is implemented by the system service. It is obtained calling the system_service
method of the connection:
system_service = connection.system_service()
When you have the reference to this system service, you can use it to obtain references to other services, calling the *_service
methods, called service locators, of the previous service. For example, to obtain a reference to the service that manages the collection of virtual machines of the system, you use the vms_service
service locator:
vms_service = system_service.vms_service()
To obtain a reference to the service that manages the virtual machine with identifier 123
, you use the vm_service
service locator of the service that manages the collection of virtual machines. It uses the identifier of the virtual machine as a parameter:
vm_service = vms_service.vm_service('123')
Calling service locators does not send a request to the server. The Python objects that they return are pure services, which do not contain any data. For example, the vm_service
Python object called in this example is not the representation of a virtual machine. It is the service that is used to retrieve, update, delete, start and stop that virtual machine.
2.6. Using Services
After you have located a service, you can call its service methods, which send requests to the server and do the real work.
Services that manage a single object usually support the get
, update
, and remove
methods.
Services that manage collections of objects usually support the list
and add
methods.
Both kinds of services, especially services that manage a single object, can support additional action methods.
2.6.1. Using get
Methods
These service methods are used to retrieve the representation of a single object. The following example retrieves the representation of the virtual machine with identifier 123
:
# Find the service that manages the virtual machine: vms_service = system_service.vms_service() vm_service = vms_service.vm_service('123') # Retrieve the representation of the virtual machine: vm = vm_service.get()
The response is an instance of the corresponding type, in this case an instance of the Python class ovirtsdk4.types.Vm
.
The get
methods of some services support additional parameters that control how to retrieve the representation of the object or what representation to retrieve if there is more than one. For example, you may want to retrieve either the current state of a virtual machine or its state the next time it is started, as they may be different. The get
method of the service that manages a virtual machine supports a next_run
Boolean parameter:
# Retrieve the representation of the virtual machine, not the # current one, but the one that will be used after the next # boot: vm = vm_service.get(next_run=True)
See the reference documentation of the SDK for details.
If the object cannot be retrieved for any reason, the SDK raises an ovirtsdk4.Error
exception, with details of the failure. This includes the situation when the object does not actually exist. Note that the exception is raised when calling the get
service method. The call to the service locator method never fails, even if the object does not exist, because that call does not send a request to the server. For example:
# Call the service that manages a non-existent virtual machine. # This call will succeed. vm_service = vms_service.vm_service('junk') # Retrieve the virtual machine. This call will raise an exception. vm = vm_service.get()
2.6.2. Using list
Methods
These service methods retrieve the representations of the objects of a collection. This example retrieves the complete collection of virtual machines of the system:
# Find the service that manages the collection of virtual # machines: vms_service = system_service.vms_service() # List the virtual machines in the collection vms = vms_service.list()
The result will be a Python list containing the instances of corresponding types. For example, in this case, the result will be a list of instances of the class ovirtsdk4.types.Vm
.
The list
methods of some services support additional parameters. For example, almost all top-level collections support a search
parameter to filter the results or a max
parameter to limit the number of results returned by the server. This example retrieves the names of virtual machines starting with my
, with an upper limit of 10 results:
vms = vms_service.list(search='name=my*', max=10)
Not all list
methods support these parameters. Some list
methods support other parameters. See the reference documentation of the SDK for details.
If a list of returned results is empty for any reason, the returned value will be an empty list. It will never be None
.
If there is an error while trying to retrieve the result, the SDK will raise an ovirtsdk4.Error
exception containing the details of the failure.
2.6.3. Using add
Methods
These service methods add new elements to a collection. They receive an instance of the relevant type describing the object to add, send the request to add it, and return an instance of the type describing the added object.
This example adds a new virtual machine called vm1:
from ovirtsdk4 import types # Add the virtual machine: vm = vms_service.add( vm=types.Vm( name='vm1', cluster=types.Cluster( name='Default' ), template=types.Template( name='mytemplate' ) ) )
If the object cannot be created for any reason, the SDK will raise an ovirtsdk4.Error
exception containing the details of the failure. It will never return None
.
The Python object returned by this add
method is an instance of the relevant type. It is not a service but a container of data. In this particular example, the returned object is an instance of the ovirtsdk4.types.Vm
class. If, after creating the virtual machine, you need to perform an operation such as retrieving or starting it, you will first need to find the service that manages it, and call the corresponding service locator:
# Add the virtual machine: vm = vms_service.add( ... ) # Find the service that manages the virtual machine: vm_service = vms_service.vm_service(vm.id) # Start the virtual machine vm_service.start()
Objects are created asynchronously. When you create a new virtual machine, the add
method will return a response before the virtual machine is completely created and ready to be used. It is good practice to poll the status of the object to ensure that it is completely created. For a virtual machine, you should check until its status is DOWN
:
# Add the virtual machine: vm = vms_service.add( ... ) # Find the service that manages the virtual machine: vm_service = vms_service.vm_service(vm.id) # Wait until the virtual machine is down, indicating that it is # completely created: while True: time.sleep(5) vm = vm_service.get() if vm.status == types.VmStatus.DOWN: break
Using a loop to retrieve the object status, with the get
method, ensures that the status attribute is updated.
2.6.4. Using update
Methods
These service methods update existing objects. They receive an instance of the relevant type describing the update to perform, send the request to update it, and return an instance of the type describing the updated object.
This example updates the name of a virtual machine from vm1
to newvm
:
from ovirtsdk4 import types # Find the virtual machine, and then the service that # manages it: vm = vms_service.list(search='name=vm1')[0] vm_service = vm_service.vm_service(vm.id) # Update the name: updated_vm = vm_service.update( vm=types.Vm( name='newvm' ) )
When performing updates, avoid sending the complete representation of the object. Send only the attributes that you want to update. Do not do this:
# Retrieve the complete representation: vm = vm_service.get() # Update the representation, in memory, without sending a request # to the server: vm.name = 'newvm' # Send the update. Do *not* do this. vms_service.update(vm)
Sending the complete representation causes two problems:
- You are sending much more information than the server needs, thus wasting resources.
- The server will try to update all the attributes of the object, even those that you did not intend to change. This may cause bugs on the server side.
The update
methods of some services support additional parameters that control how or what to update. For example, you may want to update either the current state of a virtual machine or the state that will be used the next time the virtual machine is started. The update
method of the service that manages a virtual machine supports a next_run
Boolean parameter:
# Update the memory of the virtual machine to 1 GiB, # not during the current run, but after next boot: vm = vm_service.update( vm=types.Vm( memory=1073741824 ), next_run=True )
If the update cannot be performed for any reason, the SDK will raise an ovirtsdk4.Error
exception containing the details of the failure. It will never return None
.
The Python object returned by this update method is an instance of the relevant type. It is not a service, but a container for data. In this particular example, the returned object will be an instance of the ovirtsdk4.types.Vm
class.
2.6.5. Using remove
Methods
These service methods remove existing objects. They usually do not take parameters, because they are methods of services that manage single objects. Therefore, the service already knows what object to remove.
This example removes the virtual machine with identifier 123
:
# Find the virtual machine by name: vm = vms_service.list(search='name=123')[0] # Find the service that manages the virtual machine using the ID: vm_service = vms_service.vm_service(vm.id) # Remove the virtual machine: vm_service.remove()
The remove
methods of some services support additional parameters that control how or what to remove. For example, it is possible to remove a virtual machine while preserving its disks, using the detach_only
Boolean parameter:
# Remove the virtual machine while preserving the disks: vm_service.remove(detach_only=True)
The remove
method returns None
if the object is removed successfully. It does not return the removed object. If the object cannot be removed for any reason, the SDK raises an ovirtsdk4.Error
exception containing the details of the failure.
2.6.6. Using Other Action Methods
There are other service methods that perform miscellaneous operations, such as stopping and starting a virtual machine:
# Start the virtual machine: vm_service.start()
Many of these methods include parameters that modify the operation. For example, the method that starts a virtual machine supports a use_cloud_init
parameter, if you want to start it using cloud-init
:
# Start the virtual machine: vm_service.start(cloud_init=True)
Most action methods return None
when they succeed and raise an ovirtsdk4.Error
when they fail. A few action methods return values. For example, the service that manages a storage domain has an is_attached
action method that checks whether the storage domain is already attached to a data center and returns a Boolean value:
# Check if the storage domain is attached to a data center: sds_service = system_service.storage_domains_service() sd_service = sds_service.storage_domain_service('123') if sd_service.is_attached(): ...
Check the reference documentation of the SDK to see the action methods supported by each service, the parameters that they take, and the values that they return.
2.7. Additional Resources
For detailed information and examples, see the following resources:
2.7.1. Generating documentation for modules
You can generate documentation using pydoc for the following modules:
- ovirtsdk.api
- ovirtsdk.infrastructure.brokers
- ovirtsdk.infrastructure.errors
The documentation is provided by the ovirt-engine-sdk-python
package. Run the following command on the Manager machine to view the latest version of these documents:
$ pydoc [MODULE]