Connecting to the {virt-product-fullname} {engine-name}

The Connection class is the entry point of the software development kit. It provides access to the services of the {virt-product-fullname} {engine-name}'s REST API.

The parameters of the Connection class are:

  • url - Base URL of the {virt-product-fullname} {engine-name} API

  • username

  • password

  • ca_file - PEM file containing the trusted CA certificates. The ca.pem file is required when connecting to a server protected by TLS. If you do not specify the ca_file, the system-wide CA certificate store is used.

Connecting to the {virt-product-fullname} {engine-name}
connection = OvirtSDK4::Connection.new(
  url: 'https://engine.example.com/ovirt-engine/api',
  username: 'admin@internal',
  password: '...',
  ca_file: 'ca.pem',
)

The connection holds critical resources, including a pool of HTTP connections to the server and an authentication token. You must free these resources when they are no longer in use:

connection.close

The connection, and all the services obtained from it, cannot be used after the connection has been closed.

If the connection fails, the software development kit will raise an Error exception, containing details of the failure.

Listing Data Centers

This Ruby example lists the data centers.

# Get the reference to the root of the services tree:
system_service = connection.system_service

# Get the reference to the service that manages the
# collection of data centers:
dcs_service = system_service.data_centers_service

# Retrieve the list of data centers and for each one
# print its name:
dcs = dcs_service.list
dcs.each do |dc|
  puts dc.name
end

In an environment with only the Default data center, the example outputs:

Default

Listing Clusters

This Ruby example lists the clusters.

# Get the reference to the root of the services tree:
system_service = connection.system_service

# Get the reference to the service that manages the
# collection of clusters:
cls_service = system_service.clusters_service

# Retrieve the list of clusters and for each one
# print its name:
cls = cls_service.list
cls.each do |cl|
  puts cl.name
end

In an environment with only the Default cluster, the example outputs:

Default

Listing Logical Networks

This Ruby example lists the logical networks.

# Get the reference to the root of the services tree:
system_service = connection.system_service

# Get the reference to the service that manages the
# collection of networks:
nws_service = system_service.networks_service

# Retrieve the list of clusters and for each one
# print its name:
nws = nws_service.list
nws.each do |nw|
  puts nw.name
end

In an environment with only the default management network, the example outputs:

ovirtmgmt

Listing Hosts

This Ruby example lists the hosts.

# Get the reference to the root of the services tree:
system_service = connection.system_service

# Get the reference to the service that manages the
# collection of hosts:
host_service = system_service.hosts_service

# Retrieve the list of hosts and for each one
# print its name:
host = host_service.list
host.each do |host|
  puts host.name
end

In an environment with only one attached host (Atlantic) the example outputs:

Atlantic

Listing ISO Files in the ISO Storage Domain

This Ruby example lists the ISO files in the ISO storage domain, myiso.

# Get the reference to the root of the services tree:
system_service = connection.system_service

# Find the service that manages the collection of storage domains:
sds_service = system_service.storage_domains_service

# Find the ISO storage domain:
sd = sds_service.list(search: 'name=myiso').first

# Find the service that manages the ISO storage domain:
sd_service = sds_service.storage_domain_service(sd.id)

# Find the service that manages the collection of files available in the storage domain:
files_service = sd_service.files_service

# List the names of the files. Note that the name of the .iso file is contained
# in the `id` attribute.
files = files_service.list
files.each do |file|
  puts file.id
end

If you do not know the name of the ISO storage domain, this Ruby example retrieves all the ISO storage domains.

# Find the ISO storage domain:
iso_sds = sds_service.list.select { |sd| sd.type == OvirtSDK4::StorageDomainType::ISO }

Creating an NFS Storage Domain

This Ruby example adds an NFS storage domain.

# Get the reference to the root of the services tree:
system_service = connection.system_service

# Get the reference to the storage domains service:
sds_service = connection.system_service.storage_domains_service

# Create a new NFS data storage domain:
sd = sds_service.add(
  OvirtSDK4::StorageDomain.new(
    name: 'mydata',
    description: 'My data',
    type: OvirtSDK4::StorageDomainType::DATA,
    host: {
      name: 'myhost'
    },
    storage: {
      type: OvirtSDK4::StorageType::NFS,
      address: 'server0.example.com',
      path: '/nfs/ovirt/40/mydata'
    }
  )
)

