inmanta-module-openstack 5.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- inmanta_module_openstack-5.0.0/MANIFEST.in +5 -0
- inmanta_module_openstack-5.0.0/PKG-INFO +8 -0
- inmanta_module_openstack-5.0.0/README.md +261 -0
- inmanta_module_openstack-5.0.0/inmanta_module_openstack.egg-info/PKG-INFO +8 -0
- inmanta_module_openstack-5.0.0/inmanta_module_openstack.egg-info/SOURCES.txt +48 -0
- inmanta_module_openstack-5.0.0/inmanta_module_openstack.egg-info/dependency_links.txt +1 -0
- inmanta_module_openstack-5.0.0/inmanta_module_openstack.egg-info/not-zip-safe +1 -0
- inmanta_module_openstack-5.0.0/inmanta_module_openstack.egg-info/requires.txt +4 -0
- inmanta_module_openstack-5.0.0/inmanta_module_openstack.egg-info/top_level.txt +1 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/__init__.py +186 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/common/__init__.py +17 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/common/base.py +327 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/common/deps.py +184 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/compute/__init__.py +17 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/compute/flavor.py +171 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/compute/host_port.py +296 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/compute/virtual_machine.py +313 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/identity/__init__.py +17 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/identity/group.py +91 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/identity/group_role.py +184 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/identity/keystone_base.py +71 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/identity/project.py +102 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/identity/role.py +189 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/identity/user.py +119 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/image/__init__.py +17 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/image/image.py +275 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/model/_init.cf +727 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/__init__.py +17 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/floating_ip.py +158 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/network.py +205 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/qos_policy.py +158 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/router.py +242 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/router_port.py +185 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/security_group.py +262 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/subnet.py +230 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/network/subnet_v6.py +241 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/quota/__init__.py +17 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/quota/quota.py +303 -0
- inmanta_module_openstack-5.0.0/inmanta_plugins/openstack/setup.cfg +42 -0
- inmanta_module_openstack-5.0.0/pyproject.toml +11 -0
- inmanta_module_openstack-5.0.0/setup.cfg +43 -0
- inmanta_module_openstack-5.0.0/tests/test_dependency_handling.py +170 -0
- inmanta_module_openstack-5.0.0/tests/test_examples.py +53 -0
- inmanta_module_openstack-5.0.0/tests/test_flavor.py +162 -0
- inmanta_module_openstack-5.0.0/tests/test_image.py +314 -0
- inmanta_module_openstack-5.0.0/tests/test_keystone.py +1049 -0
- inmanta_module_openstack-5.0.0/tests/test_neutron.py +1386 -0
- inmanta_module_openstack-5.0.0/tests/test_nova.py +230 -0
- inmanta_module_openstack-5.0.0/tests/test_quota.py +235 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# openstack adapter
|
|
2
|
+
|
|
3
|
+
The openstack module provides support for managing various resources on OpenStack, including virtual
|
|
4
|
+
machines, networks, routers, ...
|
|
5
|
+
|
|
6
|
+
## How to use it
|
|
7
|
+
|
|
8
|
+
This guide explains how to start virtual machines on OpenStack.More examples can be found in the `examples` folder of this repo.
|
|
9
|
+
|
|
10
|
+
### Prerequisites
|
|
11
|
+
|
|
12
|
+
This tutorial requires you to have an account on an OpenStack. The example below loads the required
|
|
13
|
+
credentials from environment variables, just like the OpenStack command line tools. Additionally,
|
|
14
|
+
the following parameters are also required:
|
|
15
|
+
|
|
16
|
+
- **ssh_public_key** : Your public ssh key (the key itself, not the name of the file it is in)
|
|
17
|
+
- **network_name** : The name of the Openstack network to connect the VM to
|
|
18
|
+
- **subnet_name** : The name of the Openstack subnet to connect the VM to
|
|
19
|
+
- **network_address**: The network address of the subnet above
|
|
20
|
+
- **flavor_name** : The name of the Openstack flavor to create the VM from
|
|
21
|
+
- **image_id** : The ID of the Openstack image to boot the VM from
|
|
22
|
+
- **os** : The OS of the image
|
|
23
|
+
|
|
24
|
+
### Creating machines
|
|
25
|
+
|
|
26
|
+
The following model creates a new virtual machine. The parameters in the list above are exposed as variables at the start of the code snippet.
|
|
27
|
+
|
|
28
|
+
```inmanta
|
|
29
|
+
import openstack
|
|
30
|
+
import ssh
|
|
31
|
+
|
|
32
|
+
## Edit this parameters
|
|
33
|
+
image_id = ""
|
|
34
|
+
network_name = ""
|
|
35
|
+
subnet_name = ""
|
|
36
|
+
network_address = ""
|
|
37
|
+
|
|
38
|
+
flavor_name = ""
|
|
39
|
+
ssh_public_key=""
|
|
40
|
+
|
|
41
|
+
# change OS parameter to match the actual image. If an OS is not modelled in an existing module,
|
|
42
|
+
# std::linux can be used for example. However, other modules might not have support for a
|
|
43
|
+
# generic os definition such as std::linux
|
|
44
|
+
os = std::linux
|
|
45
|
+
## End edit
|
|
46
|
+
|
|
47
|
+
# register ssh key
|
|
48
|
+
ssh_key = ssh::Key(name="mykey", public_key=ssh_public_key)
|
|
49
|
+
|
|
50
|
+
# Define the OpenStack provider to use
|
|
51
|
+
provider = openstack::Provider(name="iaas_openstack", connection_url=std::get_env("OS_AUTH_URL"),
|
|
52
|
+
username=std::get_env("OS_USERNAME"),
|
|
53
|
+
password=std::get_env("OS_PASSWORD"),
|
|
54
|
+
project_name=std::get_env("OS_PROJECT_NAME"),
|
|
55
|
+
user_domain_name=std::gen_env("OS_USER_DOMAIN_NAME"),
|
|
56
|
+
project_domain_name=std::gen_env("OS_PROJECT_DOMAIN_NAME"))
|
|
57
|
+
|
|
58
|
+
# Define the project to boot the VM in, but do not let inmanta manage it
|
|
59
|
+
project = openstack::Project(provider=provider, name=provider.project_name, description="", enabled=true,
|
|
60
|
+
managed=false)
|
|
61
|
+
|
|
62
|
+
# Define the network objects to connect the virtual machine to but again, do not manage them
|
|
63
|
+
net = openstack::Network(provider=provider, project=project, name=network_name, managed=false)
|
|
64
|
+
subnet = openstack::Subnet(provider=provider, project=project, network=net, dhcp=true, managed=false,
|
|
65
|
+
name=subnet_name, network_address=network_address)
|
|
66
|
+
|
|
67
|
+
# Define the virtual machine
|
|
68
|
+
vm = openstack::Host(provider=provider, project=project, key_pair=ssh_key, name="testhost",
|
|
69
|
+
image=image_id, os=os, flavor=flavor_name, user_data="", subnet=subnet)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Getting the agent on the machine
|
|
73
|
+
|
|
74
|
+
The user_data attribute of the :inmanta:entity:`openstack::VirtualMachine` entity can inject a shell script that is executed
|
|
75
|
+
at first boot of the virtual machine (through cloud-init). Below is an example script to install
|
|
76
|
+
the inmanta agent (from RPM) and let it connect back to the management server.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
#!/bin/bash
|
|
80
|
+
|
|
81
|
+
hostname {{ name }}
|
|
82
|
+
|
|
83
|
+
curl -1sLf \
|
|
84
|
+
'https://packages.inmanta.com/public/oss-stable/setup.rpm.sh' \
|
|
85
|
+
| sudo -E bash
|
|
86
|
+
|
|
87
|
+
dnf install -y inmanta-oss-agent
|
|
88
|
+
|
|
89
|
+
cat > /etc/inmanta/agent.cfg <<EOF
|
|
90
|
+
[config]
|
|
91
|
+
heartbeat-interval = 60
|
|
92
|
+
fact-expire = 60
|
|
93
|
+
state-dir=/var/lib/inmanta
|
|
94
|
+
environment={{ env_id }}
|
|
95
|
+
agent-names=\$node-name
|
|
96
|
+
[agent_rest_transport]
|
|
97
|
+
port={{port}}
|
|
98
|
+
host={{env_server}}
|
|
99
|
+
EOF
|
|
100
|
+
|
|
101
|
+
systemctl start inmanta-agent
|
|
102
|
+
systemctl enable inmanta-agent
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Pushing resources to the machine
|
|
107
|
+
|
|
108
|
+
You can use adapters from std or other modules to manage resources on the machine. For example, we can create a config file in /tmp by adding the following lines to the model that created the virtual machine:
|
|
109
|
+
|
|
110
|
+
```inmanta
|
|
111
|
+
#put a file on the machine
|
|
112
|
+
std::ConfigFile(host = host1, path="/tmp/test", content="I did it!")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Actual usage
|
|
116
|
+
|
|
117
|
+
Creating instances of `openstack::Host`, as shown above requires many parameters and relations,
|
|
118
|
+
creating a model that is hard to read. Often, these parameters are all the same within a single
|
|
119
|
+
model. This means that Inmanta can encapsulate this complexity.
|
|
120
|
+
|
|
121
|
+
In a larger model, a new `Host` type can encapsulate all settings that are the same for all hosts.
|
|
122
|
+
Additionally, an entity that represents the `infrastructure` can hold shared configuration such as
|
|
123
|
+
the provider, monitoring, shared networks, global parameters,...)
|
|
124
|
+
|
|
125
|
+
For example (`full source here <https://github.com/inmanta/openstack/tree/master/examples/openstackclean>`_)
|
|
126
|
+
|
|
127
|
+
Applied to the example above the main file is reduced to:
|
|
128
|
+
|
|
129
|
+
```inmanta
|
|
130
|
+
import mymodule
|
|
131
|
+
import ssh
|
|
132
|
+
|
|
133
|
+
## Edit this parameters
|
|
134
|
+
image_id = ""
|
|
135
|
+
network_name = ""
|
|
136
|
+
subnet_name = ""
|
|
137
|
+
network_address = ""
|
|
138
|
+
|
|
139
|
+
flavor_name = ""
|
|
140
|
+
ssh_public_key=""
|
|
141
|
+
|
|
142
|
+
# change OS parameter to match the actual image. If an OS is not modelled in an existing module,
|
|
143
|
+
# std::linux can be used for example. However, other modules might not have support for a
|
|
144
|
+
# generic os definition such as std::linux
|
|
145
|
+
os = std::linux
|
|
146
|
+
## End edit
|
|
147
|
+
|
|
148
|
+
# register ssh key
|
|
149
|
+
ssh_key = ssh::Key(name="mykey", public_key=ssh_public_key)
|
|
150
|
+
|
|
151
|
+
# create the cluster
|
|
152
|
+
cluster = mymodule::MyCluster(network_name=network_name, subnet_name=subnet_name,
|
|
153
|
+
image_id=image_id, flavor=flavor_name, key=ssh_key,
|
|
154
|
+
network_address=network_address, os=os)
|
|
155
|
+
|
|
156
|
+
# make a vm!
|
|
157
|
+
host1 = mymodule::MyHost(name="testhost", cluster=cluster)
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
With the following module:
|
|
161
|
+
|
|
162
|
+
```inmanta
|
|
163
|
+
import openstack
|
|
164
|
+
import ssh
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
entity MyCluster:
|
|
168
|
+
"""
|
|
169
|
+
A cluster object that represents all shared config and infrastructure,
|
|
170
|
+
including connecting to OpenStack.
|
|
171
|
+
"""
|
|
172
|
+
string network_name
|
|
173
|
+
string subnet_name
|
|
174
|
+
string network_address
|
|
175
|
+
string image_id
|
|
176
|
+
string flavor
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
#input: the ssh key for all VMs
|
|
180
|
+
MyCluster.key [1] -- ssh::Key
|
|
181
|
+
|
|
182
|
+
#input: the OS for all VMs
|
|
183
|
+
MyCluster.os [1] -- std::OS
|
|
184
|
+
|
|
185
|
+
#internal: objects needed to construct hosts
|
|
186
|
+
MyCluster.provider [1] -- openstack::Provider
|
|
187
|
+
MyCluster.project [1] -- openstack::Project
|
|
188
|
+
MyCluster.net [1] -- openstack::Network
|
|
189
|
+
MyCluster.subnet [1] -- openstack::Subnet
|
|
190
|
+
|
|
191
|
+
implementation connection for MyCluster:
|
|
192
|
+
# Define the OpenStack provider to use
|
|
193
|
+
self.provider = openstack::Provider(name="iaas_openstack",
|
|
194
|
+
connection_url=std::get_env("OS_AUTH_URL"),
|
|
195
|
+
username=std::get_env("OS_USERNAME"),
|
|
196
|
+
password=std::get_env("OS_PASSWORD"),
|
|
197
|
+
project_name=std::get_env("OS_PROJECT_NAME"),
|
|
198
|
+
user_domain_name=std::gen_env("OS_USER_DOMAIN_NAME"),
|
|
199
|
+
project_domain_name=std::gen_env("OS_PROJECT_DOMAIN_NAME")
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Define the project to boot the VM in, but do not let inmanta manage it
|
|
203
|
+
self.project = openstack::Project(provider=self.provider, name=self.provider.project_name,
|
|
204
|
+
description="", enabled=true, managed=false)
|
|
205
|
+
|
|
206
|
+
# Define the network objects to connect the virtual machine to but again, do not manage them
|
|
207
|
+
self.net = openstack::Network(provider=self.provider, project=self.project,
|
|
208
|
+
name=self.network_name, managed=false)
|
|
209
|
+
self.subnet = openstack::Subnet(provider=self.provider, project=self.project,
|
|
210
|
+
network=self.net, dhcp=true, name=self.subnet_name,
|
|
211
|
+
network_address=self.network_address, managed=false)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
implement MyCluster using connection
|
|
215
|
+
|
|
216
|
+
#define our own host type
|
|
217
|
+
entity MyHost extends openstack::Host:
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
#input: the cluster object
|
|
221
|
+
MyCluster.hosts [0:] -- MyHost.cluster [1]
|
|
222
|
+
|
|
223
|
+
implementation myhost for MyHost:
|
|
224
|
+
#wire up all config for agent injection
|
|
225
|
+
env_name = std::environment_name()
|
|
226
|
+
env_id = std::environment()
|
|
227
|
+
env_server = std::environment_server()
|
|
228
|
+
port = std::server_port()
|
|
229
|
+
|
|
230
|
+
#wire up all config for vm creation
|
|
231
|
+
self.provider = cluster.provider
|
|
232
|
+
self.project = cluster.project
|
|
233
|
+
self.image = cluster.image_id
|
|
234
|
+
self.subnet = cluster.subnet
|
|
235
|
+
self.user_data = std::template("mymodule/user_data.tmpl")
|
|
236
|
+
self.key_pair = cluster.key
|
|
237
|
+
self.os = cluster.os
|
|
238
|
+
self.flavor = cluster.flavor
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# use our implemenation
|
|
242
|
+
# and also the catchall std::hostDefaults
|
|
243
|
+
# and the openstackVM implementation that sets the ip and create the eth0 port
|
|
244
|
+
implement MyHost using myhost, std::hostDefaults, openstack::openstackVM, openstack::eth0Port
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
If this were not an example, we would make the following changes:
|
|
248
|
+
|
|
249
|
+
- hardcode the ``image_id`` and ``os`` (and perhaps ``flavor``) into the defintion of ``myhost``.
|
|
250
|
+
- the parameters on top would be moved to either an lsm service or filled in directly into the constructor.
|
|
251
|
+
- use ``std::password`` to store passwords, to prevent accidential check-ins with passwords in the source
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
```{toctree}
|
|
258
|
+
:maxdepth: 1
|
|
259
|
+
autodoc.rst
|
|
260
|
+
CHANGELOG.md
|
|
261
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
MANIFEST.in
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.cfg
|
|
5
|
+
inmanta_module_openstack.egg-info/PKG-INFO
|
|
6
|
+
inmanta_module_openstack.egg-info/SOURCES.txt
|
|
7
|
+
inmanta_module_openstack.egg-info/dependency_links.txt
|
|
8
|
+
inmanta_module_openstack.egg-info/not-zip-safe
|
|
9
|
+
inmanta_module_openstack.egg-info/requires.txt
|
|
10
|
+
inmanta_module_openstack.egg-info/top_level.txt
|
|
11
|
+
inmanta_plugins/openstack/__init__.py
|
|
12
|
+
inmanta_plugins/openstack/setup.cfg
|
|
13
|
+
inmanta_plugins/openstack/common/__init__.py
|
|
14
|
+
inmanta_plugins/openstack/common/base.py
|
|
15
|
+
inmanta_plugins/openstack/common/deps.py
|
|
16
|
+
inmanta_plugins/openstack/compute/__init__.py
|
|
17
|
+
inmanta_plugins/openstack/compute/flavor.py
|
|
18
|
+
inmanta_plugins/openstack/compute/host_port.py
|
|
19
|
+
inmanta_plugins/openstack/compute/virtual_machine.py
|
|
20
|
+
inmanta_plugins/openstack/identity/__init__.py
|
|
21
|
+
inmanta_plugins/openstack/identity/group.py
|
|
22
|
+
inmanta_plugins/openstack/identity/group_role.py
|
|
23
|
+
inmanta_plugins/openstack/identity/keystone_base.py
|
|
24
|
+
inmanta_plugins/openstack/identity/project.py
|
|
25
|
+
inmanta_plugins/openstack/identity/role.py
|
|
26
|
+
inmanta_plugins/openstack/identity/user.py
|
|
27
|
+
inmanta_plugins/openstack/image/__init__.py
|
|
28
|
+
inmanta_plugins/openstack/image/image.py
|
|
29
|
+
inmanta_plugins/openstack/model/_init.cf
|
|
30
|
+
inmanta_plugins/openstack/network/__init__.py
|
|
31
|
+
inmanta_plugins/openstack/network/floating_ip.py
|
|
32
|
+
inmanta_plugins/openstack/network/network.py
|
|
33
|
+
inmanta_plugins/openstack/network/qos_policy.py
|
|
34
|
+
inmanta_plugins/openstack/network/router.py
|
|
35
|
+
inmanta_plugins/openstack/network/router_port.py
|
|
36
|
+
inmanta_plugins/openstack/network/security_group.py
|
|
37
|
+
inmanta_plugins/openstack/network/subnet.py
|
|
38
|
+
inmanta_plugins/openstack/network/subnet_v6.py
|
|
39
|
+
inmanta_plugins/openstack/quota/__init__.py
|
|
40
|
+
inmanta_plugins/openstack/quota/quota.py
|
|
41
|
+
tests/test_dependency_handling.py
|
|
42
|
+
tests/test_examples.py
|
|
43
|
+
tests/test_flavor.py
|
|
44
|
+
tests/test_image.py
|
|
45
|
+
tests/test_keystone.py
|
|
46
|
+
tests/test_neutron.py
|
|
47
|
+
tests/test_nova.py
|
|
48
|
+
tests/test_quota.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
inmanta_plugins
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2022 Inmanta
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
Contact: code@inmanta.com
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import datetime
|
|
20
|
+
import logging
|
|
21
|
+
import math
|
|
22
|
+
from typing import Optional
|
|
23
|
+
|
|
24
|
+
import keystoneauth1.exceptions
|
|
25
|
+
import openstack.connection
|
|
26
|
+
import openstack.exceptions
|
|
27
|
+
from inmanta.execute.util import Unknown
|
|
28
|
+
from inmanta.plugins import PluginException, plugin
|
|
29
|
+
|
|
30
|
+
IMAGES = {}
|
|
31
|
+
FIND_IMAGE_RESULT = {}
|
|
32
|
+
|
|
33
|
+
LOGGER = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@plugin
|
|
37
|
+
def find_image(
|
|
38
|
+
provider: "openstack::Provider", os: "std::OS", name: "string" = None
|
|
39
|
+
) -> "string":
|
|
40
|
+
"""
|
|
41
|
+
Search for an image that matches the given operating system. This plugin uses
|
|
42
|
+
the os_distro and os_version tags of an image and the name and version attributes of
|
|
43
|
+
the OS parameter.
|
|
44
|
+
|
|
45
|
+
If multiple images match, the most recent image is returned.
|
|
46
|
+
|
|
47
|
+
:param provider: The provider to query for an image
|
|
48
|
+
:param os: The operating system and version (using os_distro and os_version metadata)
|
|
49
|
+
:param name: An optional string that the image name should contain
|
|
50
|
+
"""
|
|
51
|
+
ident = (provider.name, os.name.lower(), str(os.version).lower(), name)
|
|
52
|
+
if ident in FIND_IMAGE_RESULT:
|
|
53
|
+
return FIND_IMAGE_RESULT[ident]
|
|
54
|
+
|
|
55
|
+
conn = None
|
|
56
|
+
try:
|
|
57
|
+
if provider.name not in IMAGES:
|
|
58
|
+
conn = openstack.connection.Connection(
|
|
59
|
+
auth_url=provider.connection_url,
|
|
60
|
+
username=provider.username,
|
|
61
|
+
password=provider.password,
|
|
62
|
+
project_name=provider.project_name,
|
|
63
|
+
user_domain_name=provider.user_domain_name,
|
|
64
|
+
project_domain_name=provider.project_domain_name,
|
|
65
|
+
verify_cert=provider.verify_cert,
|
|
66
|
+
)
|
|
67
|
+
IMAGES[provider.name] = list(conn.image.images())
|
|
68
|
+
|
|
69
|
+
target_distro = os.name.lower()
|
|
70
|
+
target_version = str(os.version).lower()
|
|
71
|
+
|
|
72
|
+
newest_time = datetime.datetime(1900, 1, 1)
|
|
73
|
+
newest_image = None
|
|
74
|
+
|
|
75
|
+
for image in IMAGES[provider.name]:
|
|
76
|
+
if image.visibility != "public":
|
|
77
|
+
continue
|
|
78
|
+
if not image.name or (name and name not in image.name):
|
|
79
|
+
continue
|
|
80
|
+
|
|
81
|
+
os_distro = getattr(image, "os_distro", None)
|
|
82
|
+
os_version = getattr(image, "os_version", None)
|
|
83
|
+
if not os_distro or not os_version:
|
|
84
|
+
continue
|
|
85
|
+
if (
|
|
86
|
+
os_distro.lower() != target_distro
|
|
87
|
+
or os_version.lower() != target_version
|
|
88
|
+
):
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
t = datetime.datetime.strptime(image.updated_at, "%Y-%m-%dT%H:%M:%SZ")
|
|
93
|
+
except Exception:
|
|
94
|
+
continue
|
|
95
|
+
|
|
96
|
+
if t > newest_time:
|
|
97
|
+
newest_time = t
|
|
98
|
+
newest_image = image
|
|
99
|
+
|
|
100
|
+
if newest_image is None:
|
|
101
|
+
raise Exception(f"No image found for os {os.name} and version {os.version}")
|
|
102
|
+
|
|
103
|
+
result = newest_image.id
|
|
104
|
+
FIND_IMAGE_RESULT[ident] = result
|
|
105
|
+
return result
|
|
106
|
+
except (
|
|
107
|
+
openstack.exceptions.SDKException,
|
|
108
|
+
keystoneauth1.exceptions.ClientException,
|
|
109
|
+
) as exc:
|
|
110
|
+
LOGGER.warning("Failed to fetch images for provider %s: %s", provider.name, exc)
|
|
111
|
+
return Unknown(None)
|
|
112
|
+
finally:
|
|
113
|
+
if conn is not None:
|
|
114
|
+
conn.close()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
FLAVORS = {}
|
|
118
|
+
FIND_FLAVOR_RESULT = {}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@plugin
|
|
122
|
+
def find_flavor(
|
|
123
|
+
provider: "openstack::Provider",
|
|
124
|
+
vcpus: "number",
|
|
125
|
+
ram: "number",
|
|
126
|
+
pinned: "bool" = False,
|
|
127
|
+
) -> "string":
|
|
128
|
+
"""
|
|
129
|
+
Find the flavor that matches the closest to the resources requested.
|
|
130
|
+
|
|
131
|
+
:param vcpus: The number of virtual cpus in the flavor
|
|
132
|
+
:param ram: The amount of ram in gigabyte
|
|
133
|
+
:param pinned: Wether the CPUs need to be pinned (#vcpu == #pcpu)
|
|
134
|
+
"""
|
|
135
|
+
ident = (provider.name, vcpus, ram, pinned)
|
|
136
|
+
if ident in FIND_FLAVOR_RESULT:
|
|
137
|
+
return FIND_FLAVOR_RESULT[ident]
|
|
138
|
+
|
|
139
|
+
conn = None
|
|
140
|
+
try:
|
|
141
|
+
if provider.name not in FLAVORS:
|
|
142
|
+
conn = openstack.connection.Connection(
|
|
143
|
+
auth_url=provider.connection_url,
|
|
144
|
+
username=provider.username,
|
|
145
|
+
password=provider.password,
|
|
146
|
+
project_name=provider.project_name,
|
|
147
|
+
user_domain_name=provider.user_domain_name,
|
|
148
|
+
project_domain_name=provider.project_domain_name,
|
|
149
|
+
verify_cert=provider.verify_cert,
|
|
150
|
+
)
|
|
151
|
+
FLAVORS[provider.name] = list(conn.compute.flavors(get_extra_specs=True))
|
|
152
|
+
|
|
153
|
+
selected: tuple[float, Optional[openstack.compute.v2.flavor.Flavor]] = (
|
|
154
|
+
1000000,
|
|
155
|
+
None,
|
|
156
|
+
)
|
|
157
|
+
for flavor in FLAVORS[provider.name]:
|
|
158
|
+
is_pinned = flavor.extra_specs.get("hw:cpu_policy") == "dedicated"
|
|
159
|
+
if is_pinned ^ pinned:
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
d_cpu = flavor.vcpus - vcpus
|
|
163
|
+
d_ram = (flavor.ram / 1024) - ram
|
|
164
|
+
distance = math.sqrt(math.pow(d_cpu, 2) + math.pow(d_ram, 2))
|
|
165
|
+
if d_cpu >= 0 and d_ram >= 0 and distance < selected[0]:
|
|
166
|
+
selected = (distance, flavor)
|
|
167
|
+
|
|
168
|
+
if selected[1] is None:
|
|
169
|
+
raise PluginException(
|
|
170
|
+
"Couldn't find a flavor with at least %s %s CPUs and %s Gigabytes of RAM."
|
|
171
|
+
% (vcpus, "pinned" if pinned else "unpinned", ram)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
FIND_FLAVOR_RESULT[ident] = selected[1].name
|
|
175
|
+
return selected[1].name
|
|
176
|
+
except (
|
|
177
|
+
openstack.exceptions.SDKException,
|
|
178
|
+
keystoneauth1.exceptions.ClientException,
|
|
179
|
+
) as exc:
|
|
180
|
+
LOGGER.warning(
|
|
181
|
+
"Failed to fetch flavors for provider %s: %s", provider.name, exc
|
|
182
|
+
)
|
|
183
|
+
return Unknown(None)
|
|
184
|
+
finally:
|
|
185
|
+
if conn is not None:
|
|
186
|
+
conn.close()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2022 Inmanta
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
Contact: code@inmanta.com
|
|
17
|
+
"""
|