oVirt / python-ovirt-engine-sdk4

Python SDK for version 4 of the oVirt Engine API
Apache License 2.0
15 stars 25 forks source link
ovirt python

= oVirt Engine API Python SDK

== Introduction

This project contains the Python SDK for the oVirt Engine API.

== Important

Note that most of the code of this SDK is automatically generated. If you just installed the package then you will have everything already, but if you downloaded the source then you will need to generate it, follow the instructions in the README.adoc file of the parent directory.

== Installation

The SDK can be installed in CentOS Linux 8 and CentOS Stream using the RPM packages provided by the oVirt project. To do so install the oVirt release package:

dnf install http://resources.ovirt.org/pub/yum-repo/ovirt-release44.rpm

Then install the SDK packages. For Python 3:

dnf install python3-ovirt-engine-sdk4

For other operating systems (and also for CentOS) you can install the SDK using the pip command, which will download the source from https://pypi.python.org/pypi[PyPI], build and install it.

The SDK uses http://www.xmlsoft.org[libxml2] for parsing and rendering XML. The part of the SDK that interacts with that library is written in C. This means that before building you must make sure you have the C compiler and the required header and libraries files installed in your system. For example, if you are using distributions like Fedora, or CentOS:

dnf -y install \

gcc \ libxml2-devel \ python3-devel

If you are using distributions like Debian, or Ubuntu:

apt-get --assume-yes install \

gcc \ libxml2-dev \ python3-dev

== Usage

=== Packages

The following are the Python modules that are most frequently needed in order to use the SDK:

ovirtsdk4::

This is the top level module. It most important element is the Connection class, as is the mechanism to connect to the server and to get 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 any error.

For certain kinds of errors there are specific error classes, extending the base error class:

ovirtsdk4.types::

This module contains the classes that implement the types used in the API. For example, the ovirtsdk4.types.Vm Python class is the implementation of the virtual machine type. These classes are just containers of data, they don't contain any logic. + Instances of these classes are used as parameters and return values of service methods. The conversion to/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 Python 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 will be automatically created by the SDK when doing the following: + [source,python]

vms_service = connection.system_service().vms_service()

+ 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 (described later) may change in the future.

There are other modules, like ovirtsdk4.http, ovirtsdk4.readers and ovirtsdk4.writers. These are used to implement the HTTP communication, and to for XML parsing and rendering. Refrain from using them, as they are internal implementation details that may change in the future: backwards compatibility isn't guaranteed.

=== Connecting to the server

To connect to the server import the ovirtsdk4 module. That will give to the Connection class. This is the entry point of the SDK, and gives you access to the root of the tree of services of the API:

[source,python]

import ovirtsdk4 as sdk

Create a connection to the server:

connection = sdk.Connection( url='https://engine.example.com/ovirt-engine/api', username='admin@internal', password='...', ca_file='ca.pem', )

The connection holds expensive 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:

[source,python]

Close the connection to the server:

connection.close()

Once a connection is closed it can't be reused.

The ca.pem file is required when connecting to a server protected with TLS. In an usual oVirt installation it will be in /etc/pki/ovirt-engine/ca.pem. If you don't specify ca_file, then system wide CA certificate store will be used.

If something fails when trying to create the connection (authentication failure, communication failure, etc) the SDK will raise a ovirtsdk4.Error exception containing the details.

=== Using types

The classes in the ovirtsdk4.types module are pure data containers, they don't have any logic or operations. Instances can be created and modified at will.

Creating or modifying one of this instances does not have any effect in the server side, unless one they are explicitly passed to a call to one of the service methods described below. Changes in 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. For example, to create an instance of a virtual machine, with an specification of the cluster and template that it should use, and the memory in bytes it should have:

[source,python]

from ovirtsdk4 import types