# Wait until the storage domain is unattached:
sd_service = sds_service.storage_domain_service(sd.id)
loop do
  sleep(5)
  sd = sd_service.get
  break if sd.status == OvirtSDK4::StorageDomainStatus::UNATTACHED
end

Attaching a Storage Domain to a Data Center

This Ruby example attaches an existing NFS storage domain, mydata, to an existing data center, mydc. This example is used to attach both data and ISO storage domains.

# Get the reference to the root of the services tree:
system_service = connection.system_service

# Locate the service that manages the storage domains and use it to
# search for the storage domain:
sds_service = system_service.storage_domains_service
sd = sds_service.list(search: 'name=mydata')[0]

# Locate the service that manages the data centers and use it to
# search for the data center:
dcs_service = system_service.data_centers_service
dc = dcs_service.list(search: 'name=mydc')[0]

# Locate the service that manages the data center where you want to
# attach the storage domain:
dc_service = dcs_service.data_center_service(dc.id)

# Locate the service that manages the storage domains that are attached
# to the data centers:
attached_sds_service = dc_service.storage_domains_service

# Use the "add" method of service that manages the attached storage
# domains to attach it:
attached_sds_service.add(
  OvirtSDK4::StorageDomain.new(
    id: sd.id
  )
)

# Wait until the storage domain is active:
attached_sd_service = attached_sds_service.storage_domain_service(sd.id)
loop do
  sleep(5)
  sd = attached_sd_service.get
  break if sd.status == OvirtSDK4::StorageDomainStatus::ACTIVE
end

Creating a Virtual Machine

This Ruby example creates a virtual machine. This example uses a hash with symbols and nested hashes as their values. Another method, more verbose, is to use the constructors of the corresponding objects directly. See Creating a Virtual Machine Instance with Attributes for more information.

# Get the reference to the "vms" service:
vms_service = connection.system_service.vms_service

# Use the "add" method to create a new virtual machine:
vms_service.add(
  OvirtSDK4::Vm.new(
    name: 'myvm',
    cluster: {
      name: 'mycluster'
    },
    template: {
      name: 'Blank'
    }
  )
)

After creating a virtual machine, it is recommended to poll the virtual machine’s status, to ensure that all the disks have been created.

Creating a vNIC Profile

This Ruby example creates a vNIC profile.

# Find the root of the tree of services:
system_service = connection.system_service

# Find the network where you want to add the profile. There may be multiple
# networks with the same name (in different data centers, for example).
# Therefore, you must look up a specific network by name, in a specific data center.
dcs_service = system_service.data_centers_service
dc = dcs_service.list(search: 'name=mydc').first
networks = connection.follow_link(dc.networks)
network = networks.detect { |n| n.name == 'mynetwork' }

# Create the vNIC profile, with passthrough and port mirroring disabled:
profiles_service = system_service.vnic_profiles_service
profiles_service.add(
  OvirtSDK4::VnicProfile.new(
    name: 'myprofile',
    pass_through: {
      mode: OvirtSDK4::VnicPassThroughMode::DISABLED,
    },
    port_mirroring: false,
    network: {
      id: network.id
    }
  )
)

Creating a vNIC

To ensure that a newly created virtual machine has network access you must create and attach a vNIC.

This Ruby example creates a vNIC and attaches it to an existing virtual machine, myvm.

# Find the root of the tree of services:
system_service = connection.system_service

# Find the virtual machine:
vms_service = system_service.vms_service
vm = vms_service.list(search: 'name=myvm').first

# In order to specify the network that the new NIC will be connected to, you must
# specify the identifier of the vNIC profile. However, there may be multiple
# profiles with the same name (for different data centers, for example), so first
# you must find the networks that are available in the cluster that the
# virtual machine belongs to.
cluster = connection.follow_link(vm.cluster)
networks = connection.follow_link(cluster.networks)
network_ids = networks.map(&:id)

