CloudStack Compute Driver Documentation

CloudStack is an Apache Software Foundation open source software designed to deploy and manage large networks of virtual machines, as a highly available, highly scalable Infrastructure as a Service (IaaS) cloud computing platform. CloudStack is used by a number of service providers to offer public cloud services, and by many companies to provide an on-premises (private) cloud offering, or as part of a hybrid cloud solution.

../../_images/cloudstack.png

CloudStack has its own non-standard API , libcloud provides a Python wrapper on top of this API with common methods with other IaaS solutions and Public cloud providers. Therefore, you can use use the CloudStack libcloud driver to communicate with your local CloudStack based private cloud as well as CloudStack based public clouds.

Instantiating a driver

When you instantiate a driver you need to pass the following arguments to the driver constructor:

  • key - Your CloudStack API key
  • secret - Your CloudStack secret key
  • host - The host of your CloudStack endpoint (e.g localhost for http://localhost:8080/client/api)
  • path - The path to your CloudStack endpoint (e.g /client/api for http://localhost:8080/client/api)
  • url - The url to your CloudStack endpoint, mutually exclusive with host and path
  • secure - True or False. True by default

Typically this will lead to:

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

apikey = 'your api key'
secretkey = 'your secret key'
host = 'example.com'
path = '/path/to/api'

Driver = get_driver(Provider.CLOUDSTACK)
conn = Driver(key=apikey, secret=secretkey, host=host, path=path)

A complete url can be used instead:

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

apikey = 'your api key'
secretkey = 'your secret key'
url = 'http://example.com/path/to/api'

Driver = get_driver(Provider.CLOUDSTACK)
conn = Driver(key=apikey, secret=secretkey, url=url)

In the testing scenario where you are running CloudStack locally, the connection may be insecure and you may run it on a specific port. In that case, the instantiation would look like this

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

apikey = 'your api key'
secretkey = 'your secret key'
host = 'localhost'
path = '/path/to/api'
port = 8080

Driver = get_driver(Provider.CLOUDSTACK)
conn = Driver(key=apikey, secret=secretkey, host=host,
              path=path, port=port, secure=False)

If you are making a connection to a secure cloud that does not use a trusted certificate, you will have to disable the SSL verification like so:

import libcloud.security as sec
sec.VERIFY_SSL_CERT = False

For more information on how SSL certificate validation works in Libcloud, see the SSL Certificate Validation page.

libcloud now features CloudStack based drivers for the exoscale and ikoula public clouds. Instantiating drivers to those clouds is shown in the example section below.

The base libcloud API allows you to:

  • list nodes, images, instance types, locations
  • list, create, attach, detach, delete volumes

Non-standard functionality and extension methods

The CloudStack driver exposes a lot of libcloud non-standard functionalities through extension methods and arguments.

These functionalities include:

  • start and stop a node
  • list disk offerings
  • list networks
  • list, allocate and release public IPs,
  • list, create and delete port forwarding rules
  • list, create and delete IP forwarding rules
  • list, create, delete and authorize security groups

Some methods are only valid for CloudStack advanced zones, while others are suited for basic zones.

For information on how to use these functionalities please see the method docstrings below. You can also use an interactive shell for exploration as shown in the examples.

Basic Zone Examples

To start experimenting with libcloud, starting an ipython interactive shell can be very handy. Tab completion and shell history are available. Below is an example of starting such an interactive shell for the exoscale public cloud. Once started you can explore the libcloud API.

1. Start an interactive shell on Exoscale public cloud

import os

from IPython.terminal.embed import InteractiveShellEmbed

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

apikey = os.getenv('EXOSCALE_API_KEY')
secretkey = os.getenv('EXOSCALE_SECRET_KEY')

Driver = get_driver(Provider.EXOSCALE)

conn = Driver(key=apikey, secret=secretkey)

shell = InteractiveShellEmbed(banner1='Hello from Libcloud Shell !!')
shell()

After experimenting through an interactive shell, you can write scripts that will directly execute libcloud commands. For instance starting a node with a specific ssh keypair and a couple of security groups can be done as shown in the following example:

2. SSH Keypairs management on Exoscale public cloud

The base libcloud API has been extended to handle management of ssh keypairs. This is very useful for CloudStack basic zones. SSH Keypairs, can be listed, created, deleted and imported. This new base API is only available in libcloud trunk.

import os
from pprint import pprint

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

cls = get_driver(Provider.EXOSCALE)
driver = cls('api key', 'api secret key')

# Create a new key pair. Most providers will return generated private key in
# the response which can be accessed at key_pair.private_key
key_pair = driver.create_key_pair(name='test-key-pair-1')
pprint(key_pair)

# Import an existing public key from a file. If you have public key as a
# string, you can use import_key_pair_from_string method instead.
key_file_path = os.path.expanduser('~/.ssh/id_rsa_test.pub')
key_pair = driver.import_key_pair_from_file(name='test-key-pair-2',
                                            key_file_path=key_file_path)
pprint(key_pair)

# Retrieve information about previously created key pair
key_pair = driver.get_key_pair(name='test-key-pair-1')
pprint(key_pair)

# Delete a key pair we have previously created
status = driver.delete_key_pair(key_pair=key_pair)
pprint(status)

3. Security Groups management on Exoscale public cloud

Currently there is no security group class defined, hence the result of ex_list_securitry_groups() is a list of dictionaries and not classes.

from pprint import pprint

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

cls = get_driver(Provider.EXOSCALE)
driver = cls('api key', 'api secret key')

# List all security groups
sg = driver.ex_list_security_groups()
pprint(sg)

# Create a new security group.
security_group = driver.ex_create_security_group(name='test-security-group')
pprint(security_group)

# Authorize an ingress rule on a security group
# If `startport` is used alone, this will be the only port open
# If `endport` is also used then the entire range will be authorized
sg = driver.ex_authorize_security_group_ingress(
    securitygroupname='test-security-group', protocol='tcp', startport='22',
    cidrlist='0.0.0.0/0'
)

pprint(sg)

# Delete a security group we have previously created
status = driver.ex_delete_security_group(name='test-security-group')
pprint(status)

4. Create a node with a keypair and a list of security groups

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

ACCESS_ID = 'your access id'
SECRET_KEY = 'your secret key'
HOST = 'hostname or ip address of your management server'
PATH = 'path to the api endpoint, e.g: /client/api'

SIZE_ID = 'id of the computer offering you want to use'
IMAGE_ID = 'id of the template you want to use'

# Name of the existing keypair you want to use
KEYPAIR_NAME = 'keypairname'

# The security groups you want this node to be added to
SECURITY_GROUP_NAMES = ['secgroup1', 'secgroup2']

cls = get_driver(Provider.CLOUDSTACK)
driver = cls(key=ACCESS_ID, secret=SECRET_KEY, secure=True,
             host=HOST, path=PATH)

sizes = driver.list_sizes()
images = driver.list_images()
size = [s for s in sizes if s.id == SIZE_ID][0]
image = [i for i in images if i.id == IMAGE_ID][0]

node = driver.create_node(name='test-node-1', image=image, size=size,
                          ex_security_groups=SECURITY_GROUP_NAMES,
                          ex_keyname=KEYPAIR_NAME)

5. Deploying a node with a keypair

Executing deployment scripts when creating node is currently only supported in basic zones. The deploy_node method is used instead of the create_node, ssh key are passed as arguments as well as a list of scripts.

from pprint import pprint

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

# Import the deployment specific modules
from libcloud.compute.deployment import ScriptDeployment
from libcloud.compute.deployment import MultiStepDeployment

cls = get_driver(Provider.EXOSCALE)
driver = cls('api key', 'api secret key')

image = driver.list_images()[0]
size = driver.list_sizes()[0]

# Define the scripts that you want to run during deployment
script = ScriptDeployment('/bin/date')
msd = MultiStepDeployment([script])

node = driver.deploy_node(name='test', image=image, size=size,
                          ssh_key='~/.ssh/id_rsa_test',
                          ex_keyname='test-keypair',
                          deploy=msd)

# The stdout of the deployment can be checked on the `script` object
pprint(script.stdout)

Advanced Zone examples

Advanced zones in CloudStack provide tenant isolation via VLANs or SDN technologies like GRE/STT meshes. In a typical advanced zones, users will deploy nodes on a private network and will use NAT to access their nodes. Therefore one needs to specify the network a node needs to be deployed on, and needs to setup port forwarding or IP forwarding rules.

1. Start an interactive shell on Ikoula public cloud

Instantiation of driver for an advanced zone is the same as with a basic zone, for example on the ikoula cloud:

import os

from IPython.terminal.embed import InteractiveShellEmbed

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

apikey = os.getenv('IKOULA_API_KEY')
secretkey = os.getenv('IKOULA_SECRET_KEY')

Driver = get_driver(Provider.IKOULA)

conn = Driver(key=apikey, secret=secretkey)

shell = InteractiveShellEmbed(banner1='Hello from Libcloud Shell !!')
shell()

2. Create a node on a guest network and allocate an IP

Starting a node requires a specific guest network.

from pprint import pprint

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

apikey = 'your api key'
secretkey = 'your secret key'

Driver = get_driver(Provider.IKOULA)
driver = Driver(key=apikey, secret=secretkey)

# This returns a list of CloudStackNetwork objects
nets = driver.ex_list_networks()

# List the images/templates available
# This returns a list of NodeImage objects
images = driver.list_images()

# List the instance types
# This returns a list of NodeSize objects
sizes = driver.list_sizes()

# Create the node
# This returns a Node object
node = driver.create_node(name='libcloud', image=images[0],
                          size=sizes[0], networks=[nets[0]])

# The node has a private IP in the guest network used
# No public IPs and no rules
pprint(node.extra)
pprint(node.private_ips)

3. List, create and delete a Port forwarding rule

To access the node via ssh you need you can create a port forwarding rule like so:

from pprint import pprint

from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver

cls = get_driver(Provider.EXOSCALE)
driver = cls('api key', 'api secret key')

# Allocate a public IP
# This returns a CloudStackAddress object
driver.ex_allocate_public_ip()

# You can now see this address when listing public IPs
ip = driver.ex_list_public_ips()[0]

node = driver.list_nodes()[0]

# Create a port forwarding rule for the node
# This returns a CloudStackPortForwardingRule object
rule = driver.ex_create_port_forwarding_rule(ip, 22, 22, 'TCP', node)
pprint(rule)


# The node now has a public IP and a rule associated to it
print node
print node.extra

API Docs