from heat.common import exception
from heat.engine.resources.network_interface import NetworkInterface
+from heat.openstack.common.gettextutils import _
from heat.openstack.common import log as logging
from heat.openstack.common import uuidutils
'PublicIp': ('Public IP address of the specified '
'instance.')}
- # template keys supported for handle_update, note trailing comma
- # is required for a single item to get a tuple not a string
- update_allowed_keys = ('Metadata',)
+ update_allowed_keys = ('Metadata', 'Properties')
+ update_allowed_properties = ('InstanceType',)
_deferred_server_statuses = ['BUILD',
'HARD_REBOOT',
security_groups = None
return security_groups
+ def _get_flavor_id(self, flavor):
+ flavor_id = None
+ flavor_list = self.nova().flavors.list()
+ for o in flavor_list:
+ if o.name == flavor:
+ flavor_id = o.id
+ break
+ if flavor_id is None:
+ raise exception.FlavorMissing(flavor_id=flavor)
+ return flavor_id
+
def handle_create(self):
security_groups = self._get_security_groups()
image_id = self._get_image_id(image_name)
- flavor_id = None
- flavor_list = self.nova().flavors.list()
- for o in flavor_list:
- if o.name == flavor:
- flavor_id = o.id
- break
- if flavor_id is None:
- raise exception.FlavorMissing(flavor_id=flavor)
+ flavor_id = self._get_flavor_id(flavor)
tags = {}
if self.properties['Tags']:
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
if 'Metadata' in tmpl_diff:
- self.metadata = tmpl_diff.get('Metadata', {})
+ self.metadata = tmpl_diff['Metadata']
+ if 'InstanceType' in prop_diff:
+ flavor = prop_diff['InstanceType']
+ flavor_id = self._get_flavor_id(flavor)
+ server = self.nova().servers.get(self.resource_id)
+ server.resize(flavor_id)
+ scheduler.TaskRunner(self._check_resize, server, flavor)()
+
+ def _check_resize(self, server, flavor):
+ """
+ Verify that the server is properly resized. If that's the case, confirm
+ the resize, if not raise an error.
+ """
+ yield
+ server.get()
+ while server.status == 'RESIZE':
+ yield
+ server.get()
+ if server.status == 'VERIFY_RESIZE':
+ server.confirm_resize()
+ else:
+ raise exception.Error(
+ "Resizing to '%s' failed, status '%s'" % (
+ flavor, server.status))
def metadata_update(self, new_metadata=None):
'''
self.assertEqual(None, instance.update(update_template))
self.assertEqual(instance.metadata, {'test': 123})
+ def test_instance_update_instance_type(self):
+ """
+ Instance.handle_update supports changing the InstanceType, and makes
+ the change making a resize API call against Nova.
+ """
+ return_server = self.fc.servers.list()[1]
+ return_server.id = 1234
+ instance = self._create_test_instance(return_server,
+ 'test_instance_update')
+
+ update_template = copy.deepcopy(instance.t)
+ update_template['Properties']['InstanceType'] = 'm1.small'
+
+ self.m.StubOutWithMock(self.fc.servers, 'get')
+ self.fc.servers.get(1234).AndReturn(return_server)
+
+ def activate_status(server):
+ server.status = 'VERIFY_RESIZE'
+ return_server.get = activate_status.__get__(return_server)
+
+ self.m.StubOutWithMock(self.fc.client, 'post_servers_1234_action')
+ self.fc.client.post_servers_1234_action(
+ body={'resize': {'flavorRef': 2}}).AndReturn((202, None))
+ self.fc.client.post_servers_1234_action(
+ body={'confirmResize': None}).AndReturn((202, None))
+ self.m.ReplayAll()
+
+ self.assertEqual(None, instance.update(update_template))
+ self.assertEqual(instance.state, (instance.UPDATE, instance.COMPLETE))
+ self.m.VerifyAll()
+
+ def test_instance_update_instance_type_failed(self):
+ """
+ If the status after a resize is not VERIFY_RESIZE, it means the resize
+ call failed, so we raise an explicit error.
+ """
+ return_server = self.fc.servers.list()[1]
+ return_server.id = 1234
+ instance = self._create_test_instance(return_server,
+ 'test_instance_update')
+
+ update_template = copy.deepcopy(instance.t)
+ update_template['Properties']['InstanceType'] = 'm1.small'
+
+ self.m.StubOutWithMock(self.fc.servers, 'get')
+ self.fc.servers.get(1234).AndReturn(return_server)
+
+ def activate_status(server):
+ server.status = 'ACTIVE'
+ return_server.get = activate_status.__get__(return_server)
+
+ self.m.StubOutWithMock(self.fc.client, 'post_servers_1234_action')
+ self.fc.client.post_servers_1234_action(
+ body={'resize': {'flavorRef': 2}}).AndReturn((202, None))
+ self.m.ReplayAll()
+
+ error = self.assertRaises(exception.ResourceFailure,
+ instance.update, update_template)
+ self.assertEqual(
+ "Error: Resizing to 'm1.small' failed, status 'ACTIVE'",
+ str(error))
+ self.assertEqual(instance.state, (instance.UPDATE, instance.FAILED))
+ self.m.VerifyAll()
+
def test_instance_update_replace(self):
return_server = self.fc.servers.list()[1]
instance = self._create_test_instance(return_server,