# Now that you know what networks are available in the cluster, you can select a
# vNIC profile that corresponds to one of those networks, and has the
# name that you want to use. The system automatically creates a vNIC
# profile for each network, with the same name as the network.
profiles_service = system_service.vnic_profiles_service
profiles = profiles_service.list
profile = profiles.detect { |p| network_ids.include?(p.network.id) && p.name == 'myprofile' }

# Locate the service that manages the network interface cards collection of the
# virtual machine:
nics_service = vms_service.vm_service(vm.id).nics_service

# Add the new network interface card:
nics_service.add(
  OvirtSDK4::Nic.new(
    name: 'mynic',
    description: 'My network interface card',
    vnic_profile: {
      id: profile.id
    }
  )
)

Creating a Virtual Disk

To ensure that a newly created virtual machine has access to persistent storage you must create and attach a disk.

This Ruby example creates and attaches a virtual storage disk to a virtual machine.

# Locate the virtual machines service and use it to find the virtual
# machine:
vms_service = connection.system_service.vms_service
vm = vms_service.list(search: 'name=myvm')[0]

# Locate the service that manages the disk attachments of the virtual
# machine:
disk_attachments_service = vms_service.vm_service(vm.id).disk_attachments_service

# Use the "add" method of the disk attachments service to add the disk.
# Note that the size of the disk, the `provisioned_size` attribute, is
# specified in bytes, so to create a disk of 10 GiB the value should
# be 10 * 2^30.
disk_attachment = disk_attachments_service.add(
  OvirtSDK4::DiskAttachment.new(
    disk: {
      name: 'mydisk',
      description: 'My disk',
      format: OvirtSDK4::DiskFormat::COW,
      provisioned_size: 10 * 2**30,
      storage_domains: [{
        name: 'mydata'
      }]
    },
    interface: OvirtSDK4::DiskInterface::VIRTIO,
    bootable: false,
    active: true
  )
)

# Wait until the disk status is OK:
disks_service = connection.system_service.disks_service
disk_service = disks_service.disk_service(disk_attachment.disk.id)
loop do
  sleep(5)
  disk = disk_service.get
  break if disk.status == OvirtSDK4::DiskStatus::OK
end

Attaching an ISO Image to a Virtual Machine

This Ruby example attaches a CD-ROM to a virtual machine and changes it to an ISO image in order to install the guest operating system.

# Get the reference to the "vms" service:
vms_service = connection.system_service.vms_service

# Find the virtual machine:
vm = vms_service.list(search: 'name=myvm')[0]

# Locate the service that manages the virtual machine:
vm_service = vms_service.vm_service(vm.id)

# Locate the service that manages the CDROM devices of the VM:
cdroms_service = vm_service.cdroms_service

# List the first CDROM device:
cdrom = cdroms_service.list[0]

# Locate the service that manages the CDROM device you just found:
cdrom_service = cdroms_service.cdrom_service(cdrom.id)

# Change the CD of the VM to 'my_iso_file.iso'. By default this
# operation permanently changes the disk that is visible to the
# virtual machine after the next boot, but it does not have any effect
# on the currently running virtual machine. If you want to change the
# disk that is visible to the current running virtual machine, change
# the `current` parameter's value to `true`.
cdrom_service.update(
  OvirtSDK4::Cdrom.new(
    file: {
      id: 'CentOS-7-x86_64-DVD-1511.iso'
    }
  ),
  current: false
)

Detaching a Virtual Disk

This Ruby example detaches a disk from a virtual machine.

# Find the virtual machine:
vm = vms_service.list(search: 'name=myvm').first

# Detach the first disk from the virtual machine:
vm_service = vms_service.vm_service(vm.id)
attachments_service = vm_service.disk_attachments_service
attachment = attachments_service.list.first

# Remove the attachment. The default behavior is that the disk is detached
# from the virtual machine, but not deleted from the system. If you wish to
# delete the disk, change the `detach_only` parameter to `false`.
attachment.remove(detach_only: true)

Starting a Virtual Machine

This Ruby example starts a virtual machine.

# Get the reference to the "vms" service:
vms_service = connection.system_service.vms_service

# Find the virtual machine:
vm = vms_service.list(search: 'name=myvm')[0]

