Authors:

Feature pages are design documents that developers have created while collaborating on oVirt.

Most of them are outdated, but provide historical design context.

They are not user documentation and should not be treated as such.

Documentation is available here.

Feature Name

API Link Following

Summary

This feature enables oVirt API users to request that the contents of some of the entity’s links be returned inline, inside the requested entity.

Owner

Detailed Description

Motivation:

Currently, when there is a need to retrieve multiple related objects from the API, the only option is to retrieve the first one, and then send additional requests to retrieve the related objects. For example, if you need a virtual machine and also the disks and NICs, first you need to send a request like this:

GET /ovirt-engine/api/vms/123

And then additional requests to get the disk attachments, the disks, and the NICs:

GET /ovirt-engine/api/vms/123/diskattachments

GET /ovirt-engine/api/disks/456

GET /ovirt-engine/api/disks/789

GET /ovirt-engine/api/vms/123/nics

In an environment with high latency this multiplies the time required to retrieve the data. In addition, it means that multiple queries have to be sent to the database to retrieve the data.

To improve these two areas the new follow parameter will be introduced. This parameter will be a list of links that the server should follow and populate. For example, the previous scenario will be solved sending this request:

GET /ovirt-engine/api/vms/123?follow=diskattachments.disks,nics

That will return the virtual machine with the disks and the NICs embedded in the same response, thus avoiding the multiple network round-trips.

The multiple database queries will be avoided only if the server is modified to retrieve that data with more efficient queries. Otherwise the server will use the naive approach of calling itself to retrieve it, which won’t improve the number of queries.

The first step is analyzing the value of follow and using it to fetch the desired hrefs - the actual links to be followed. Single entities contain their href directly inside them (e.g: vm.getCluster().getHref()), and hrefs of sub-collections (e.g: nics, disks) are stored in the links collection (1. vm.getLinks() 2. Find specific link. 3.link.getHref()). This division exists for historic reasons.

This requires the value cluster from follow=cluster,... to be translated to: vm.getCluster(). This is done by string manipulation. Ideally, an explicit annotation on the getter method would be used: @Link(name="cluster", method="getter"). However it’s impossible to add such an annotation because the generation of business-entities is done by a third-party (the xjc compiler).

The href attribute of the returned entity/entities is used to fetch it/them. This is done by crawling along the API tree to the context where the information can be retrieved.

Crawling along the API tree is done using ServiceTree class, which was enhanced to provide this kind of service (https://gerrit.ovirt.org/#/c/76077/).

After the information has been retrieved it needs to be set in the original entity (for example, vm.setCluster(cluster). Getting from follow=cluster,… to vm.setCluster() is done by string manipulation, but would ideally be done using an annotation.

All business-logic described above is placed in: follow(ActionableResource entity) in BaseBackendResource - the parent class for all API resources.

This method is transparently invoked. To achieve transparent invocation, generated Jax-rs interfaces were made to include doGet(), doList() methods, and jax-rs @GET annotations was moved to them, making GET requests land there. These methods have a default implementation which invokes follow():

@GET default public Host doGet(String id) { Host host = get(); follow(host); return host; }

This follow() method is final, purposely forbidding developers from overriding it. But as part of its flow it invokes another signature of follow(). This method is the entry point for developers who need to add the specific following logic.

For cases like GET ...api/vms?follow="a,b,c", when developer adds specific logic for following a, his code should include:

  1. Check if a exists in follow url parameter.
  2. Follow a and insert results into the vm
  3. Mark a as followed.

Example: get VMs with Nics. • Class: BackendVmsResource • Override follow( ActionableResource entity, LinksTreenode linksTree ) Decide whether to intervene: Boolean exists = linksTree.pathExists(“nics”) Run engine query: getBackendCollection(VdcQueryType.GetVmsWithNics). Map and set data Mark followed=true: linksTree.markAsFollowed(“nics”)

Prerequisites

None

Limitations

The generic solution is naive and costly in terms of performance. Frequent cases requiring link-following should be handled manually (the infrastructure provides an intervention point).

Benefit to oVirt

This feature saves the need for client-side scripts for following links, and enables running designated queries for common scenarios (e.g: get vms+nics+disks).

User Experience

Commas separate the links that are to be followed: GET .../api/vms?follow=nics,disk_attachments

Dots denote levels of depth. Limitless levels of following possible: GET ...api/vms?follow=disk_attachments.template

Combinations possible: GET .../api/vms?follow=nics.x, nics.y