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