API documentation

The following documentation is based on the source code of version 0.12.2 of the negotiator project:

negotiator_host

Channel for communication with guests.

This module implements the GuestChannel class which provides the host side of the channel between QEMU hosts and guests. Channel objects can be used to query and command running guests.

class negotiator_host.AutomaticGuestChannel(guest_name, unix_socket)

Thin wrapper for GuestChannel that puts it in a separate process.

Uses multiprocessing.Process to isolate guest channels in separate processes.

__init__(guest_name, unix_socket)

Initialize a GuestChannel in a separate process.

Parameters:
  • guest_name – The name of the guest to connect to (a string).
  • unix_socket – The absolute pathname of the UNIX socket that we should connect to (a string).
run()

Start the main loop of the common negotiator interface.

class negotiator_host.GuestChannel(guest_name, unix_socket=None)

The host side of the channel connecting KVM/QEMU hosts and guests.

See also AutomaticGuestChannel which wraps GuestChannel and puts it in its own process.

__init__(guest_name, unix_socket=None)

Initialize a negotiator host agent.

Parameters:
  • guest_name – The name of the guest to connect to (a string).
  • unix_socket – The absolute pathname of the UNIX socket that we should connect to (a string, optional).
prepare_environment()

Prepare environment variables for command execution on KVM/QEMU hosts.

The following environment variables are currently exposed to commands:

$NEGOTIATOR_GUEST
The name of the KVM/QEMU guest that invoked the command.
exception negotiator_host.GuestChannelInitializationError

Exception raised by GuestChannel when socket initialization fails.

exception negotiator_host.GuestDiscoveryError

Exception raised by find_running_guests() when virsh list fails.

class negotiator_host.HostDaemon

The host daemon automatically manages a group of processes that handle “guest to host” calls.

__init__()

Initialize the host daemon.

cleanup_workers(running_guests)

Cleanup crashed workers and workers for guests that are no longer running.

enter_main_loop()

Create and maintain active channels for all running guests.

spawn_workers(running_guests)

Spawn new workers on demand (ignoring guests known not to support negotiator).

update_workers()

Automatically spawn subprocesses (workers) to maintain connections to all guests.

negotiator_host.find_channels_of_guest(guest_name)

Find the pathnames of the channels associated to a guest.

Parameters:guest_name – The name of the guest (a string).
Returns:A dictionary with channel names (strings) as keys and pathnames of UNIX socket files (strings) as values. If no channels are detected an empty dictionary will be returned.

This function uses virsh dumpxml and parses the XML output to determine the pathnames of the channels associated to the guest.

negotiator_host.find_running_guests()

Find the names of the guests running on the current host.

This function parses the output of the virsh list command instead of using the libvirt API because of two reasons:

  1. I’m under the impression that the libvirt API is still very much in flux and large changes are still being made, so it’s not the most stable foundation for Negotiator to find running guests.
  2. The Python libvirt API needs to match the version of the libvirt API on the host system and there is AFAIK no obvious way to express this in the setup.py script of Negotiator.
Returns:A generator of strings.
Raises:GuestDiscoveryError when virsh list fails.
negotiator_host.find_supported_guests()

Find guests supporting the negotiator interface.

Returns:A generator of strings with guest names.

This function uses find_running_guests() to determine which guests are currently running and then uses find_channels_of_guest() to determine which guests support the negotiator interface.

negotiator_host.cli

Usage: negotiator-host [OPTIONS] GUEST_NAME

Communicate from a KVM/QEMU host system with running guest systems using a guest agent daemon running inside the guests.

Supported options:

Option Description
-g, --list-guests List the names of the guests that have the appropriate channel.
-c, --list-commands List the commands that the guest exposes to its host.
-e, --execute=COMMAND Execute the given command inside GUEST_NAME. The standard output stream of the command inside the guest is intercepted and copied to the standard output stream on the host. If the command exits with a nonzero status code the negotiator-host program will also exit with a nonzero status code.
-t, --timeout=SECONDS Set the number of seconds before a remote call without a response times out. A value of zero disables the timeout (in this case the command can hang indefinitely). The default is 10 seconds.
-d, --daemon Start the host daemon that answers real time requests from guests.
-v, --verbose Increase logging verbosity (can be repeated).
-q, --quiet Decrease logging verbosity (can be repeated).
-h, --help Show this message and exit.
class negotiator_host.cli.Context

Enables main() to inject a custom timeout into partially applied actions.

__init__()

Initialize a context for executing commands on the host.

execute_command(guest_name, command_line)