vm = types.Vm( name='myvm', cluster=types.Cluster( name='mycluster' ), 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 then populate the object step by step, using the setters, or using a mix of both approaches:

[source,python]

vm = types.Vm() vm.name = 'myvm' vm.cluster = types.Cluster(name='mycluster') 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 http://ovirt.github.io/ovirt-engine-api-model/master/#types/vm[Vm] type is defined as a list of objects of type CustomProperty, so when using it in the SDK it will be a Python list:

[source,python]

vm = types.Vm( name='myvm', custom_properties=[ types.CustomProperty(...), types.CustomProperty(...), ... ] )

Attributes that are defined as enumerated values in the specification of the API are implemented as enum in Python, using the native support for enums in Python 3, and using the https://pypi.python.org/pypi/enum34[enum34] package in Python 2.7. For example, the status attribute of the Vm type is defined using the http://ovirt.github.io/ovirt-engine-api-model/master/#types/vm_status[VmStatus] enum:

[source,python]

if vm.status == types.VmStatus.DOWN: ... elif vm.status == types.VmStatus.IMAGE_LOCKED: ....

NOTE: In the specification of the API the values of enum types appear in lower case, because that is what is used in the XML or JSON documents. But in Python it is common practice to use upper case for this kind of constants, so that is how they are defined in the Python SDK: all upper case.

Reading the attributes of instances of types is done using the corresponding properties:

[source,python]

print("vm.name: %s" % vm.name) print("vm.memory: %s" % vm.memory) for custom_property in vm.custom_properties: ...

=== Using links

Some of the attributes of types are defined as links in the specification of the API. This is done to indicate that their value won't usually be populated when retrieving the representation of that object, only a link will be returned instead. For example, when retrieving a virtual machine, the XML returned by the server will look like this:

[source,python]

myvm http://ovirt.github.io/ovirt-engine-api-model/master/#services/vm/methods/get/parameters/next_run[next_run] boolean parameter:

[source,python]

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)

Check the http://ovirt.github.io/ovirt-engine-sdk/master[reference] documentation of the SDK to find out the details.

If the object can't be retrieved, for whatever the reason, the SDK will raise a ovirtsdk4.Error exception, containing the details of the failure. This includes the situation when the object doesn't actually exist. Note that the exception will be raised when calling the get service method, the call to the service locator method never fails, even if the object doesn't exist, because it doesn't send any request to the server. For example:

[source,python]

Find the service that manages a virtual machine that does

not exist. This will succeed.

vm_service = vms_service.vm_service('junk')

Retrieve the virtual machine. This will raise an exception.

vm = vm_service.get()

==== Using the list methods

These service methods are used to retrieve the representations of the objects of the collection. For example, to retrieve the complete collection of virtual machines of the system:

[source,python]

Find the service that manages the collection of virtual

machines:

vms_service = system_service.vms_service() 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 Python class ovirtsdk4.types.Vm.

The list methods of some services support additional parameters. For example, almost all the top level collections support a search parameter that can be used ask the server to filter the results, and a max parameter that can be used to limit the number of results returned by the server. For example, to get the list of virtual machines whose name starts with my, and to get at most 10 results:

[source,python]

vms = vms_service.list(search='name=my*', max=10)

NOTE: Not all the list methods support these parameters, and some list methods may support other additional parameters. Check the http://ovirt.github.io/ovirt-engine-sdk/master[reference] documentation of the SDK to find out the details.

If list of results is empty, for whatever the reason, the returned value will be an empty Python list, it will never be None.

If there is an error while trying to retrieve the result, then the SDK will raise an ovirtsdk4.Error exception containing the details of the failure.

==== Using the add methods

These service methods add new elements to the 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.

For example, to add a new virtual machine named myvm:

[source,python]

from ovirtsdk4 import types

Add the virtual machine:

vm = vms_service.add( vm=types.Vm( name='myvm', cluster=types.Cluster( name='mycluster' ), template=types.Template( name='mytemplate' ) ) )

If the object can't be created, for whatever the reason, the SDK will raise an ovirtsdk4.Error exception containing the details of the failure. It will never return None.

It is very important to understand that the Python object returned by this add method is an instance of the relevant type, it isn't a service, just a container of data. In this particular example the returned object will be an instance of the ovirtsdk4.types.Vm class. If once the virtual machine is created you need to perform some operation on it, like retrieving it again, or starting it, you will first need to find the service that manages it, calling the corresponding service locator:

[source,python]

Add the virtual machine:

vm = vms_service.add( ... )

Find the service that manages the virtual machine:

vm_service = vms_service.vm_service(vm.id)

Perform some other operation on the virtual machine, like

starting it:

vm_service.start()

