Categories
Cloud cloud-init openstack Programming python Virtualization

Openstack: First steps

Recently I got access to an Openstack system and did my first steps on it. Maybe some of the things I learned so far are of interest to others.

Using cloudinit for customization

Cloud providers offer the possibility to alter the configuration of their VM images in a standardized way: cloudinit. This uses a simple YAML file to apply certain settings. Here’s a simple example for my Ubuntu VMs:

linux > cat cloudinit.yaml
#cloud-config
package_update: true
package_upgrade: true
package_reboot_if_required: true
locale: de_DE
timezone: Europe/Berlin

So basically the above just updates all installed packages (and reboots if required). It also applies the correct locale and timezone.

Keep in mind that the first line is a mandatory header not a comment! (Took me an hour to figure that out). You can check your cloudinit config like this (the package cloud-init is installed by default):

linux > cloud-init schema --config-file=cloudinit.yaml --annotate
<...>
Valid schema cloudinit.yaml

There are much more options (like adding users/groups, setting SSH keys and so on). When using the web interface you can just upload such a file during VM creation. We’ll get back to this in a later step when we apply this settings using the API.

API tokens

While you can simply access your Openstack’s API with your login an password, this may cause security risks: especially in bigger environments where your login and password may also be used for multiple other systems you shouldn’t store those data in a provisioning script (or other possibly unsafe locations).

A better solution is to create user specific API tokens that are only usable for this special purpose. In addition Openstack allows to limit the roles available by this this means and it can add ACLs (access control lists) to further limit the possibilities of such a token.

Make sure to export the token (and corresponding settings) as clouds.yaml. This file can lateron be used to connect via openstack client software (or SDK). The file should look something like:

linux > cat clouds.yaml
clouds:
  openstack:
    auth:
      auth_url: https://api.cc.mydomain.de:5000
      application_credential_id: "12345678901234567890122345678901"
      application_credential_secret: "my_super_long&secure!_password"
    region_name: "DE"
    interface: "public"
    identity_api_version: 3
    auth_type: "v3applicationcredential"

The important thing is to know the name of our “cloud” – in our case “openstack” (see 2nd line of clouds.yaml)

Accessing Openstack from CLI

So first of all we need an Openstack client (as usual test environment is an Ubuntu 24.04):

linux > sudo apt install python3-openstackclient

Once installed we change into the directory where our downloaded clouds.yaml file is located and do a first test run:

linux > openstack --os-cloud openstack image list
+--------------------------------------+-------------------------+--------+
| ID                                   | Name                    | Status |
+--------------------------------------+-------------------------+--------+
| caa6b88a-70b9-48e8-9be0-6e299f091051 | AlmaLinux 8             | active |
| 9b5a03c5-0138-47e3-b76a-a0d21558ed87 | AlmaLinux 9             | active |
| 60d515fa-230c-4ccf-bb89-3d574c36ae70 | Debian 11               | active |
| 12043350-5032-44e2-8b4b-5d6099a0c03d | Debian 12               | active |
| 0c6a60f1-ffc4-43dc-98c5-d31535f07622 | Kubernetes CAPI 1.27.15 | active |
| fdb489f9-7607-4c0b-8956-26ce74e35f54 | Kubernetes CAPI 1.28.11 | active |
| 60c324e7-6486-46c6-aeb3-1c28f3586220 | Kubernetes CAPI 1.30.2  | active |
| 2acfd099-8ebb-4acc-a926-d7815dc5b38b | OPNsense 24.1           | active |
| f50c3f06-118a-4532-af51-179d212bb257 | Rocky 9                 | active |
| ce96f9da-b3d3-4c25-b275-355eebebd75a | Ubuntu 22.04            | active |
| 585dca5f-73de-4288-ad90-64c9b5d23dcc | Ubuntu 24.04            | active |
| b1a60be3-b6a9-4c83-a80a-71a700d98f2e | Windows 2022 - 07/24    | active |
+--------------------------------------+-------------------------+--------+

Accessing Openstack with python

While there are command line tools to automate Openstack tasks, you may find the python integration more flexible for your use case. First of all: install the requires python package:

linux > sudo apt install python3-openstacksdk

To be honest I didn’t have such a use case – I only was curious ;-). So first let’s start with getting an API connection and list some data. Once again make sure that your clouds.yaml file is located in the same directory.

#!/usr/bin/env python3

import os
import json
from openstack import connection

# Take config from clouds.yaml
conn = connection.from_config(cloud_name='openstack')

print('Images:')
for image in conn.image.images():
    print(image.id, image.name)
    print(json.dumps(image, indent=2))

print('Networks:')
for network in conn.network.networks():
    print(network.id, network.name)
    print(json.dumps(network, indent=2))

print('Volumes:')
for volume in conn.block_storage.volumes():
    print(volume.id, volume.name)
    print(json.dumps(volume, indent=2))

print('Flavors:')
for flavor in conn.compute.flavors():
    print(flavor.id, flavor.name)
    print(json.dumps(flavor, indent=2))

print('Servers:')
for server in conn.compute.servers():
    print(server.id, server.name)
    #print(json.dumps(server, indent=2))

This will list some of the Openstack elements like available images (kind of operating system) or flavors (dimension like # of CPUs, amount of memory, etc.) of VM and also the networks that are provided by Openstack.

They’re often referenced by their id, as we’ll see in the next example. The ids will vary on your installation, so make sure to replace them with the desired ones of your configuration!

And now let’s create a VM.

The following code already has two extra options:

  • It applies the auto configuration settings specified in a cloudinit.yaml file
  • It changes the default installation disk size (otherwise determined by the selected flavor)
#!/usr/bin/env python3

import sys
import os
import json
import base64
from openstack import connection

conn = connection.from_config(cloud_name='openstack')

# Read cloudinit.yaml
with open('cloudinit.yaml', 'r') as f:
    cloudinit_config = f.read()
cloudinit_userdata = base64.b64encode(cloudinit_config.encode("utf-8")).decode('utf-8')

# Define the VM details
vm_name = 'test2'
image_id = '585dca5f-73de-4288-ad90-64c9b5d23dcc'
flavor_id = '12d224ee-9c54-42e9-b762-d857ab4ce533'
network_id = 'c178b03f-17b3-44b0-9d63-d955e932827b'
volume_size = 100 # GB

volume = conn.block_storage.create_volume(
            size = volume_size,
            imageRef = image_id # makes it bootable
            )

# Create the VM
server = conn.compute.create_server(
    name = vm_name,
    image_id = image_id,
    flavor_id = flavor_id,
    networks = [{'uuid': network_id}],
    use_cloud_init = True,
    user_data = cloudinit_userdata,
    block_device_mapping_v2 = [{
        'source_type': 'volume',
        'destination_type': 'volume',
        'uuid': volume.id,
        'boot_index': '0',
        'volume_size': volume_size,
        'delete_on_termination': True,
        }]
)

# Print the VM details
print('VM created:')
print('  ID:', server.id)
print('  Name:', server.name)
print('  Status:', server.status)

Leave a Reply

Your email address will not be published. Required fields are marked *