IaaS Hosting API

Introduction

The Gandi Hosting API provides a set of remote requests to manage your hosting account like the Gandi.net control panel does. You can manage your hosting devices and servers instances located in every Gandi datacenter from any location using the same API.

Connect to the API server

The Gandi Hosting API is provided through a set of XML-RPC calls.

>>> import xmlrpc.client
>>> api = xmlrpc.client.ServerProxy('https://rpc.gandi.net/xmlrpc/')
>>>
>>> apikey = 'my 24-character API key'
>>>
>>> # Now you can call API methods.
>>> # You must authenticate yourself by passing
>>> # the API key as the first method's argument
>>> version = api.version.info(apikey)

Note

In Python, use the xmlrpcclient module from the standard library.

<?php
// Library installed from PEAR
require_once 'XML/RPC2/Client.php';

// The first step is to connect to the API
$version_api = XML_RPC2_Client::create(
    'https://rpc.gandi.net/xmlrpc/',
    array( 'prefix' => 'version.', 'sslverify' => True )
);

// Warning !
// PEAR::XML_RPC2 checks the SSL certificate with Curl
// Curl has its own CA bundle so you may :
// * disable the 'sslverify' option: leads to security issue
// * enable the 'sslverify' option (default) and add the Gandi
// SSL certificate to the Curl bundle: best choice for security
// See: https://curl.haxx.se/docs/sslcerts.html

$apikey = 'my 24-character API key';

// Now you can call API method
// You must authenticate yourself by passing the API key
// as the first method's argument
$result = $version_api->info($apikey);

// Warning !
// PEAR::XML_RPC2 has known bugs on methods calls
// See https://pear.php.net/bugs/bug.php?id=13963
// You may use this call instead of the above one :
// $result = $version_api->__call("info", $apikey);

// dump the result
print_r($result);
?>

Note

In PHP 5, use the XML_RPC2 package from pear.

XML_RPC2 works with ‘prefix’ in order to bind to namespace. The ‘prefix’ isn’t editable, so you have to instanciante a client by namespace.

> var xmlrpc = require('xmlrpc')
> var api = xmlrpc.createSecureClient({
...  host: 'rpc.gandi.net',
...  port: 443,
...  path: '/xmlrpc/'
... })
>
> var apikey = 'my 24-character API key'
>
> // Now you can call API methods.
> // You must authenticate yourself by passing the API key
> // as the first method's argument
> api.methodCall('version.info', [apikey], function (error, value) {
...  console.dir(value)
... })

Note

With NodeJS, use the npm xmlrpc package.

use XML::RPC;

my $api = XML::RPC->new('https://rpc.gandi.net/xmlrpc/');

my $apikey = 'my 24-character API key';

# Now you can call API methods.
# You must authenticate yourself by passing the API key
# as the first method's argument
my $version = $api->call( 'version.info', $apikey );

Note

With perl, use the cpan xml::rpc package.

require 'xmlrpc/client'

server = XMLRPC::Client.new2('https://rpc.gandi.net/xmlrpc/')

apikey = 'my 24-character API key'

# Now you can call API methods.
# You must authenticate yourself by passing the API key
# as the first method's argument
version = server.call("version.info", apikey)

Note

With ruby, use the xmlrpc/client module from the standard library. Ruby does not support gzip by default, the ZlibParserDecorator is used to enabled with Ruby >1.9.

For older ruby version, neither set the http_header_extra nor the parser.

Note

To avoid RuntimeError with ruby >= 1.9, add:

XMLRPC::Config.module_eval {
    remove_const(:ENABLE_NIL_PARSER)
    const_set(:ENABLE_NIL_PARSER, true)
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <xmlrpc.h>
#include <xmlrpc_client.h>

#define CLIENT_NAME             "Documentation Client"
#define CLIENT_VERSION          "0.1"
#define CLIENT_USERAGENT        CLIENT_NAME "/" CLIENT_VERSION

#define SERVER_URL              "https://rpc.gandi.net/xmlrpc/"

int     client_connect(xmlrpc_env *);
void    client_check_fault(xmlrpc_env *);

int
main(int argc, char **argv)
{
        xmlrpc_env      env;
        xmlrpc_value    *apikey, *rv;

        client_connect(&env);

        apikey = xmlrpc_build_value(&env, "(s)", "my 24-character API key");
        rv = xmlrpc_client_call_params(&env, SERVER_URL, "version.info", apikey);
        client_check_fault(&env);

        xmlrpc_DECREF(rv);
        xmlrpc_DECREF(apikey);

        xmlrpc_env_clean(&env);
        xmlrpc_client_cleanup();

        return (0);
}

int
client_connect(xmlrpc_env *env)
{
        struct xmlrpc_clientparms clientp;
        struct xmlrpc_curl_xportparms curlp;

        curlp.network_interface = NULL;         /* use curl's default */
        curlp.ssl_verifypeer = 1;               /* Gandi API CA must be present */
        curlp.ssl_verifyhost = 2;
        curlp.user_agent = CLIENT_USERAGENT;    /* XML-RPC requirement */

        clientp.transport = "curl";
        clientp.transportparmsP = &curlp;
        clientp.transportparm_size = XMLRPC_CXPSIZE(user_agent);

        xmlrpc_env_init(env);
        xmlrpc_client_init2(env, XMLRPC_CLIENT_NO_FLAGS, CLIENT_NAME,
            CLIENT_VERSION, &clientp, XMLRPC_CPSIZE(transportparm_size));
        client_check_fault(env);

        return (1);
}

void
client_check_fault(xmlrpc_env *env)
{
        if (env->fault_occurred) {
                fprintf(stderr, "XML-RPC Fault: %s (%d)\n", env->fault_string,
                    env->fault_code);
                exit(1);
        }
}

Note

With C, use the xmlrpc-c library.

Overview

A server instance is represented by a virtual machine object using the class VM.

A VM object contains:

  • disks
  • network interfaces

Attributes Conventions

The attributes used by VM follow these conventions:

  • id or *_id: unique positive integer (datacenter_id for example) or

list of unique positive integers (ifaces_id for example) - name: unicode string - description: unicode string - signature: cryptographic hash or pgp signature

Virtual Machine Management

VM creation workflow

  1. Gandi credits
  2. Choose a datacenter
  3. Choose an operating system image
  4. Create the VM

Gandi Hosting provides device and server instances management in the cloud. The main object is the virtual machine or VM (server instance). You can attach devices like disks and network interfaces to a VM.

Note

The Hosting API only manages devices and servers instances. Billing (credits management) is only managed on the control panel (Gandi.net website).

Gandi credits

Creating hosting resources will consume Gandi Credits.

Your Gandi Credits balance is shared with all your hosting resources.

Gandi sells Credit Packages. You can list the current offers by using catalog.list() and you can acquire those through the API using hosting.account.credit().

Hosting Resources are priced in Gandi Credits and their prices are accessible through the hosting.catalog.list() method.

Once you have acquired Hosting Resources you may retrieve your hosting rate with:

>>> account = api.hosting.account.info(apikey)
>>> account['rating_enabled']
True

Choose a datacenter

Gandi hosts virtual servers in several datacenters.

You can obtain a list of Gandi datacenters via the hosting API.

>>> api.hosting.datacenter.list(apikey)
[{'country': 'France',
  'id': 1,
  'iso': 'FR',
  'name': 'Equinix Paris'},
 {'country': 'United States of America',
  'id': 2,
  'iso': 'US',
  'name': 'Level3 Baltimore'}]

As you can see in the previous exemple, the country in which the datacenter is located is listed in its attributes.

The following exemple shows how to filter the list of datacenters with an existing VM location in order to extract a list of datacenters located in the same country as the virtual machine

>>> fr_datacenters = [dc for dc in api.hosting.datacenter.list(apikey)
...     if dc['country'].lower() == myvm_description['datacenter'].lower()]
>>> fr_datacenters
[{'country': 'France', 'id': 1, 'iso': 'FR', 'name': 'Equinix Paris'}]
>>> dc_id = fr_datacenters[0]['id']

Choose an operating system image

Before you create a VM, you need to choose which OS disk image to use. Let’s consider you want to create a VM using an Ubuntu image:

>>> images = api.hosting.image.list(apikey, {'datacenter_id': 1})
>>> ubuntu_images = [x for x in images if x['label'].lower().startswith('ubuntu')]
>>> ubuntu_images
[{'author_id': 248842,
  'datacenter_id': 1,
  'date_created': <DateTime '20100101T00:00:00' at 25cd248>,
  'date_updated': <DateTime '20101007T06:43:17' at 25cd200>,
  'disk_id': 43024,
  'id': 15,
  'label': 'Ubuntu 9.10',
  'os_arch': 'x86-32',
  'visibility': 'all'},
 {'author_id': 248842,
  'datacenter_id': 1,
  'date_created': <DateTime '20100101T00:00:00' at 25cd2d8>,
  'date_updated': <DateTime '20101007T06:43:17' at 25cd290>,
  'disk_id': 45512,
  'id': 16,
  'label': 'Ubuntu 9.10 + ispCP',
  'os_arch': 'x86-32',
  'visibility': 'alpha'},
 {'author_id': 248842,
  'datacenter_id': 1,
  'date_created': <DateTime '20100101T00:00:00' at 25cd368>,
  'date_updated': <DateTime '20101007T06:43:17' at 25cd320>,
  'disk_id': 49300,
  'id': 17,
  'label': 'Ubuntu 9.10 + Cherokee',
  'os_arch': 'x86-32',
  'visibility': 'all'},
 {'author_id': 248842,
  'datacenter_id': 1,
  'date_created': <DateTime '20100101T00:00:00' at 25cd3f8>,
  'date_updated': <DateTime '20101007T06:43:17' at 25cd3b0>,
  'disk_id': 51848,
  'id': 18,
  'label': 'Ubuntu 10.04',
  'os_arch': 'x86-32',
  'visibility': 'all'}]

This returns a list of existing images whose name starts with ubuntu.

You can also filter the list with a version identifier:

>>> filter(lambda x: '10.04' in x['label'], ubuntu_images)
[{'author_id': 248842,
  'datacenter_id': 1,
  'date_created': <DateTime '20100101T00:00:00' at 25cd3f8>,
  'date_updated': <DateTime '20101007T06:43:17' at 25cd3b0>,
  'disk_id': 51848,
  'id': 18,
  'label': 'Ubuntu 10.04',
  'os_arch': 'x86-32',
  'visibility': 'all'}]

Now you can store for further use the identifier of this disk image:

>>> src_disk_id = 51848

Note

You may choose a disk image you created yourself and configured to fit your needs. It is easier to duplicate VMs this way.

Create the VM

In order to create a VM using Gandi API you can use several methods in the API Hosting module, such as VM.create() and VM.create_from().

>>> disk_spec = {
    'datacenter_id': dc_id,
    'name': myvm_description['disks'][0]}
>>> vm_spec = {
    'datacenter_id':dc_id,
    'hostname':     myvm_description['hostname'],
    'memory':       myvm_description['memory'],
    'cores':        myvm_description['cores'],
    'ip_version':   myvm_description['ifaces']['ip_version'],
    'bandwidth':    myvm_description['ifaces']['bandwidth'],
    'password':     'password_to_change'}
>>> op = api.hosting.vm.create_from(apikey, vm_spec, disk_spec, src_disk_id)

VM.create() takes an apikey and a specifications dictionnary as inputs. VM.create_from() accepts the previous arguments and also a dictionnary describing a Disk object to be created at the same time than the VM and also the Id of a Disk Image to copy to the newly create Disk object. This is used to create a new VM and install an image onto it ASAP.

Behind the scenes, the Gandi backend creates a new disk from the source disk. Then it will queue the VM creation via our Operation scheduler.

You can track your order using the Operation class.

A VM.create_from() Operation executes three steps:

  • disk_create
  • iface_create
  • vm_create

You can poll the operation status by calling operation.info():

>>> op2 = api.operation.info(apikey, op[2]['id'])
>>> vm_id = op2['params']['vm_id']
>>> vm_id
    63907

Note

in order to boot, a VM will use the cmdline defined with its first disk. With a linux disk image, cmdline may contain root=/dev/xvda1. This means the kernel will mount the first partition of the first disk as the root filesystem.

The provisioning process of a new VM automatically create a network interface. As our system tries to recycle IPs, if you have released an IP (by deleting the network interface that was using it), the next created network interface will get the old IP.

After the creation, the VM is automatically started.

Log into a VM

Log onto your newly started VM using ssh:

$ ssh user@vm_ip
    <enter password from vm_spec passed to vm.create>

Note

You may want to add an SSH public key for some users in ~<user>/.ssh/authorized_keys . Please refer to the system administration and security manual of the distribution. You may also add directly a public SSH key in the vm_spec object using the ssh_key parameter. vm_spec can manage password, SSH key or both authentications.

Update a VM

How to increase the amount of RAM available to a VM:

>>> op = api.hosting.vm.update(apikey, vm['id'], {'memory': 1024})

The following VM attributes can be updated:

  • hostname: a Unicode string up to 63 characters
  • memory: by step of 64MB between 256MB and 24576MB
  • vm_max_memory: 2048MB, 4096MB, 24576MB
  • cores: by unit between 1 and 12

The state is managed by VM.start(), VM.reboot() and VM.stop().

Note

vm_max_memory is used to limit the memory reserved by xen to manage memory allocation on the guest. It is an implementation-specific attribute we need to expose to the user. In practice, it defines the maximum memory the VM can manage. When a VM is created with less than 2048MB of memory, vm_max_memory is equal to 2048MB. If you want to increase memory beyond this limit you need to update vm_max_memory and reboot the VM.

Retrieve statistics usage for a VM

The VM is running on id 648 and you want to retrieve its network usage:

>>> query_spec = {
    'start': "2014-09-28 00:00:00",
    'end': "2014-09-29 23:59:59",
    'resource_id': [648],
    'query': 'vif.bytes.all',
    'resource_type': 'vm',
    'sampler': {'unit': 'hours', 'value': 1, 'function': 'max'}}
>>> api.hosting.metric.query(apikey, query_spec)
[{'direction': ['out'],
  'metric': 'vif.bytes',
  'points': [{'timestamp': '2014-11-12T00:00:00', 'value': 28.0},
             {'timestamp': '2014-11-12T01:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T02:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T03:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T04:00:00', 'value': 28.0},
             {'timestamp': '2014-11-12T05:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T06:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T07:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T08:00:00', 'value': 28.0},
             {'timestamp': '2014-11-12T09:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T10:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T11:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T12:00:00', 'value': 28.0},
             {'timestamp': '2014-11-12T13:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T14:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T15:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T16:00:00', 'value': 28.0},
             {'timestamp': '2014-11-12T17:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T18:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T19:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T20:00:00', 'value': 28.0},
             {'timestamp': '2014-11-12T21:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T22:00:00', 'value': 0.0},
             {'timestamp': '2014-11-12T23:00:00', 'value': 0.0}],
  'resource_id': 648,
  'resource_type': 'vm',
  'type': ['public']},
 {'direction': ['in'],
  'metric': 'vif.bytes',
  'points': [{'timestamp': '2014-11-12T00:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T01:00:00', 'value': 804.0},
             {'timestamp': '2014-11-12T02:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T03:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T04:00:00', 'value': 780.0},
             {'timestamp': '2014-11-12T05:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T06:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T07:00:00', 'value': 780.0},
             {'timestamp': '2014-11-12T08:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T09:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T10:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T11:00:00', 'value': 780.0},
             {'timestamp': '2014-11-12T12:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T13:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T14:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T15:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T16:00:00', 'value': 780.0},
             {'timestamp': '2014-11-12T17:00:00', 'value': 780.0},
             {'timestamp': '2014-11-12T18:00:00', 'value': 804.0},
             {'timestamp': '2014-11-12T19:00:00', 'value': 780.0},
             {'timestamp': '2014-11-12T20:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T21:00:00', 'value': 780.0},
             {'timestamp': '2014-11-12T22:00:00', 'value': 670.0},
             {'timestamp': '2014-11-12T23:00:00', 'value': 780.0}],
  'resource_id': 648,
  'resource_type': 'vm',
  'type': ['public']}]