Note that the creation of most objects is an asynchronous task. That means, for example, that when creating a new virtual machine the add method will return before the virtual machine is completely created and ready to be used. It is good practice to poll the status of the object till it is completely created. For a virtual machine that means checking till the status is down. So the recommended approach to create a virtual machine is the following:

[source,python]

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 till the virtual machine is down, which means that it is

completely created:

while True: time.sleep(5) vm = vm_service.get() if vm.status == types.VmStatus.DOWN: break

In the above loop it is very important to retrieve the object each time, using the get method, otherwise the status attribute won't be updated.

==== Using the 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.

For example, to update the name of a virtual machine from myvm to newvm:

[source,python]

from ovirtsdk4 import types

Find the virtual machine, and then the service that

manages it:

vm = vms_service.list(search='name=myvm')[0] vm_service = vms_service.vm_service(vm.id)

Update the name:

updated_vm = vms_service.update( vm=types.Vm( name='newvm' ) )

When performing updates, try to avoid sending the complete representation of the object, send only the attributes that you want to update. For example, try to avoid this:

[source,python]

Retrieve the current representation:

vm = vm_service.get()

Update the representation, in memory, no request sent

to the server:

vm.name = 'newvm'

Send the update. Do not do this.

vm_service.update(vm)

The problem with that is double. First you are sending much more information than what the server needs, thus wasting resources. Second, and more important, the server will try to update all the attributes of the object, even those that you didn't need to change. Usually that isn't a problem, but has caused many unexpected bugs in the server side in the past.

The update methods of some services support additional parameters that control how or what to update. For example, for virtual machines you may want to update its current state, or the state that will be used the next time it is started. To do so the update method of the service that manages a virtual machine supports a http://ovirt.github.io/ovirt-engine-api-model/master/#services/vm/methods/update/parameters/next_run[next_run] boolean parameter:

[source,python]

Update the memory of the virtual machine 1 GiB, but not the current

one, the one it will have after the next boot:

vm = vm_service.update( vm=types.Vm( memory=1073741824 ), next_run=True )

If the update can't be performed, for whatever the 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 isn't a service, just a container of data. In this particular example the returned object will be an instance of the ovirtsdk4.types.Vm class.

==== Using the remove methods

These service methods remove existing objects. They usually don't receive any parameters, as they are methods of the services that manage single objects, therefore the service already knows what object to remove.

For example, to remove the virtual machine with identifier 123:

[source,python]

Find the service that manages the virtual machine:

vm_service = vms_service.vm_service('123')

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, for virtual machines it is possible to remove the virtual machine while preserving the disks. To do so the remove method of the service that manages a virtual machine supports a http://ovirt.github.io/ovirt-engine-api-model/master/#services/vm/methods/remove[detach_only] boolean parameter:

[source,python]

Remove the virtual machine, but preserve the disks:

vm_service.remove(detach_only=True)

The remove methods return None if the object is removed successfully. It does not return the removed object. If the object can't removed, for whatever the reason, the SDK will raise an ovirtsdk4.Error exception containing the details of the failure.

==== Using action methods

These service methods perform miscellaneous operations. For example, the service that manages a virtual machine has methods to start and stop it:

[source,python]

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 http://ovirt.github.io/ovirt-engine-api-model/master/#services/vm/methods/start/parameters/use_cloud_init[use_cloud_init] parameter that indicates if you want to start it using https://cloudinit.readthedocs.io/cloud-init[cloud-init]:

[source,python]

Start the virtual machine:

vm_service.start(cloud_init=True)

Most action methods return None when they succeed, and raise a ovirtsdk4.Error when they fail. But a few action methods return values. For example, the service that manages a storage domains has an http://ovirt.github.io/ovirt-engine-api-model/master/#services/storage_domain/methods/is_attachedd[is_attached] action method that checks if the storage domain is already attached to a data center. That method returns a boolean:

[source,python]

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 http://ovirt.github.io/ovirt-engine-sdk[reference] documentation of the SDK to see the action methods supported by each service, the parameters that they support, and the values that they return.

== More information

The reference documentation of the API is available http://ovirt.github.io/ovirt-engine-api-model[here].

The reference documentation of the SDK is available http://ovirt.github.io/ovirt-engine-sdk[here].

There is a collection of examples that show how to use the SDK https://github.com/oVirt/ovirt-engine-sdk/tree/master/sdk/examples[here].