blob: e2190cf7ea493cb9e02c318e8c9dbe4e94298704 [file] [log] [blame]
Soren Hansenbc1d3a02011-09-08 13:33:17 +02001
2import json
3import time
4
5from kong import exceptions
6from kong import openstack
Soren Hansen6adacc82011-09-09 13:34:35 +02007from kong import tests
Soren Hansenbc1d3a02011-09-08 13:33:17 +02008from kong.common import ssh
9
10import unittest2 as unittest
11
12
Soren Hansen6adacc82011-09-09 13:34:35 +020013class ServerActionsTest(tests.FunctionalTest):
Soren Hansenbc1d3a02011-09-08 13:33:17 +020014
15 multi_node = openstack.Manager().config.env.multi_node
16
17 def setUp(self):
Soren Hansen6adacc82011-09-09 13:34:35 +020018 super(ServerActionsTest, self).setUp()
Soren Hansend6b047a2011-09-09 13:39:32 +020019 self.os = openstack.Manager(self.nova)
Soren Hansenbc1d3a02011-09-08 13:33:17 +020020
21 self.image_ref = self.os.config.env.image_ref
22 self.image_ref_alt = self.os.config.env.image_ref_alt
23 self.flavor_ref = self.os.config.env.flavor_ref
24 self.flavor_ref_alt = self.os.config.env.flavor_ref_alt
25 self.ssh_timeout = self.os.config.nova.ssh_timeout
26
27 self.server_password = 'testpwd'
28 self.server_name = 'testserver'
29
30 expected_server = {
31 'name' : self.server_name,
32 'imageRef' : self.image_ref,
33 'flavorRef' : self.flavor_ref,
34 'adminPass' : self.server_password,
35 }
36
37 created_server = self.os.nova.create_server(expected_server)
38
39 self.server_id = created_server['id']
40 self._wait_for_status(self.server_id, 'ACTIVE')
41
42 server = self.os.nova.get_server(self.server_id)
43
44 # KNOWN-ISSUE lp?
45 #self.access_ip = server['accessIPv4']
46 self.access_ip = server['addresses']['public'][0]['addr']
47
48 # Ensure server came up
49 self._assert_ssh_password()
50
51 def tearDown(self):
52 self.os.nova.delete_server(self.server_id)
53
54 def _get_ssh_client(self, password):
55 return ssh.Client(self.access_ip, 'root', password, self.ssh_timeout)
56
57 def _assert_ssh_password(self, password=None):
58 _password = password or self.server_password
59 client = self._get_ssh_client(_password)
60 self.assertTrue(client.test_connection_auth())
61
62 def _wait_for_status(self, server_id, status):
63 try:
64 self.os.nova.wait_for_server_status(server_id, status)
65 except exceptions.TimeoutException:
66 self.fail("Server failed to change status to %s" % status)
67
68 def _get_boot_time(self):
69 """Return the time the server was started"""
70 output = self._read_file("/proc/uptime")
71 uptime = float(output.split().pop(0))
72 return time.time() - uptime
73
74 def _write_file(self, filename, contents):
75 return self._exec_command("echo -n %s > %s" % (contents, filename))
76
77 def _read_file(self, filename):
78 return self._exec_command("cat %s" % filename)
79
80 def _exec_command(self, command):
81 client = self._get_ssh_client(self.server_password)
82 return client.exec_command(command)
83
84 def test_reboot_server_soft(self):
85 """Reboot a server (SOFT)"""
86
87 # SSH and get the uptime
88 initial_time_started = self._get_boot_time()
89
90 # Make reboot request
91 post_body = json.dumps({
92 'reboot' : {
93 'type' : 'SOFT',
94 }
95 })
96 url = "/servers/%s/action" % self.server_id
97 response, body = self.os.nova.request('POST', url, body=post_body)
98 self.assertEqual(response['status'], '202')
99
100 # Assert status transition
101 # KNOWN-ISSUE
102 #self.os.nova.wait_for_server_status(self.server_id, 'REBOOT')
103 ssh_client = self._get_ssh_client(self.server_password)
104 ssh_client.connect_until_closed()
105 self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
106
107 # SSH and verify uptime is less than before
108 post_reboot_time_started = self._get_boot_time()
109 self.assertTrue(initial_time_started < post_reboot_time_started)
110
111 def test_reboot_server_hard(self):
112 """Reboot a server (HARD)"""
113
114 # SSH and get the uptime
115 initial_time_started = self._get_boot_time()
116
117 # Make reboot request
118 post_body = json.dumps({
119 'reboot' : {
120 'type' : 'HARD',
121 }
122 })
123 url = "/servers/%s/action" % self.server_id
124 response, body = self.os.nova.request('POST', url, body=post_body)
125 self.assertEqual(response['status'], '202')
126
127 # Assert status transition
128 # KNOWN-ISSUE
129 #self.os.nova.wait_for_server_status(self.server_id, 'HARD_REBOOT')
130 ssh_client = self._get_ssh_client(self.server_password)
131 ssh_client.connect_until_closed()
132 self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
133
134 # SSH and verify uptime is less than before
135 post_reboot_time_started = self._get_boot_time()
136 self.assertTrue(initial_time_started < post_reboot_time_started)
137
138 def test_change_server_password(self):
139 """Change root password of a server"""
140
141 # SSH into server using original password
142 self._assert_ssh_password()
143
144 # Change server password
145 post_body = json.dumps({
146 'changePassword' : {
147 'adminPass' : 'test123',
148 }
149 })
150 url = '/servers/%s/action' % self.server_id
151 response, body = self.os.nova.request('POST', url, body=post_body)
152
153 # Assert status transition
154 self.assertEqual('202', response['status'])
155 # KNOWN-ISSUE
156 #self.os.nova.wait_for_server_status(self.server_id, 'PASSWORD')
157 self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
158
159 # SSH into server using new password
160 self._assert_ssh_password('test123')
161
162 def test_rebuild_server(self):
163 """Rebuild a server"""
164
165 filename = '/tmp/testfile'
166 contents = 'WORDS'
167 self._write_file(filename, contents)
168 self.assertEqual(self._read_file(filename), contents)
169
170 # Make rebuild request
171 post_body = json.dumps({
172 'rebuild' : {
173 'imageRef' : self.image_ref_alt,
174 }
175 })
176 url = '/servers/%s/action' % self.server_id
177 response, body = self.os.nova.request('POST', url, body=post_body)
178
179 # Ensure correct status transition
180 self.assertEqual('202', response['status'])
181 # KNOWN-ISSUE
182 #self.os.nova.wait_for_server_status(self.server_id, 'REBUILD')
183 self.os.nova.wait_for_server_status(self.server_id, 'BUILD')
184 self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
185
186 # Treats an issue where we ssh'd in too soon after rebuild
187 time.sleep(30)
188
189 # Check that the instance's imageRef matches the new imageRef
190 server = self.os.nova.get_server(self.server_id)
191 ref_match = self.image_ref_alt == server['image']['links'][0]['href']
192 id_match = self.image_ref_alt == server['image']['id']
193 self.assertTrue(ref_match or id_match)
194
195 # SSH into the server to ensure it came back up
196 self._assert_ssh_password()
197
198 # make sure file is gone
199 self.assertEqual(self._read_file(filename), '')
200
201 @unittest.skipIf(not multi_node, 'Multiple compute nodes required')
202 def test_resize_server_confirm(self):
203 """Resize a server"""
204 # Make resize request
205 post_body = json.dumps({
206 'resize' : {
207 'flavorRef': self.flavor_ref_alt,
208 }
209 })
210 url = '/servers/%s/action' % self.server_id
211 response, body = self.os.nova.request('POST', url, body=post_body)
212
213 # Wait for status transition
214 self.assertEqual('202', response['status'])
215 # KNOWN-ISSUE
216 #self.os.nova.wait_for_server_status(self.server_id, 'VERIFY_RESIZE')
217 self.os.nova.wait_for_server_status(self.server_id, 'RESIZE-CONFIRM')
218
219 # Ensure API reports new flavor
220 server = self.os.nova.get_server(self.server_id)
221 self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
222
223 #SSH into the server to ensure it came back up
224 self._assert_ssh_password()
225
226 # Make confirmResize request
227 post_body = json.dumps({
228 'confirmResize' : 'null'
229 })
230 url = '/servers/%s/action' % self.server_id
231 response, body = self.os.nova.request('POST', url, body=post_body)
232
233 # Wait for status transition
234 self.assertEqual('204', response['status'])
235 self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
236
237 # Ensure API still reports new flavor
238 server = self.os.nova.get_server(self.server_id)
239 self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
240
241 @unittest.skipIf(not multi_node, 'Multiple compute nodes required')
242 def test_resize_server_revert(self):
243 """Resize a server, then revert"""
244
245 # Make resize request
246 post_body = json.dumps({
247 'resize' : {
248 'flavorRef': self.flavor_ref_alt,
249 }
250 })
251 url = '/servers/%s/action' % self.server_id
252 response, body = self.os.nova.request('POST', url, body=post_body)
253
254 # Wait for status transition
255 self.assertEqual('202', response['status'])
256 # KNOWN-ISSUE
257 #self.os.nova.wait_for_server_status(self.server_id, 'VERIFY_RESIZE')
258 self.os.nova.wait_for_server_status(self.server_id, 'RESIZE-CONFIRM')
259
260 # SSH into the server to ensure it came back up
261 self._assert_ssh_password()
262
263 # Ensure API reports new flavor
264 server = self.os.nova.get_server(self.server_id)
265 self.assertEqual(self.flavor_ref_alt, server['flavor']['id'])
266
267 # Make revertResize request
268 post_body = json.dumps({
269 'revertResize' : 'null'
270 })
271 url = '/servers/%s/action' % self.server_id
272 response, body = self.os.nova.request('POST', url, body=post_body)
273
274 # Assert status transition
275 self.assertEqual('202', response['status'])
276 self.os.nova.wait_for_server_status(self.server_id, 'ACTIVE')
277
278 # Ensure flavor ref was reverted to original
279 server = self.os.nova.get_server(self.server_id)
280 self.assertEqual(self.flavor_ref, server['flavor']['id'])
281
282
283class SnapshotTests(unittest.TestCase):
284
285 def setUp(self):
286 self.os = openstack.Manager()
287
288 self.image_ref = self.os.config.env.image_ref
289 self.flavor_ref = self.os.config.env.flavor_ref
290 self.ssh_timeout = self.os.config.nova.ssh_timeout
291
292 self.server_name = 'testserver'
293
294 expected_server = {
295 'name' : self.server_name,
296 'imageRef' : self.image_ref,
297 'flavorRef' : self.flavor_ref,
298 }
299
300 created_server = self.os.nova.create_server(expected_server)
301 self.server_id = created_server['id']
302
303 def tearDown(self):
304 self.os.nova.delete_server(self.server_id)
305
306 def _wait_for_status(self, server_id, status):
307 try:
308 self.os.nova.wait_for_server_status(server_id, status)
309 except exceptions.TimeoutException:
310 self.fail("Server failed to change status to %s" % status)
311
312 def test_snapshot_server_active(self):
313 """Create image from an existing server"""
314
315 # Wait for server to come up before running this test
316 self._wait_for_status(self.server_id, 'ACTIVE')
317
318 # Create snapshot
319 image_data = {'name' : 'backup'}
320 req_body = json.dumps({'createImage': image_data})
321 url = '/servers/%s/action' % self.server_id
322 response, body = self.os.nova.request('POST', url, body=req_body)
323 print response
324 print body
325
326 self.assertEqual(response['status'], '202')
327 image_ref = response['location']
328 snapshot_id = image_ref.rsplit('/',1)[1]
329
330 # Get snapshot and check its attributes
331 resp, body = self.os.nova.request('GET', '/images/%s' % snapshot_id)
332 snapshot = json.loads(body)['image']
333 self.assertEqual(snapshot['name'], image_data['name'])
334 server_ref = snapshot['server']['links'][0]['href']
335 self.assertTrue(server_ref.endswith('/%s' % self.server_id))
336
337 # Ensure image is actually created
338 self.os.nova.wait_for_image_status(snapshot['id'], 'ACTIVE')
339
340 # Cleaning up
341 self.os.nova.request('DELETE', '/images/%s' % snapshot_id)
342
343 def test_snapshot_server_inactive(self):
344 """Ensure inability to snapshot server in BUILD state"""
345
346 # Create snapshot
347 req_body = json.dumps({'createImage': {'name' : 'backup'}})
348 url = '/servers/%s/action' % self.server_id
349 response, body = self.os.nova.request('POST', url, body=req_body)
350
351 # KNOWN-ISSUE - we shouldn't be able to snapshot a building server
352 #self.assertEqual(response['status'], '400') # what status code?
353 self.assertEqual(response['status'], '202')
354 snapshot_id = response['location'].rsplit('/', 1)[1]
355 # Delete image for now, won't need this once correct status code is in
356 self.os.nova.request('DELETE', '/images/%s' % snapshot_id)