Show an error message on instance launch error
Since instance creation happens asynchronously, all that currently happens on the Horizon side is the state changes from Creating to Error. This change checks the instance state during the Ajax request on the backend and returns an Ajax error message popup if the status is 'ERROR'. Change-Id: I259e27b7b6c73a38098514a701c235feb81af143 Closes-bug: 1236168.
This commit is contained in:
parent
9c08d883ea
commit
2dddc39a0b
@ -71,7 +71,7 @@ class Server(base.APIResourceWrapper):
|
||||
"""
|
||||
_attrs = ['addresses', 'attrs', 'id', 'image', 'links',
|
||||
'metadata', 'name', 'private_ip', 'public_ip', 'status', 'uuid',
|
||||
'image_name', 'VirtualInterfaces', 'flavor', 'key_name',
|
||||
'image_name', 'VirtualInterfaces', 'flavor', 'key_name', 'fault',
|
||||
'tenant_id', 'user_id', 'OS-EXT-STS:power_state',
|
||||
'OS-EXT-STS:task_state', 'OS-EXT-SRV-ATTR:instance_name',
|
||||
'OS-EXT-SRV-ATTR:host', 'created']
|
||||
|
@ -431,6 +431,29 @@ class SimpleDisassociateIP(tables.Action):
|
||||
return shortcuts.redirect("horizon:project:instances:index")
|
||||
|
||||
|
||||
def instance_fault_to_friendly_message(instance):
|
||||
fault = getattr(instance, 'fault', {})
|
||||
message = fault.get('message', _("Unknown"))
|
||||
default_message = _("Please try again later [Error: %s].") % message
|
||||
fault_map = {
|
||||
'NoValidHost': _("There is not enough capacity for this "
|
||||
"flavor in the selected availability zone. "
|
||||
"Try again later or select a different availability "
|
||||
"zone.")
|
||||
}
|
||||
return fault_map.get(message, default_message)
|
||||
|
||||
|
||||
def get_instance_error(instance):
|
||||
if instance.status.lower() != 'error':
|
||||
return None
|
||||
message = instance_fault_to_friendly_message(instance)
|
||||
preamble = _('Failed to launch instance "%s"'
|
||||
) % instance.name or instance.id
|
||||
message = string_concat(preamble, ': ', message)
|
||||
return message
|
||||
|
||||
|
||||
class UpdateRow(tables.Row):
|
||||
ajax = True
|
||||
|
||||
@ -438,6 +461,9 @@ class UpdateRow(tables.Row):
|
||||
instance = api.nova.server_get(request, instance_id)
|
||||
instance.full_flavor = api.nova.flavor_get(request,
|
||||
instance.flavor["id"])
|
||||
error = get_instance_error(instance)
|
||||
if error:
|
||||
messages.error(request, error)
|
||||
return instance
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from django.core.urlresolvers import reverse # noqa
|
||||
@ -2043,3 +2044,88 @@ class InstanceTests(test.TestCase):
|
||||
password=password,
|
||||
confirm_password=password)
|
||||
self.assertRedirectsNoFollow(res, INDEX_URL)
|
||||
|
||||
|
||||
class InstanceAjaxTests(test.TestCase):
|
||||
@test.create_stubs({api.nova: ("server_get",
|
||||
"flavor_get",
|
||||
"extension_supported"),
|
||||
api.neutron: ("is_extension_supported",)})
|
||||
def test_row_update(self):
|
||||
server = self.servers.first()
|
||||
instance_id = server.id
|
||||
flavor_id = server.flavor["id"]
|
||||
flavors = self.flavors.list()
|
||||
full_flavors = SortedDict([(f.id, f) for f in flavors])
|
||||
|
||||
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest))\
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'security-group')\
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.nova.server_get(IsA(http.HttpRequest), instance_id)\
|
||||
.AndReturn(server)
|
||||
api.nova.flavor_get(IsA(http.HttpRequest), flavor_id)\
|
||||
.AndReturn(full_flavors[flavor_id])
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
params = {'action': 'row_update',
|
||||
'table': 'instances',
|
||||
'obj_id': instance_id,
|
||||
}
|
||||
res = self.client.get('?'.join((INDEX_URL, urlencode(params))),
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertContains(res, server.name)
|
||||
|
||||
@test.create_stubs({api.nova: ("server_get",
|
||||
"flavor_get",
|
||||
"extension_supported"),
|
||||
api.neutron: ("is_extension_supported",)})
|
||||
def test_row_update_instance_error(self):
|
||||
server = self.servers.first()
|
||||
instance_id = server.id
|
||||
flavor_id = server.flavor["id"]
|
||||
flavors = self.flavors.list()
|
||||
full_flavors = SortedDict([(f.id, f) for f in flavors])
|
||||
|
||||
server.status = 'ERROR'
|
||||
server.fault = {"message": "NoValidHost",
|
||||
"code": 500,
|
||||
"details": "No valid host was found. \n "
|
||||
"File \"/mnt/stack/nova/nova/"
|
||||
"scheduler/filter_scheduler.py\", "
|
||||
"line 105, in schedule_run_instance\n "
|
||||
"raise exception.NoValidHost"
|
||||
"(reason=\"\")\n",
|
||||
"created": "2013-10-07T00:08:32Z"}
|
||||
|
||||
api.nova.extension_supported('AdminActions', IsA(http.HttpRequest))\
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.neutron.is_extension_supported(IsA(http.HttpRequest),
|
||||
'security-group')\
|
||||
.MultipleTimes().AndReturn(True)
|
||||
api.nova.server_get(IsA(http.HttpRequest), instance_id)\
|
||||
.AndReturn(server)
|
||||
api.nova.flavor_get(IsA(http.HttpRequest), flavor_id)\
|
||||
.AndReturn(full_flavors[flavor_id])
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
params = {'action': 'row_update',
|
||||
'table': 'instances',
|
||||
'obj_id': instance_id,
|
||||
}
|
||||
res = self.client.get('?'.join((INDEX_URL, urlencode(params))),
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertContains(res, server.name)
|
||||
self.assertTrue(res.has_header('X-Horizon-Messages'))
|
||||
messages = json.loads(res['X-Horizon-Messages'])
|
||||
self.assertEqual(len(messages), 1)
|
||||
# (Pdb) messages
|
||||
# [[u'error', u'Failed to launch instance "server_1": \
|
||||
# There is not enough capacity for this flavor in the \
|
||||
# selected availability zone. Try again later or select \
|
||||
# a different availability zone.', u'']]
|
||||
self.assertEqual(messages[0][0], 'error')
|
||||
self.assertTrue(messages[0][1].startswith('Failed'))
|
||||
|
Loading…
Reference in New Issue
Block a user