Gandi

Table Of Contents

Previous topic

Gandi Hosting API

Next topic

IaaS Hosting API Reference

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 & servers instances in all Gandi datacenters 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 xmlrpclib
>>> api = xmlrpclib.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 xmlrpclib module from the standard library.

In Python 3 xmlrpclib has been renamed xmlrpc.client.

<?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: http://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 http://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'

class ZlibParserDecorator
  def initialize(parser)
    @parser = parser
  end
  def parseMethodResponse(responseText)
    @parser.parseMethodResponse(Zlib::GzipReader.new(StringIO.new(responseText)).read)
  end
  def parseMethodCall(*args)
    @parser.parseMethodCall(*args)
  end
end


server = XMLRPC::Client.new2('https://rpc.gandi.net/xmlrpc/')
server.http_header_extra = { "Accept-Encoding" => "gzip" }
server.set_parser ZlibParserDecorator.new(server.send(:parser))

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 with the object hosting.vm. A hosting.vm object contains:

  • disks
  • network interfaces

Attributes Conventions

The attributes respect the following 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

Steps to create a VM

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

Gandi Hosting provides devices & servers 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 & servers instances. Billing (credits management) is managed on the control panel (Gandi.net website).

Gandi credits

Note

Gandi has made the choice to drop the hosting.product API for many reasons. The introduction of credits is meant to simplify creation, management and renewal of your resources.

To create resources, you now just need credits, and your credits amount is shared by every hosting resource. To credit your account, just buy some via hosting.account.credit(). Credits are sold via packages, to get the pricing, use catalog.list() method. Then, to get resource prices, in credits, call the hosting.catalog.list(). Finally, watch your rating with hosting.rating.list().

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

Choose a datacenter

Gandi hosts virtual servers in several datacenters. At first, you need to choose where your servers instances will be:

>>> 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'}]

You can automate this by retrieving the datacenter’s country from the configuration file:

>>> 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 the disk image with the OS you want. Lets imagine you want to create a VM with 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'}]

It will return the list of all images with a name like (‘~’) ubuntu (case-insensitive). You can filter the list for the version:

>>> 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'}]

Let take the disk associated with this image to create our VM:

>>> 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

Create the VM from a vm_spec mapping with hosting.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)

hosting.vm.create_from() takes the VM specification as its second argument, the disk specification as its third and finally the source disk id. Behind the scenes, the Gandi backend creates a new disk from the source disk. When you ask Gandi to create a VM, the VM is not immediately available. It will start to work in Gandi’s backend to prepare the environment of the VM. This work is tracked with the Operation.

A VM create operation does 3 operations:

  • 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 process of VM creation will automatically create a network interface. When creating a network interface, the system tries to recycle an IP. If you have already 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

Once the VM has successfully started you can log in:

$ 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 operation system distribution. You may also add directly the public SSH key in the vm_spec object using the ssh_key parameter. The vm_spec can manage password, SSH key or both.

Update a VM

The VM is running and you want to increase its memory:

>>> 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 hosting.vm.start(), hosting.vm.reboot(), and hosting.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.