Execute a command inside the named guest.

print_commands(guest_name)

Print the commands supported by the guest.

print_guest_names()

Print the names of the guests that Negotiator can connect with.

negotiator_host.cli.main()

Command line interface for the negotiator-host program.

negotiator_guest

The guest agent daemon and client.

This module implements the guest agent, the Python daemon process that’s always running inside KVM/QEMU guests.

class negotiator_guest.GuestAgent(character_device, retry=False)

Implementation of the daemon running inside KVM/QEMU guests.

__init__(character_device, retry=False)

Initialize a negotiator guest agent.

Parameters:
  • character_device – The absolute pathname of the character device that we should use to connect to the host (a string).
  • retryTrue to retry EBUSY errors, False otherwise (defaults to False).

Note

When retry is True it is (somewhat theoretically) possible for infinite retrying to cause control to never be returned to the caller. This is why callers are expected to use TimeOut or a similar solution.

raw_readline()

Read a newline terminated string from the remote side.

This method overrides the raw_readline() method of the NegotiatorInterface() class to implement blocking reads based on os.O_ASYNC and signal.SIGIO (see also WaitForRead).

Returns:The data read from the remote side (a string).
retry_open(character_device, mode)

Open the character device and retry EBUSY errors.

class negotiator_guest.WaitForRead(group=None, target=None, name=None, args=(), kwargs={})

Used by GuestAgent.raw_readline() to implement blocking reads.

run()

Endless loop that waits for one or more SIGIO signals to arrive.

signal_handler(signal_number, frame)

Signal handler for SIGIO signals that immediately exits the process.

negotiator_guest.find_character_device(port_name)

Find the character device for the given port name.

Parameters:port_name – The name of the virtio port (a string).
Returns:The absolute pathname of a character device (a string).
Raises:Exception when the character device cannot be found.

negotiator_guest.cli

Usage: negotiator-guest [OPTIONS]

Communicate from a KVM/QEMU guest system to its host or start the guest daemon to allow the host to execute commands on its guests.

Supported options:

