-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtest_server.py
286 lines (187 loc) · 8.43 KB
/
test_server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
"""
Server Functionality
====================
The cloudscale.ch API can be used to automate actions otherwise available
through the web-interface. Most importantly, servers can be launched and scaled
at any time.
API Docs: https://www.cloudscale.ch/en/api/v1
"""
from util import extract_number
from util import oneliner
def test_change_flavor_from_flex_to_flex(create_server):
""" It is possible to change from one flex flavor to another. """
# Start a server with the flex-4-1 flavor
server = create_server(flavor='flex-4-1')
assert server.assigned_memory() == 4
assert server.assigned_cpus() == 1
# To change the flavor we need to stop the server first
server.stop()
# Change the flavor to flex-8-2
server.update(flavor='flex-8-2')
# Make sure the server has been scaled
server.start()
assert server.assigned_memory() == 8
assert server.assigned_cpus() == 2
def test_change_flavor_from_flex_to_plus(create_server):
""" It is possible to change from a flex to a plus flavor. """
# Start a server with the flex-4-1 flavor
server = create_server(flavor='flex-4-1')
assert server.assigned_memory() == 4
assert server.assigned_cpus() == 1
# To change the flavor we need to stop the server first
server.stop()
# Change the flavor to plus-8-2
server.update(flavor='plus-8-2')
# Make sure the server has been scaled
server.start()
assert server.assigned_memory() == 8
assert server.assigned_cpus() == 2
def test_change_flavor_from_plus_to_flex(create_server):
""" It is possible to change from a plus to a flex flavor. """
# Start a server with the plus-8-2 flavor
server = create_server(flavor='plus-8-2')
assert server.assigned_memory() == 8
assert server.assigned_cpus() == 2
# To change the flavor we need to stop the server first
server.stop()
# Change the flavor to flex-4-1
server.update(flavor='flex-4-1')
# Make sure the server has been scaled
server.start()
assert server.assigned_memory() == 4
assert server.assigned_cpus() == 1
def test_change_flavor_from_plus_to_plus(create_server):
""" It is possible to change from one plus flavor to another. """
# Start a server with the plus-8-2 flavor
server = create_server(flavor='plus-8-2')
assert server.assigned_memory() == 8
assert server.assigned_cpus() == 2
# To change the flavor we need to stop the server first
server.stop()
# Change the flavor to plus-12-3
server.update(flavor='plus-12-3')
# Make sure the server has been scaled
server.start()
assert server.assigned_memory() == 12
assert server.assigned_cpus() == 3
def test_hostname(create_server):
""" Servers can be named on creation, with some restrctions.
During creation, the name must only contain letters (a-z), digits (0-9),
hyphens (-) and dots (.). The server's hostname in the guest VM is set
to this name.
"""
# Servers can be created using a fully qualified domain name
server = create_server(name='node-1.example.org', auto_name=False)
assert server.output_of('hostname --fqdn') == 'node-1.example.org'
# Servers can be also be created using a simple name
server = create_server(name='node-1', auto_name=False)
assert server.output_of('hostname --fqdn') == 'node-1'
def test_rename_server(server):
""" Servers can be renamed at any time, with few restrictions.
After creation, the name can be any 1-255 characters long. Unicode is
supported. The server's hostname in the guest VM is not changed.
"""
# Server names can be chosen quite freely
server.update(name='hal-9000.example.org')
assert server.name == 'hal-9000.example.org'
# Up to 255 characters are allowed
server.update(name='0' * 255)
assert len(server.name) == 255
# Feel free to use special characters
server.update(name='🤖-host')
assert server.name == '🤖-host'
server.update(name='acme | cluster nodes | master')
assert server.name == 'acme | cluster nodes | master'
def test_reboot_server(server):
""" Servers can be rebooted using the API or through the shell. """
# Get the time of the last boot
boot_timestamp = server.output_of('uptime --since')
# Reboot the server through the API (automatically reconnects)
server.reboot()
# Make sure that the reboot happened
assert server.output_of('uptime --since') != boot_timestamp
# Update the time of the last boot
boot_timestamp = server.output_of('uptime --since')
# Try to reboot through the shell
server.run('sudo systemctl reboot')
# Wait for SSH to be unavailable, or we might re-connect prematurely
server.wait_for_port(22, 'offline', timeout=10)
# Wait for the server to finish rebooting
server.connect()
# Make sure that this reboot happened as well
assert server.output_of('uptime --since') != boot_timestamp
def test_stop_and_start_server(server):
""" Servers can be stopped using the API or through the shell. """
# Get the time of the last boot
boot_timestamp = server.output_of('uptime --since')
# Stop the server through the API, then start it
server.stop()
server.start()
# Make sure that the reboot happened
server.output_of('uptime --since') != boot_timestamp
# Update the time of the last boot
boot_timestamp = server.output_of('uptime --since')
# Try to stop the server through the shell
server.run('sudo systemctl poweroff')
# Wait for the server to fully stop
server.wait_for(status='stopped')
# Start it using the API
server.start()
# Make sure the server was started
assert server.output_of('uptime --since') != boot_timestamp
def test_rename_server_group(server_group):
""" Server groups can be renamed freely. """
# Change the name of the server group
server_group.rename('frontend-servers')
# Make sure the name has been set
assert server_group.name == 'frontend-servers'
def test_no_cpu_steal_on_plus_flavor(create_server, image):
""" Plus flavor servers have dedicated CPU cores, which means other tenants
cannot cause CPU steal as they might with shared CPU cores.
Note that due to an implementation detail, CPU steal of up to 1% may be
observed.
"""
# Create a Plus-8-2 instance
server = create_server(image=image, flavor='plus-8-2')
# We need a stress tool to saturate our cores
server.assert_run('sudo apt update --allow-releaseinfo-change ')
server.assert_run('sudo apt install -y stress')
# Run stress in the background, on all cores
server.assert_run('sudo systemd-run stress --cpu 2')
# Observe CPU steal for 30 seconds
steal = server.output_of(oneliner("""
top -n 60 -d 0.5 -b
| egrep '^%Cpu'
| egrep -o '[0-9.]+ st'
"""))
max_steal = max(extract_number(line) for line in steal.splitlines())
# Make sure the CPU steal does not exceed 1%
assert max_steal <= 1
def test_random_number_generator(server):
""" Our servers come with a paravirtual random number generator.
See https://www.cloudscale.ch/en/news/2020/03/09/entropy-random-numbers.
"""
# Make sure the 'rdrand' CPU feature is enabled
server.assert_run('grep -q rdrand /proc/cpuinfo')
# Ensure that we can also see the hwrng virtio device
path = '/sys/devices/virtual/misc/hw_random/rng_available'
server.assert_run(f'grep -q virtio_rng {path}')
def test_metadata_on_all_images(server):
""" All servers have access to metadata through a link-local IP address and
a read-only config drive.
See https://docs.openstack.org/nova/latest/user/metadata.html
"""
# The config drive is usually available as /dev/sr0. But to be sure what
# its device path is, we can query the block devices for a device with the
# label 'config-2'
path = server.output_of(
"lsblk --paths --output LABEL,NAME | grep config-2 | awk '{print $2}'")
# We can mount this drive as a CD-ROM
server.assert_run(f'sudo mkdir /mnt/config-drive')
server.assert_run(f'sudo mount -t iso9660 {path} -o ro /mnt/config-drive')
# Amongst other things we'll find the UUID of the server in the metadata
assert server.uuid in server.output_of(
'cat /mnt/config-drive/openstack/latest/meta_data.json')
# We can find the same information on the metadata service
assert server.uuid in server.http_get(
'http://169.254.169.254/openstack/latest/meta_data.json')