From 932f3aafd1c735a8ec9e158a94ebb983d6baeb0e Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Wed, 14 Dec 2011 16:15:52 +0900 Subject: Unbreak start instance and fixes bug 905270 This patch fixes the bug 905270 https://bugs.launchpad.net/nova/+bug/905270 According to EC2 documentation, EBS-instances that initiated shutdown result in stopped state. And then it can be started again. (On the other hand non-EBS instance result in terminted when instance initiated shutdown) However, the current nova case, the shutdowned instance always results in terminated status. As related issues are - describe-instance-attribute instance_initiated_shutdown_behavior doesn't work correctly - instance attribute disable_api_termination isn't supported - stop instance was broken by the change set of the following. It needs unbreak. > commit eb03d47fecd3bfc24243da29ee01679b334a08fe > Author: Vishvananda Ishaya > Date: Fri Sep 23 09:22:32 2011 -0700 > > Remove AoE, Clean up volume code > > * Removes Ata Over Ethernet > * Adds drivers to libvirt for volumes > * Adds initialize_connection and terminate_connection to volume api > * Passes connection info back through volume api > > Change-Id: I1b1626f40bebe8466ab410fb174683293c7c474f This patch - unbreak start instance - implement instance_initiated_shutdown_behavior and make it EC2 compatible - implement disable_api_termination --- Changes 5 -> 6: - fixes to catch up 26b7b9457a5899ecca93fd67d3879efcad4e4968 Changes 4 -> 5: - HACKING compilance Changes 3 -> 4: - rebased to 4c5586a28fd7a085369c49f6039876ffdc86b526 sqlalchemy migrate version Changes 2 -> 3: - rename long name to shorter one s/instance_initiated_shutdown_behavior/shutdown_terminate/g s/disable_api_termination/disable_terminate/g as suggested Kevin L. Mitchell - improved nova.api.ec2.cloud._state_description - pep8 - broken out patches are available for easy review at git://github.com/yamahata/nova.git lp905270-2 Changes 1 -> 2: - fixed an unit test failure pointed out by Mark. (I think ebtabls failure strongly suggests installation problem) - introduce vm_states.SHUTOFF and put instance state which is in power_state.{NOSTATE, SHUTOFF} into vm_states.SHUTOFF. - simplified logic a bit by vm_states.SHUTOFF as suggested by Vish. - instance_initiated_shutdown_behavior:String(255) => instance_initiated_shutdown_terminate:Boolean() as suggested by Vish. - Added Johannes Erdfelt to reviews as they written the vm_states state machine checker. I'd have liked to add David Subiros either, but he doesn't seem to be a registered user of the gerrit. Change-Id: Ibeb94f65137feadad2c343913b39195e3f96a35e --- nova/api/ec2/cloud.py | 65 ++++++++++++++++++++++++-------------------- nova/api/ec2/inst_state.py | 60 ++++++++++++++++++++++++++++++++++++++++ nova/api/openstack/common.py | 3 ++ 3 files changed, 99 insertions(+), 29 deletions(-) create mode 100644 nova/api/ec2/inst_state.py (limited to 'nova/api') diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 01cf61860..9fcaf30d7 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -32,8 +32,10 @@ import urllib from nova.api.ec2 import ec2utils from nova.compute import instance_types +from nova.api.ec2 import inst_state from nova import block_device from nova import compute +from nova.compute import power_state from nova.compute import vm_states from nova import crypto from nova import db @@ -79,26 +81,35 @@ def _gen_key(context, user_id, key_name): # EC2 API can return the following values as documented in the EC2 API # http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ # ApiReference-ItemType-InstanceStateType.html -# pending | running | shutting-down | terminated | stopping | stopped +# pending 0 | running 16 | shutting-down 32 | terminated 48 | stopping 64 | +# stopped 80 _STATE_DESCRIPTION_MAP = { - None: 'pending', - vm_states.ACTIVE: 'running', - vm_states.BUILDING: 'pending', - vm_states.REBUILDING: 'pending', - vm_states.DELETED: 'terminated', - vm_states.SOFT_DELETE: 'terminated', - vm_states.STOPPED: 'stopped', - vm_states.MIGRATING: 'migrate', - vm_states.RESIZING: 'resize', - vm_states.PAUSED: 'pause', - vm_states.SUSPENDED: 'suspend', - vm_states.RESCUED: 'rescue', + None: inst_state.PENDING, + vm_states.ACTIVE: inst_state.RUNNING, + vm_states.BUILDING: inst_state.PENDING, + vm_states.REBUILDING: inst_state.PENDING, + vm_states.DELETED: inst_state.TERMINATED, + vm_states.SOFT_DELETE: inst_state.TERMINATED, + vm_states.STOPPED: inst_state.STOPPED, + vm_states.SHUTOFF: inst_state.SHUTOFF, + vm_states.MIGRATING: inst_state.MIGRATE, + vm_states.RESIZING: inst_state.RESIZE, + vm_states.PAUSED: inst_state.PAUSE, + vm_states.SUSPENDED: inst_state.SUSPEND, + vm_states.RESCUED: inst_state.RESCUE, } -def state_description_from_vm_state(vm_state): +def _state_description(vm_state, shutdown_terminate): """Map the vm state to the server status string""" - return _STATE_DESCRIPTION_MAP.get(vm_state, vm_state) + if (vm_state == vm_states.SHUTOFF and + not shutdown_terminate): + name = inst_state.STOPPED + else: + name = _STATE_DESCRIPTION_MAP.get(vm_state, vm_state) + + return {'code': inst_state.name_to_code(name), + 'name': name} def _parse_block_device_mapping(bdm): @@ -987,21 +998,17 @@ class CloudController(object): tmp['rootDeviceName'], result) def _format_attr_disable_api_termination(instance, result): - _unsupported_attribute(instance, result) + result['disableApiTermination'] = instance['disable_terminate'] def _format_attr_group_set(instance, result): CloudController._format_group_set(instance, result) def _format_attr_instance_initiated_shutdown_behavior(instance, result): - vm_state = instance['vm_state'] - state_to_value = { - vm_states.STOPPED: 'stopped', - vm_states.DELETED: 'terminated', - } - value = state_to_value.get(vm_state) - if value: - result['instanceInitiatedShutdownBehavior'] = value + if instance['shutdown_terminate']: + result['instanceInitiatedShutdownBehavior'] = 'terminate' + else: + result['instanceInitiatedShutdownBehavior'] = 'stop' def _format_attr_instance_type(instance, result): self._format_instance_type(instance, result) @@ -1157,9 +1164,8 @@ class CloudController(object): i['imageId'] = ec2utils.image_ec2_id(image_id) self._format_kernel_id(context, instance, i, 'kernelId') self._format_ramdisk_id(context, instance, i, 'ramdiskId') - i['instanceState'] = { - 'code': instance['power_state'], - 'name': state_description_from_vm_state(instance['vm_state'])} + i['instanceState'] = _state_description( + instance['vm_state'], instance['shutdown_terminate']) fixed_ip = None floating_ip = None @@ -1575,10 +1581,11 @@ class CloudController(object): vm_state = instance['vm_state'] # if the instance is in subtle state, refuse to proceed. - if vm_state not in (vm_states.ACTIVE, vm_states.STOPPED): + if vm_state not in (vm_states.ACTIVE, vm_states.SHUTOFF, + vm_states.STOPPED): raise exception.InstanceNotRunning(instance_id=ec2_instance_id) - if vm_state == vm_states.ACTIVE: + if vm_state in (vm_states.ACTIVE, vm_states.SHUTOFF): restart_instance = True self.compute_api.stop(context, instance_id=instance_id) diff --git a/nova/api/ec2/inst_state.py b/nova/api/ec2/inst_state.py new file mode 100644 index 000000000..68d18c8ad --- /dev/null +++ b/nova/api/ec2/inst_state.py @@ -0,0 +1,60 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 Isaku Yamahata +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +PENDING_CODE = 0 +RUNNING_CODE = 16 +SHUTTING_DOWN_CODE = 32 +TERMINATED_CODE = 48 +STOPPING_CODE = 64 +STOPPED_CODE = 80 + +PENDING = 'pending' +RUNNING = 'running' +SHUTTING_DOWN = 'shutting-down' +TERMINATED = 'terminated' +STOPPING = 'stopping' +STOPPED = 'stopped' + +# non-ec2 value +SHUTOFF = 'shutoff' +MIGRATE = 'migrate' +RESIZE = 'resize' +PAUSE = 'pause' +SUSPEND = 'suspend' +RESCUE = 'rescue' + +# EC2 API instance status code +_NAME_TO_CODE = { + PENDING: PENDING_CODE, + RUNNING: RUNNING_CODE, + SHUTTING_DOWN: SHUTTING_DOWN_CODE, + TERMINATED: TERMINATED_CODE, + STOPPING: STOPPING_CODE, + STOPPED: STOPPED_CODE, + + # approximation + SHUTOFF: TERMINATED_CODE, + MIGRATE: RUNNING_CODE, + RESIZE: RUNNING_CODE, + PAUSE: STOPPED_CODE, + SUSPEND: STOPPED_CODE, + RESCUE: RUNNING_CODE, +} + + +def name_to_code(name): + return _NAME_TO_CODE.get(name, PENDING_CODE) diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index e96c42ac9..42acb4c56 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -57,6 +57,9 @@ _STATE_MAP = { vm_states.STOPPED: { 'default': 'STOPPED', }, + vm_states.SHUTOFF: { + 'default': 'SHUTOFF', + }, vm_states.MIGRATING: { 'default': 'MIGRATING', }, -- cgit