Option Description
-l, --list-commands List the commands that the host exposes to its guests.
-e, --execute=COMMAND Execute the given command on the KVM/QEMU host. The standard output stream of the command on the host is intercepted and copied to the standard output stream on the guest. If the command exits with a nonzero status code the negotiator-guest program will also exit with a nonzero status code.
-d, --daemon Start the guest daemon. When using this command line option the “negotiator-guest” program never returns (unless an unexpected error condition occurs).
-t, --timeout=SECONDS Set the number of seconds before a remote call without a response times out. A value of zero disables the timeout (in this case the command can hang indefinitely). The default is 10 seconds.
-c, --character-device=PATH By default the appropriate character device is automatically selected based on /sys/class/virtio-ports/*/name. If the automatic selection doesn’t work, you can set the absolute pathname of the character device that’s used to communicate with the negotiator-host daemon running on the KVM/QEMU host.
-v, --verbose Increase logging verbosity (can be repeated).
-q, --quiet Decrease logging verbosity (can be repeated).
-h, --help Show this message and exit.
negotiator_guest.cli.main()

Command line interface for the negotiator-guest program.

negotiator_common

Common shared functionality between the negotiator host and guest.

This Python module contains the functionality that is shared between the negotiator-host and negotiator-guest packages. By moving all of the shared functionality to a separate Python package and using Python package dependencies to pull in the negotiator-common package we stimulate code reuse while avoiding code duplication.

class negotiator_common.NegotiatorInterface(handle, label)

Common logic shared between the host/guest components.

This class defines the protocol that’s used to communicate between the Python programs running on the hosts and guests.

__init__(handle, label)

Initialize a negotiator host or guest agent.

Parameters:
  • handle – A file like object connected to the other side.
  • label – A string describing the file like object (used in logging).

This constructor is intended to be called by sub classes to provide the base class with the context it needs to set up bidirectional communication between the host and guest agents.

call_remote_method(method, *args, **kw)

Call a method on the remote object.

Parameters:
  • method – The name of the method to call (a string).
  • args – The positional arguments for the method.
  • kw – The keyword arguments for the method.
Returns:

The return value of the remote method.

enter_main_loop()

Wait for requests from the other side.

The communication protocol for remote procedure calls is as follows:

  • Every request is a dictionary containing at least a command key with a string value (the name of the method to invoke).
  • The value of the optional arguments key gives a list of positional arguments to pass to the method.
  • The value of the optional keyword-arguments key gives a dictionary of keyword arguments to pass to the method.

Responses are structured as follows:

  • Every response is a dictionary containing at least a success key with a boolean value.
  • If success=True the key result gives the return value of the method.
  • If success=False the key error gives a string explaining what went wrong.
Raises:ProtocolError when the remote side violates the defined protocol.
execute(*command, **options)

Execute a user defined or built-in command.

Parameters:
  • command – The command name and any arguments (one or more strings).
  • input – The input to feed to the command on its standard input stream (a string or None).
Returns:

The output of the command (a string) or None if the command exited with a nonzero exit code.

list_commands()

Find the names of the user defined commands.

Returns:A list of executable names (strings).
prepare_environment()

Prepare environment variables for command execution.

This method can be overridden by sub classes to prepare environment variables for external command execution.

raw_read(num_bytes)

Read the given number of bytes from the remote side.

Parameters:num_bytes – The number of bytes to read (an integer).
Returns:The data read from the remote side (a string).
raw_readline()

Read a newline terminated string from the remote side.

Returns:The data read from the remote side (a string).
raw_write(data)

Write a string of data to the remote side.

Parameters:data – The data to write tot the remote side (a string).
read()

Wait for a JSON encoded message from the remote side.

The basic communication protocol is really simple:

  1. First an ASCII encoded integer number is received, terminated by a newline.
  2. Second the number of bytes given by step 1 is read and interpreted as a JSON encoded value. This step is not terminated by a newline.

That’s it :-).

Returns:The JSON value decoded to a Python value.
Raises:ProtocolError when the remote side violates the defined protocol.
write(value)

Send a Python value to the other side.

Parameters:value – Any Python value that can be encoded as JSON.
exception negotiator_common.ProtocolError

Exception that is raised when the communication protocol is violated.

exception negotiator_common.RemoteMethodFailed

Exception that is raised when a remote method call failed.

negotiator_common.config

Configuration defaults for the negotiator project.

negotiator_common.config.BUILTIN_COMMANDS_DIRECTORY = '/home/docs/checkouts/readthedocs.org/user_builds/negotiator/checkouts/latest/common/negotiator_common/scripts'

The directory with built-in commands (a string).

negotiator_common.config.DEFAULT_TIMEOUT = 10

The number of seconds to wait for a reply from the other side (an integer).

If more time elapses an exception is raised causing the process to exit with a nonzero status code.

negotiator_common.config.GUEST_TO_HOST_CHANNEL_NAME = 'negotiator-guest-to-host.0'

The name of the channel that’s used for communication initiated by the guest (a string).

negotiator_common.config.HOST_TO_GUEST_CHANNEL_NAME = 'negotiator-host-to-guest.0'

The name of the channel that’s used for communication initiated by the host (a string).

negotiator_common.config.SUPPORTED_CHANNEL_NAMES = ('negotiator-guest-to-host.0', 'negotiator-host-to-guest.0')

A tuple of strings with supported channel names (containing GUEST_TO_HOST_CHANNEL_NAME and HOST_TO_GUEST_CHANNEL_NAME).

negotiator_common.config.USER_COMMANDS_DIRECTORY = '/usr/lib/negotiator/commands'

The pathname of the directory containing user defined commands that ‘the other side’ can invoke through negotiator.

negotiator_common.utils

Miscellaneous functionality.

class negotiator_common.utils.GracefulShutdown

Context manager to enable graceful handling of SIGTERM.

This context manager translates termination signals (SIGTERM) into TerminationError exceptions.

__enter__()

Start intercepting termination signals.

__exit__(exc_type, exc_value, traceback)

Stop intercepting termination signals.

signal_handler(signum, frame)

Raise TerminationError when the timeout elapses.

exception negotiator_common.utils.TerminationError

Exception that is raised when SIGTERM is received.

class negotiator_common.utils.TimeOut(num_seconds)

Context manager that enforces timeouts using UNIX alarm signals.

__enter__()

Schedule the timeout.

__exit__(exc_type, exc_value, traceback)

Clear the timeout and restore the previous signal handler.

__init__(num_seconds)

Initialize the context manager.

Parameters:num_seconds – The number of seconds after which to interrupt the running operation (an integer).
signal_handler(signum, frame)

Raise TimeOutError when the timeout elapses.

exception negotiator_common.utils.TimeOutError

Exception raised by the TimeOut context manager.

negotiator_common.utils.format_call(function, *args, **kw)

Format a Python function call into a human readable string.

Parameters:
  • function – The name of the function that’s called (a string).
  • args – The positional arguments to the function (if any).
  • kw – The keyword arguments to the function (if any).