12
12
# License for the specific language governing permissions and limitations
13
13
# under the License.
22
from nose.plugins.attrib import attr
19
from heat.engine import environment
24
20
from heat.tests.v1_1 import fakes
25
21
from heat.engine.resources import instance as instances
22
from heat.common import exception
26
23
from heat.common import template_format
27
24
from heat.engine import parser
25
from heat.engine import resource
26
from heat.engine import scheduler
28
27
from heat.openstack.common import uuidutils
31
@attr(tag=['unit', 'resource', 'instance'])
33
class instancesTest(unittest.TestCase):
28
from heat.tests.common import HeatTestCase
29
from heat.tests import utils
30
from heat.tests.utils import setup_dummy_db
35
"AWSTemplateFormatVersion" : "2010-09-09",
36
"Description" : "WordPress",
39
"Description" : "KeyName",
46
"Type": "AWS::EC2::Instance",
48
"ImageId" : "F17-x86_64-gold",
49
"InstanceType" : "m1.large",
51
"UserData" : "wordpress"
59
class InstancesTest(HeatTestCase):
61
super(InstancesTest, self).setUp()
36
62
self.fc = fakes.FakeClient()
37
self.path = os.path.dirname(os.path.realpath(__file__)).\
38
replace('heat/tests', 'templates')
42
print "instancesTest teardown complete"
65
def _setup_test_stack(self, stack_name):
66
t = template_format.parse(wp_template)
67
template = parser.Template(t)
68
stack = parser.Stack(None, stack_name, template,
69
environment.Environment({'KeyName': 'test'}),
70
stack_id=uuidutils.generate_uuid())
73
def _setup_test_instance(self, return_server, name, image_id=None):
74
stack_name = '%s_stack' % name
75
(t, stack) = self._setup_test_stack(stack_name)
77
t['Resources']['WebServer']['Properties']['ImageId'] = \
78
image_id or 'CentOS 5.2'
79
t['Resources']['WebServer']['Properties']['InstanceType'] = \
81
instance = instances.Instance('%s_name' % name,
82
t['Resources']['WebServer'], stack)
84
self.m.StubOutWithMock(instance, 'nova')
85
instance.nova().MultipleTimes().AndReturn(self.fc)
87
instance.t = instance.stack.resolve_runtime_data(instance.t)
89
# need to resolve the template functions
90
server_userdata = instance._build_userdata(
91
instance.t['Properties']['UserData'])
92
self.m.StubOutWithMock(self.fc.servers, 'create')
93
self.fc.servers.create(
94
image=1, flavor=1, key_name='test',
95
name=utils.PhysName(stack_name, instance.name),
97
userdata=server_userdata, scheduler_hints=None,
98
meta=None, nics=None, availability_zone=None).AndReturn(
103
def _create_test_instance(self, return_server, name):
104
instance = self._setup_test_instance(return_server, name)
106
scheduler.TaskRunner(instance.create)()
44
109
def test_instance_create(self):
45
f = open("%s/WordPress_Single_Instance_gold.template" % self.path)
46
t = template_format.parse(f.read())
49
stack_name = 'instance_create_test_stack'
50
template = parser.Template(t)
51
params = parser.Parameters(stack_name, template, {'KeyName': 'test'})
52
stack = parser.Stack(None, stack_name, template, params,
53
stack_id=uuidutils.generate_uuid())
110
return_server = self.fc.servers.list()[1]
111
instance = self._create_test_instance(return_server,
112
'test_instance_create')
113
# this makes sure the auto increment worked on instance creation
114
self.assertTrue(instance.id > 0)
116
expected_ip = return_server.networks['public'][0]
117
self.assertEqual(instance.FnGetAtt('PublicIp'), expected_ip)
118
self.assertEqual(instance.FnGetAtt('PrivateIp'), expected_ip)
119
self.assertEqual(instance.FnGetAtt('PrivateDnsName'), expected_ip)
120
self.assertEqual(instance.FnGetAtt('PrivateDnsName'), expected_ip)
124
def test_instance_create_with_image_id(self):
125
return_server = self.fc.servers.list()[1]
126
instance = self._setup_test_instance(return_server,
127
'test_instance_create_image_id',
129
self.m.StubOutWithMock(uuidutils, "is_uuid_like")
130
uuidutils.is_uuid_like('1').AndReturn(True)
133
scheduler.TaskRunner(instance.create)()
135
# this makes sure the auto increment worked on instance creation
136
self.assertTrue(instance.id > 0)
138
expected_ip = return_server.networks['public'][0]
139
self.assertEqual(instance.FnGetAtt('PublicIp'), expected_ip)
140
self.assertEqual(instance.FnGetAtt('PrivateIp'), expected_ip)
141
self.assertEqual(instance.FnGetAtt('PrivateDnsName'), expected_ip)
142
self.assertEqual(instance.FnGetAtt('PrivateDnsName'), expected_ip)
146
def test_instance_create_image_name_err(self):
147
stack_name = 'test_instance_create_image_name_err_stack'
148
(t, stack) = self._setup_test_stack(stack_name)
150
# create an instance with non exist image name
151
t['Resources']['WebServer']['Properties']['ImageId'] = 'Slackware'
152
instance = instances.Instance('instance_create_image_err',
153
t['Resources']['WebServer'], stack)
155
self.m.StubOutWithMock(instance, 'nova')
156
instance.nova().MultipleTimes().AndReturn(self.fc)
159
self.assertRaises(exception.ImageNotFound, instance.handle_create)
163
def test_instance_create_duplicate_image_name_err(self):
164
stack_name = 'test_instance_create_image_name_err_stack'
165
(t, stack) = self._setup_test_stack(stack_name)
167
# create an instance with a non unique image name
55
168
t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2'
56
t['Resources']['WebServer']['Properties']['InstanceType'] = \
58
instance = instances.Instance('create_instance_name',
59
t['Resources']['WebServer'], stack)
61
self.m.StubOutWithMock(instance, 'nova')
62
instance.nova().MultipleTimes().AndReturn(self.fc)
64
instance.t = instance.stack.resolve_runtime_data(instance.t)
66
# need to resolve the template functions
67
server_userdata = instance._build_userdata(
68
instance.t['Properties']['UserData'])
69
self.m.StubOutWithMock(self.fc.servers, 'create')
70
self.fc.servers.create(
71
image=1, flavor=1, key_name='test',
72
name='%s.%s' % (stack_name, instance.name),
74
userdata=server_userdata, scheduler_hints=None,
75
meta=None, nics=None, availability_zone=None).AndReturn(
76
self.fc.servers.list()[1])
79
self.assertEqual(instance.create(), None)
81
# this makes sure the auto increment worked on instance creation
82
self.assertTrue(instance.id > 0)
84
self.assertEqual(instance.FnGetAtt('PublicIp'), '4.5.6.7')
85
self.assertEqual(instance.FnGetAtt('PrivateIp'), '4.5.6.7')
86
self.assertEqual(instance.FnGetAtt('PrivateDnsName'), '4.5.6.7')
87
self.assertEqual(instance.FnGetAtt('PrivateDnsName'), '4.5.6.7')
169
instance = instances.Instance('instance_create_image_err',
170
t['Resources']['WebServer'], stack)
172
self.m.StubOutWithMock(instance, 'nova')
173
instance.nova().MultipleTimes().AndReturn(self.fc)
174
self.m.StubOutWithMock(self.fc.client, "get_images_detail")
175
self.fc.client.get_images_detail().AndReturn((
176
200, {'images': [{'id': 1, 'name': 'CentOS 5.2'},
177
{'id': 4, 'name': 'CentOS 5.2'}]}))
180
self.assertRaises(exception.NoUniqueImageFound, instance.handle_create)
184
def test_instance_create_image_id_err(self):
185
stack_name = 'test_instance_create_image_id_err_stack'
186
(t, stack) = self._setup_test_stack(stack_name)
188
# create an instance with non exist image Id
189
t['Resources']['WebServer']['Properties']['ImageId'] = '1'
190
instance = instances.Instance('instance_create_image_err',
191
t['Resources']['WebServer'], stack)
193
self.m.StubOutWithMock(instance, 'nova')
194
instance.nova().MultipleTimes().AndReturn(self.fc)
195
self.m.StubOutWithMock(uuidutils, "is_uuid_like")
196
uuidutils.is_uuid_like('1').AndReturn(True)
197
self.m.StubOutWithMock(self.fc.client, "get_images_1")
198
self.fc.client.get_images_1().AndRaise(
199
instances.clients.novaclient.exceptions.NotFound(404))
202
self.assertRaises(exception.ImageNotFound, instance.handle_create)
206
def test_instance_validate(self):
207
stack_name = 'test_instance_validate_stack'
208
(t, stack) = self._setup_test_stack(stack_name)
210
# create an instance with non exist image Id
211
t['Resources']['WebServer']['Properties']['ImageId'] = '1'
212
instance = instances.Instance('instance_create_image_err',
213
t['Resources']['WebServer'], stack)
215
self.m.StubOutWithMock(instance, 'nova')
216
instance.nova().MultipleTimes().AndReturn(self.fc)
218
self.m.StubOutWithMock(uuidutils, "is_uuid_like")
219
uuidutils.is_uuid_like('1').AndReturn(True)
222
self.assertEqual(instance.validate(), None)
89
224
self.m.VerifyAll()
91
226
def test_instance_create_delete(self):
92
f = open("%s/WordPress_Single_Instance_gold.template" % self.path)
93
t = template_format.parse(f.read())
96
stack_name = 'instance_create_delete_test_stack'
97
template = parser.Template(t)
98
params = parser.Parameters(stack_name, template, {'KeyName': 'test'})
99
stack = parser.Stack(None, stack_name, template, params,
100
stack_id=uuidutils.generate_uuid())
102
t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2'
103
t['Resources']['WebServer']['Properties']['InstanceType'] = \
105
instance = instances.Instance('create_delete_instance_name',
106
t['Resources']['WebServer'], stack)
108
self.m.StubOutWithMock(instance, 'nova')
109
instance.nova().MultipleTimes().AndReturn(self.fc)
111
instance.t = instance.stack.resolve_runtime_data(instance.t)
113
# need to resolve the template functions
114
server_userdata = instance._build_userdata(
115
instance.t['Properties']['UserData'])
116
self.m.StubOutWithMock(self.fc.servers, 'create')
117
self.fc.servers.create(
118
image=1, flavor=1, key_name='test',
119
name='%s.%s' % (stack_name, instance.name),
120
security_groups=None,
121
userdata=server_userdata, scheduler_hints=None,
122
meta=None, nics=None, availability_zone=None).AndReturn(
123
self.fc.servers.list()[1])
126
self.assertEqual(instance.create(), None)
227
return_server = self.fc.servers.list()[1]
228
instance = self._create_test_instance(return_server,
229
'test_instance_create_delete')
127
230
instance.resource_id = 1234
129
232
# this makes sure the auto increment worked on instance creation
137
240
instance.delete()
138
241
self.assertTrue(instance.resource_id is None)
139
self.assertEqual(instance.state, instance.DELETE_COMPLETE)
242
self.assertEqual(instance.state, (instance.DELETE, instance.COMPLETE))
140
243
self.m.VerifyAll()
142
245
def test_instance_update_metadata(self):
143
f = open("%s/WordPress_Single_Instance_gold.template" % self.path)
144
t = template_format.parse(f.read())
147
stack_name = 'instance_update_test_stack'
148
template = parser.Template(t)
149
params = parser.Parameters(stack_name, template, {'KeyName': 'test'})
150
stack = parser.Stack(None, stack_name, template, params,
151
stack_id=uuidutils.generate_uuid())
153
t['Resources']['WebServer']['Properties']['ImageId'] = 'CentOS 5.2'
154
t['Resources']['WebServer']['Properties']['InstanceType'] = \
156
instance = instances.Instance('create_instance_name',
157
t['Resources']['WebServer'], stack)
159
self.m.StubOutWithMock(instance, 'nova')
160
instance.nova().MultipleTimes().AndReturn(self.fc)
162
instance.t = instance.stack.resolve_runtime_data(instance.t)
164
# need to resolve the template functions
165
server_userdata = instance._build_userdata(
166
instance.t['Properties']['UserData'])
167
self.m.StubOutWithMock(self.fc.servers, 'create')
168
self.fc.servers.create(
169
image=1, flavor=1, key_name='test',
170
name='%s.%s' % (stack_name, instance.name),
171
security_groups=None,
172
userdata=server_userdata, scheduler_hints=None,
173
meta=None, nics=None, availability_zone=None).AndReturn(
174
self.fc.servers.list()[1])
177
self.assertEqual(instance.create(), None)
246
return_server = self.fc.servers.list()[1]
247
instance = self._create_test_instance(return_server,
248
'test_instance_update')
179
250
update_template = copy.deepcopy(instance.t)
180
251
update_template['Metadata'] = {'test': 123}
181
self.assertEqual(instance.update(update_template),
182
instance.UPDATE_COMPLETE)
252
self.assertEqual(None, instance.update(update_template))
183
253
self.assertEqual(instance.metadata, {'test': 123})
255
def test_instance_update_replace(self):
256
return_server = self.fc.servers.list()[1]
257
instance = self._create_test_instance(return_server,
258
'test_instance_update')
260
update_template = copy.deepcopy(instance.t)
261
update_template['Notallowed'] = {'test': 123}
262
self.assertRaises(resource.UpdateReplace,
263
instance.update, update_template)
265
def test_instance_update_properties(self):
266
return_server = self.fc.servers.list()[1]
267
instance = self._create_test_instance(return_server,
268
'test_instance_update')
270
update_template = copy.deepcopy(instance.t)
271
update_template['Properties']['KeyName'] = 'mustreplace'
272
self.assertRaises(resource.UpdateReplace,
273
instance.update, update_template)
275
def test_instance_status_build(self):
276
return_server = self.fc.servers.list()[0]
277
instance = self._setup_test_instance(return_server,
278
'test_instance_status_build')
279
instance.resource_id = 1234
281
# Bind fake get method which Instance.check_create_complete will call
282
def activate_status(server):
283
server.status = 'ACTIVE'
284
return_server.get = activate_status.__get__(return_server)
287
scheduler.TaskRunner(instance.create)()
288
self.assertEqual(instance.state, (instance.CREATE, instance.COMPLETE))
290
def test_instance_status_suspend_immediate(self):
291
return_server = self.fc.servers.list()[1]
292
instance = self._create_test_instance(return_server,
293
'test_instance_suspend')
295
instance.resource_id = 1234
298
# Override the get_servers_1234 handler status to SUSPENDED
299
d = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
300
d['server']['status'] = 'SUSPENDED'
301
self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
302
get = self.fc.client.get_servers_1234
303
get().AndReturn((200, d))
306
scheduler.TaskRunner(instance.suspend)()
307
self.assertEqual(instance.state, (instance.SUSPEND, instance.COMPLETE))
311
def test_instance_status_resume_immediate(self):
312
return_server = self.fc.servers.list()[1]
313
instance = self._create_test_instance(return_server,
314
'test_instance_resume')
316
instance.resource_id = 1234
319
# Override the get_servers_1234 handler status to SUSPENDED
320
d = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
321
d['server']['status'] = 'ACTIVE'
322
self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
323
get = self.fc.client.get_servers_1234
324
get().AndReturn((200, d))
326
instance.state_set(instance.SUSPEND, instance.COMPLETE)
328
scheduler.TaskRunner(instance.resume)()
329
self.assertEqual(instance.state, (instance.RESUME, instance.COMPLETE))
333
def test_instance_status_suspend_wait(self):
334
return_server = self.fc.servers.list()[1]
335
instance = self._create_test_instance(return_server,
336
'test_instance_suspend')
338
instance.resource_id = 1234
341
# Override the get_servers_1234 handler status to SUSPENDED, but
342
# return the ACTIVE state first (twice, so we sleep)
343
d1 = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
344
d2 = copy.deepcopy(d1)
345
d1['server']['status'] = 'ACTIVE'
346
d2['server']['status'] = 'SUSPENDED'
347
self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
348
get = self.fc.client.get_servers_1234
349
get().AndReturn((200, d1))
350
get().AndReturn((200, d1))
351
get().AndReturn((200, d2))
354
scheduler.TaskRunner(instance.suspend)()
355
self.assertEqual(instance.state, (instance.SUSPEND, instance.COMPLETE))
359
def test_instance_status_resume_wait(self):
360
return_server = self.fc.servers.list()[1]
361
instance = self._create_test_instance(return_server,
362
'test_instance_resume')
364
instance.resource_id = 1234
367
# Override the get_servers_1234 handler status to ACTIVE, but
368
# return the SUSPENDED state first (twice, so we sleep)
369
d1 = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
370
d2 = copy.deepcopy(d1)
371
d1['server']['status'] = 'SUSPENDED'
372
d2['server']['status'] = 'ACTIVE'
373
self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
374
get = self.fc.client.get_servers_1234
375
get().AndReturn((200, d1))
376
get().AndReturn((200, d1))
377
get().AndReturn((200, d2))
380
instance.state_set(instance.SUSPEND, instance.COMPLETE)
382
scheduler.TaskRunner(instance.resume)()
383
self.assertEqual(instance.state, (instance.RESUME, instance.COMPLETE))
387
def test_instance_suspend_volumes_step(self):
388
return_server = self.fc.servers.list()[1]
389
instance = self._create_test_instance(return_server,
390
'test_instance_suspend')
392
instance.resource_id = 1234
395
# Override the get_servers_1234 handler status to SUSPENDED
396
d = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
397
d['server']['status'] = 'SUSPENDED'
399
# Return a dummy PollingTaskGroup to make check_suspend_complete step
402
dummy_tg = scheduler.PollingTaskGroup([dummy_detach, dummy_detach])
403
self.m.StubOutWithMock(instance, '_detach_volumes_task')
404
instance._detach_volumes_task().AndReturn(dummy_tg)
406
self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
407
get = self.fc.client.get_servers_1234
408
get().AndReturn((200, d))
411
scheduler.TaskRunner(instance.suspend)()
412
self.assertEqual(instance.state, (instance.SUSPEND, instance.COMPLETE))
416
def test_instance_resume_volumes_step(self):
417
return_server = self.fc.servers.list()[1]
418
instance = self._create_test_instance(return_server,
419
'test_instance_resume')
421
instance.resource_id = 1234
424
# Override the get_servers_1234 handler status to ACTIVE
425
d = {'server': self.fc.client.get_servers_detail()[1]['servers'][0]}
426
d['server']['status'] = 'ACTIVE'
428
# Return a dummy PollingTaskGroup to make check_resume_complete step
431
dummy_tg = scheduler.PollingTaskGroup([dummy_attach, dummy_attach])
432
self.m.StubOutWithMock(instance, '_attach_volumes_task')
433
instance._attach_volumes_task().AndReturn(dummy_tg)
435
self.m.StubOutWithMock(self.fc.client, 'get_servers_1234')
436
get = self.fc.client.get_servers_1234
437
get().AndReturn((200, d))
441
instance.state_set(instance.SUSPEND, instance.COMPLETE)
443
scheduler.TaskRunner(instance.resume)()
444
self.assertEqual(instance.state, (instance.RESUME, instance.COMPLETE))
448
def test_instance_status_build_spawning(self):
449
self._test_instance_status_not_build_active('BUILD(SPAWNING)')
451
def test_instance_status_hard_reboot(self):
452
self._test_instance_status_not_build_active('HARD_REBOOT')
454
def test_instance_status_password(self):
455
self._test_instance_status_not_build_active('PASSWORD')
457
def test_instance_status_reboot(self):
458
self._test_instance_status_not_build_active('REBOOT')
460
def test_instance_status_rescue(self):
461
self._test_instance_status_not_build_active('RESCUE')
463
def test_instance_status_resize(self):
464
self._test_instance_status_not_build_active('RESIZE')
466
def test_instance_status_revert_resize(self):
467
self._test_instance_status_not_build_active('REVERT_RESIZE')
469
def test_instance_status_shutoff(self):
470
self._test_instance_status_not_build_active('SHUTOFF')
472
def test_instance_status_suspended(self):
473
self._test_instance_status_not_build_active('SUSPENDED')
475
def test_instance_status_verify_resize(self):
476
self._test_instance_status_not_build_active('VERIFY_RESIZE')
478
def _test_instance_status_not_build_active(self, uncommon_status):
479
return_server = self.fc.servers.list()[0]
480
instance = self._setup_test_instance(return_server,
481
'test_instance_status_build')
482
instance.resource_id = 1234
484
# Bind fake get method which Instance.check_create_complete will call
485
def activate_status(server):
486
if hasattr(server, '_test_check_iterations'):
487
server._test_check_iterations += 1
489
server._test_check_iterations = 1
490
if server._test_check_iterations == 1:
491
server.status = uncommon_status
492
if server._test_check_iterations > 2:
493
server.status = 'ACTIVE'
494
return_server.get = activate_status.__get__(return_server)
497
scheduler.TaskRunner(instance.create)()
498
self.assertEqual(instance.state, (instance.CREATE, instance.COMPLETE))
185
502
def test_build_nics(self):
186
self.assertEqual(None, instances.Instance._build_nics([]))
187
self.assertEqual(None, instances.Instance._build_nics(None))
503
return_server = self.fc.servers.list()[1]
504
instance = self._create_test_instance(return_server,
507
self.assertEqual(None, instance._build_nics([]))
508
self.assertEqual(None, instance._build_nics(None))
188
509
self.assertEqual([
189
510
{'port-id': 'id3'}, {'port-id': 'id1'}, {'port-id': 'id2'}],
190
instances.Instance._build_nics([
511
instance._build_nics([
191
512
'id3', 'id1', 'id2']))
192
513
self.assertEqual([
193
514
{'port-id': 'id1'},
194
515
{'port-id': 'id2'},
195
{'port-id': 'id3'}], instances.Instance._build_nics([
516
{'port-id': 'id3'}], instance._build_nics([
196
517
{'NetworkInterfaceId': 'id3', 'DeviceIndex': '3'},
197
518
{'NetworkInterfaceId': 'id1', 'DeviceIndex': '1'},
198
519
{'NetworkInterfaceId': 'id2', 'DeviceIndex': 2},