# Locate the service that manages the virtual machine, as that is where
# the action methods are defined:
vm_service = vms_service.vm_service(vm.id)

# Call the "start" method of the service to start it:
vm_service.start

# Wait until the virtual machine status is UP:
loop do
  sleep(5)
  vm = vm_service.get
  break if vm.status == OvirtSDK4::VmStatus::UP
end

Starting a Virtual Machine with Specific Boot Devices and Boot Order

This Ruby example starts a virtual machine specifying the boot devices and boot order.

# Find the root of the tree of services:
system_service = connection.system_service

# Find the virtual machine:
vms_service = system_service.vms_service
vm = vms_service.list(search: 'name=myvm').first

# Find the service that manages the virtual machine:
vm_service = vms_service.vm_service(vm.id)

# Start the virtual machine explicitly indicating the boot devices and order:
vm_service.start(
  vm: {
    os: {
      boot: {
        devices: [
          OvirtSDK4::BootDevice::NETWORK,
          OvirtSDK4::BootDevice::CDROM
        ]
      }
    }
  }
)

# Wait until the virtual machine is up:
loop do
  sleep(5)
  vm = vm_service.get
  break if vm.status == OvirtSDK4::VmStatus::UP
end

Starting a Virtual Machine with Cloud-Init

This Ruby example starts a virtual machine using the Cloud-Init tool to set the root password and network configuration.

# Find the virtual machine:
vms_service = connection.system_service.vms_service
vm = vms_service.list(search: 'name=myvm')[0]

# Find the service that manages the virtual machine:
vm_service = vms_service.vm_service(vm.id)

# Create a cloud-init script to execute in the
# deployed virtual machine. The script must be correctly
# formatted and indented because it uses YAML.
my_script = "
write_files:
  - content: |
      Hello, world!
    path: /tmp/greeting.txt
    permissions: '0644'
"

# Start the virtual machine, enabling cloud-init and providing the
# password for the `root` user and the network configuration:
vm_service.start(
  use_cloud_init: true,
  vm: {
    initialization: {
      user_name: 'root',
      root_password: 'redhat123',
      host_name: 'myvm.example.com',
      nic_configurations: [
        {
          name: 'eth0',
          on_boot: true,
          boot_protocol: OvirtSDK4::BootProtocol::STATIC,
          ip: {
            version: OvirtSDK4::IpVersion::V4,
            address: '192.168.0.100',
            netmask: '255.255.255.0',
            gateway: '192.168.0.1'
          }
        }
      ],
      dns_servers: '192.168.0.1 192.168.0.2 192.168.0.3',
      dns_search: 'example.com',
      custom_script: my_script
    }
  }
)

Checking System Events

This Ruby example retrieves logged system events.

# In order to ensure that no events are lost, it is recommended to write
# the index of the last processed event, in persistent storage.
# Here, it is stored in a file called `index.txt`. In a production environment,
# it will likely be stored in a database.
INDEX_TXT = 'index.txt'.freeze

def write_index(index)
  File.open(INDEX_TXT, 'w') { |f| f.write(index.to_s) }
end

def read_index
  return File.read(INDEX_TXT).to_i if File.exist?(INDEX_TXT)
  nil
end

# This is the function that is called to process the events. It prints
# the identifier and description of each event.
def process_event(event)
  puts("#{event.id} - #{event.description}")
end

# Find the root of the tree of services:
system_service = connection.system_service

# Find the service that manages the collection of events:
events_service = system_service.events_service

# If no index is stored yet, retrieve the last event and start with it.
# Events are ordered by index, in ascending order. `max=1` retrieves only one event,
# the last event.
unless read_index
  events = events_service.list(max: 1)
  unless events.empty?
    first = events.first
    process_event(first)
    write_index(first.id.to_i)
  end
end

# This loop retrieves the events, always starting from the last index. It waits
# before repeating. The `from` parameter specifies that you want to retrieve
# events that are newer than the last index that was processed. Note: the `max`
# parameter is not used, so that all pending events will be retrieved.
loop do
  sleep(5)
  events = events_service.list(from: read_index)
  events.each do |event|
    process_event(event)
    write_index(event.id.to_i)
  end
end