OpenStack¶
The openstack module provides support for managing various resources on OpenStack, including virtual machines, networks, routers, …
This guide explains how to start virtual machines on OpenStack.
Prerequisites¶
This tutorial requires you to have an account on an OpenStack. The example below loads the required credentials from environment variables, just like the OpenStack command line tools. Additionally, the following parameters are also required:
ssh_public_key |
Your public ssh key (the key itself, not the name of the file it is in) |
network_name |
The name of the Openstack network to connect the VM to |
subnet_name |
The name of the Openstack subnet to connect the VM to |
network_address |
The network address of the subnet above |
flavor_name |
The name of the Openstack flavor to create the VM from |
image_id |
The ID of the Openstack image to boot the VM from |
os |
The OS of the image |
The model below exposes these parameters at the top of the code snippet.
Creating machines¶
1import openstack
2import ssh
3import redhat
4import ubuntu
5
6## Edit this parameters
7image_id = ""
8network_name = ""
9subnet_name = ""
10network_address = ""
11
12flavor_name = ""
13ssh_public_key=""
14
15# change OS parameter to match the actual image. If an OS is not modelled in an existing module,
16# std::linux can be used for example. However, other modules might not have support for a
17# generic os definition such as std::linux
18os = redhat::fedora23
19## End edit
20
21# register ssh key
22ssh_key = ssh::Key(name="mykey", public_key=ssh_public_key)
23
24# Define the OpenStack provider to use
25provider = openstack::Provider(name="iaas_openstack", connection_url=std::get_env("OS_AUTH_URL"),
26 username=std::get_env("OS_USERNAME"),
27 password=std::get_env("OS_PASSWORD"),
28 tenant=std::get_env("OS_PROJECT_NAME"))
29
30# Define the project/tenant to boot the VM in, but do not let inmanta manage it
31project = openstack::Project(provider=provider, name=provider.tenant, description="", enabled=true,
32 managed=false)
33
34# Define the network objects to connect the virtual machine to but again, do not manage them
35net = openstack::Network(provider=provider, project=project, name=network_name, managed=false)
36subnet = openstack::Subnet(provider=provider, project=project, network=net, dhcp=true, managed=false,
37 name=subnet_name, network_address=network_address)
38
39# Define the virtual machine
40vm = openstack::Host(provider=provider, project=project, key_pair=ssh_key, name="testhost",
41 image=image_id, os=os, flavor=flavor_name, user_data="", subnet=subnet)
Getting the agent on the machine¶
The user_data attribute of the openstack::VirtualMachine
entity can inject a shell script that is executed
at first boot of the virtual machine (through cloud-init). Below is an example script to install
the inmanta agent (from RPM) and let it connect back to the management server.
#!/bin/bash
hostname {{ name }}
setenforce 0
cat > /etc/yum.repos.d/inmanta.repo <<EOF
[bartvanbrabant-inmanta]
name=Copr repo for inmanta owned by bartvanbrabant
baseurl=https://copr-be.cloud.fedoraproject.org/results/bartvanbrabant/inmanta/fedora-\$releasever-\$basearch/
type=rpm-md
skip_if_unavailable=True
gpgcheck=1
gpgkey=https://copr-be.cloud.fedoraproject.org/results/bartvanbrabant/inmanta/pubkey.gpg
repo_gpgcheck=0
enabled=1
enabled_metadata=1
EOF
dnf install -y python3-inmanta-agent
cat > /etc/inmanta/agent.cfg <<EOF
[config]
heartbeat-interval = 60
fact-expire = 60
state-dir=/var/lib/inmanta
environment={{ env_id }}
agent-names=\$node-name
[agent_rest_transport]
port={{port}}
host={{env_server}}
EOF
systemctl start inmanta-agent
systemctl enable inmanta-agent
Pushing config to the machine¶
To install config:
#put a file on the machine
std::ConfigFile(host = host1, path="/tmp/test", content="I did it!")
Actual usage¶
Creating instances of openstack::Host
, as shown above requires many parameters and relations,
creating a model that is hard to read. Often, these parameters are all the same within a single
model. This means that Inmanta can encapsulate this complexity.
In a larger model, a new Host
type can encapsulate all settings that are the same for all hosts.
Additionally, an entity that represents the infrastructure can hold shared configuration such as
the provider, monitoring, shared networks, global parameters,…)
For example (full source here)
Applied to the example above the main file is reduced to:
1import mymodule
2import ssh
3import redhat
4import ubuntu
5
6## Edit this parameters
7image_id = ""
8network_name = ""
9subnet_name = ""
10network_address = ""
11
12flavor_name = ""
13ssh_public_key=""
14
15# change OS parameter to match the actual image. If an OS is not modelled in an existing module,
16# std::linux can be used for example. However, other modules might not have support for a
17# generic os definition such as std::linux
18os = redhat::fedora23
19## End edit
20
21# register ssh key
22ssh_key = ssh::Key(name="mykey", public_key=ssh_public_key)
23
24# create the cluster
25cluster = mymodule::MyCluster(network_name=network_name, subnet_name=subnet_name,
26 image_id=image_id, flavor=flavor_name, key=ssh_key,
27 network_address=network_address, os=os)
28
29# make a vm!
30host1 = mymodule::MyHost(name="testhost", cluster=cluster)
With the following module:
1import openstack
2import ssh
3
4
5entity MyCluster:
6 """
7 A cluster object that represents all shared config and infrastructure,
8 including connecting to OpenStack.
9 """
10 string network_name
11 string subnet_name
12 string network_address
13 string image_id
14 string flavor
15end
16
17#input: the ssh key for all VMs
18MyCluster.key [1] -- ssh::Key
19
20#input: the OS for all VMs
21MyCluster.os [1] -- std::OS
22
23#internal: objects needed to construct hosts
24MyCluster.provider [1] -- openstack::Provider
25MyCluster.project [1] -- openstack::Project
26MyCluster.net [1] -- openstack::Network
27MyCluster.subnet [1] -- openstack::Subnet
28
29implementation connection for MyCluster:
30 # Define the OpenStack provider to use
31 self.provider = openstack::Provider(name="iaas_openstack",
32 connection_url=std::get_env("OS_AUTH_URL"),
33 username=std::get_env("OS_USERNAME"),
34 password=std::get_env("OS_PASSWORD"),
35 tenant=std::get_env("OS_PROJECT_NAME"))
36
37 # Define the project/tenant to boot the VM in, but do not let inmanta manage it
38 self.project = openstack::Project(provider=self.provider, name=self.provider.tenant,
39 description="", enabled=true, managed=false)
40
41 # Define the network objects to connect the virtual machine to but again, do not manage them
42 self.net = openstack::Network(provider=self.provider, project=self.project,
43 name=self.network_name, managed=false)
44 self.subnet = openstack::Subnet(provider=self.provider, project=self.project,
45 network=self.net, dhcp=true, name=self.subnet_name,
46 network_address=self.network_address, managed=false)
47end
48
49implement MyCluster using connection
50
51#define our own host type
52entity MyHost extends openstack::Host:
53end
54
55#input: the cluster object
56MyCluster.hosts [0:] -- MyHost.cluster [1]
57
58implementation myhost for MyHost:
59 #wire up all config for agent injection
60 env_name = std::environment_name()
61 env_id = std::environment()
62 env_server = std::environment_server()
63 port = std::server_port()
64
65 #wire up all config for vm creation
66 self.provider = cluster.provider
67 self.project = cluster.project
68 self.image = cluster.image_id
69 self.subnet = cluster.subnet
70 self.user_data = std::template("mymodule/user_data.tmpl")
71 self.key_pair = cluster.key
72 self.os = cluster.os
73 self.flavor = cluster.flavor
74end
75
76# use our implemenation
77# and also the catchall std::hostDefaults
78# and the openstackVM implementation that sets the ip and create the eth0 port
79implement MyHost using myhost, std::hostDefaults, openstack::openstackVM, openstack::eth0Port
If this were not an example, we would make the following changes:
hardcode the
image_id
andos
(and perhapsflavor
) into the defintion ofmyhost
.the parameters on top would be moved to either a forms or filled in directly into the constructor.
use
std::password
to store passwords, to prevent accidential check-ins with passwords in the source