Skip to content

Adding a new cloud provider

Klaus Weidner edited this page Jun 23, 2015 · 3 revisions

Adding a new cloud provider

Requirements and assumptions

The cloud provider must provide a command line tool or Python-scriptable API suitable for non-interactive use, including appropriate error or status reporting.

This document is currently focused on setup for Linux-based virtual machines. TODO(klausw): update for Windows.

Required tool/API features:

  • Create a new VM using a specified boot disk image such as Ubuntu 14.04, and retrieve its assigned IP address(es).

  • Create a non-root user (perfkit) for Linux VMs:

    • passwordless SSH access using a provided public key
    • passwordless sudo capability
  • The guest OS must be able to download data from the Internet, for example to fetch distribution tarballs for benchmarks.

  • Destroy a VM.

Optional but useful tool/API features:

  • Configure a firewall. (A simple "permit everything" static config works also.)

  • Create / attach / destroy additional block storage devices for use as scratch disks. This is optional if the standard boot disk has sufficient free space for running benchmarks.

  • Check existence of a specific virtual machine or resource. This is useful for automatically retrying failed creation / deletion requests.

  • Use a package manager to add dependencies. Alternatively, a custom base image could be created that contains all needed software.

Overview

Briefly, adding support for a new cloud provider in PerfKitBenchmarker needs the following steps:

  1. Create implementation classes for virtual machine / disk / network resources.
  2. If needed, add custom package installation support.
  3. Edit perfkitbenchmarker/benchmark_spec.py to integrate the new implementation.
  4. Update README.md to document the new provider.

As an example, see this commit which adds support for the DigitalOcean provider.

Create resource implementation classes

PerfKitBenchmarker is based around a hierarchy of resource classes. Incomplete example for illustration:

BaseResource
│
├── BaseDisk
│   │
│   ├─ StripedDisk
│   ├─ AwsDisk
│   ├─ AzureDisk
│   └─ GceDisk
│
├── BaseFirewall
│
├── BaseNetwork
│
├── BaseVirtualMachine
│   │
│   ├─ AwsVirtualMachine
│   │  ├─ DebianBasedAwsVirtualMachine
│   │  └─ RhelBasedAwsVirtualMachine
│   │
│   └─ GceVirtualMachine
│      ├─ DebianBasedGceVirtualMachine
│      └─ RhelBasedGceVirtualMachine
│
├── AzureAffinityGroup
├── AzureStorageAccount
└── AzureVirtualNetwork

At the most basic level, each resource must support a Create and Delete method to implement a basic life cycle. The default implementations for these methods in BaseResource split this up into individual steps, with built-in retry logic:

  def Create(self):
    self._CreateDependencies()
    self._CreateResource()
    self._PostCreate()

  def Delete(self):
    self._DeleteResource()
    self._DeleteDependencies()

  @vm_util.Retry(retryable_exceptions=(errors.Resource.RetryableCreationError,))
  def _CreateResource(self):
    self._Create()
    try:
      if not self._Exists():
        raise errors.Resource.RetryableCreationError(...)
    except NotImplementedError:
      pass

To follow this life cycle, your subclass should implement the _Create and _Delete methods. _Exists is optional but highly recommended for robustness.

If a virtual machine requires additional resources that need to be created and destroyed separately, you should handle them through separate resource classes that are managed as dependencies for the parent resource. This helps ensure that they are consistently cleaned up when appropriate. Examples include IP addresses, networks, or scratch disks that are not automatically included as part of the base VM.

Package installation support

PerfKitBenchmarker provides convenient mixins for Debian (Apt-based) and RHEL (Yum-based) installers:

class DebianBasedGceVirtualMachine(GceVirtualMachine, linux_virtual_machine.DebianMixin):
  pass


class RhelBasedGceVirtualMachine(GceVirtualMachine, linux_virtual_machine.RhelMixin):
  pass

Adding support for additional installers is more complex. In brief, this would require adding new {name}Install(vm) toplevel functions to all supported packages in perfkitbenchmarker/packages/, and then adding a new mixin class to perfkitbenchmarker/linux_virtual_machine.py which uses the corresponding {name}Install(vm) functions.

benchmark_spec.py integration

See the current examples. Briefly:

  • import the cloud-specific resource classes
  • add new sections to the DEFAULTS and CLASSES dictionaries
  • add the new cloud name to the 'cloud' enum flag.

README.md documentation

Lastly, please update instructions in the toplevel README.md file to explain how to use pkb.py for the new cloud provider. This is especially important for required cloud-specific initialization such as setting up accounts and authentication.