diff options
Diffstat (limited to 'nova/compute/api.py')
-rw-r--r-- | nova/compute/api.py | 71 |
1 files changed, 42 insertions, 29 deletions
diff --git a/nova/compute/api.py b/nova/compute/api.py index 1e24e8ce5..48d404148 100644 --- a/nova/compute/api.py +++ b/nova/compute/api.py @@ -110,10 +110,12 @@ RO_SECURITY_GROUPS = ['default'] SM_IMAGE_PROP_PREFIX = "image_" -def check_instance_state(vm_state=None, task_state=(None,)): +def check_instance_state(vm_state=None, task_state=(None,), + must_have_launched=True): """Decorator to check VM and/or task state before entry to API functions. - If the instance is in the wrong state, the wrapper will raise an exception. + If the instance is in the wrong state, or has not been sucessfully started + at least once the wrapper will raise an exception. """ if vm_state is not None and not isinstance(vm_state, set): @@ -137,6 +139,13 @@ def check_instance_state(vm_state=None, task_state=(None,)): instance_uuid=instance['uuid'], state=instance['task_state'], method=f.__name__) + if must_have_launched and not instance['launched_at']: + raise exception.InstanceInvalidState( + attr=None, + not_launched=True, + instance_uuid=instance['uuid'], + state=instance['vm_state'], + method=f.__name__) return f(self, context, instance, *args, **kw) return inner @@ -487,21 +496,20 @@ class API(base.Base): instance['uuid'], updates) return instance - def _check_config_drive(self, context, config_drive): - try: - bool_like = strutils.bool_from_string(config_drive, strict=True) - except ValueError: - bool_like = False - - if config_drive is None: - return None, None - elif bool_like: - return None, bool_like + def _check_config_drive(self, config_drive): + if config_drive: + try: + bool_val = strutils.bool_from_string(config_drive, + strict=True) + except ValueError: + raise exception.ConfigDriveInvalidValue(option=config_drive) else: - cd_image_service, config_drive_id = \ - glance.get_remote_image_service(context, config_drive) - cd_image_service.show(context, config_drive_id) - return config_drive_id, None + bool_val = False + # FIXME(comstud): Bug ID 1193438 filed for this. This looks silly, + # but this is because the config drive column is a String. False + # is represented by using an empty string. And for whatever + # reason, we rely on the DB to cast True to a String. + return True if bool_val else '' def _check_requested_image(self, context, image_id, image, instance_type): if not image: @@ -589,8 +597,7 @@ class API(base.Base): kernel_id, ramdisk_id = self._handle_kernel_and_ramdisk( context, kernel_id, ramdisk_id, image) - config_drive_id, config_drive = self._check_config_drive( - context, config_drive) + config_drive = self._check_config_drive(config_drive) if key_data is None and key_name: key_pair = self.db.key_pair_get(context, context.user_id, @@ -610,8 +617,7 @@ class API(base.Base): 'ramdisk_id': ramdisk_id or '', 'power_state': power_state.NOSTATE, 'vm_state': vm_states.BUILDING, - 'config_drive_id': config_drive_id or '', - 'config_drive': config_drive or '', + 'config_drive': config_drive, 'user_id': context.user_id, 'project_id': context.project_id, 'instance_type_id': instance_type['id'], @@ -1305,7 +1311,8 @@ class API(base.Base): # NOTE(maoy): we allow delete to be called no matter what vm_state says. @wrap_check_policy @check_instance_lock - @check_instance_state(vm_state=None, task_state=None) + @check_instance_state(vm_state=None, task_state=None, + must_have_launched=True) def soft_delete(self, context, instance): """Terminate an instance.""" LOG.debug(_('Going to try to soft delete instance'), @@ -1329,7 +1336,8 @@ class API(base.Base): @wrap_check_policy @check_instance_lock - @check_instance_state(vm_state=None, task_state=None) + @check_instance_state(vm_state=None, task_state=None, + must_have_launched=False) def delete(self, context, instance): """Terminate an instance.""" LOG.debug(_("Going to try to terminate instance"), instance=instance) @@ -1369,7 +1377,8 @@ class API(base.Base): @wrap_check_policy @check_instance_lock - @check_instance_state(vm_state=[vm_states.SOFT_DELETED]) + @check_instance_state(vm_state=[vm_states.SOFT_DELETED], + must_have_launched=False) def force_delete(self, context, instance): """Force delete a previously deleted (but not reclaimed) instance.""" self._delete_instance(context, instance) @@ -1790,7 +1799,8 @@ class API(base.Base): @wrap_check_policy @check_instance_lock @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED, - vm_states.PAUSED, vm_states.SUSPENDED], + vm_states.PAUSED, vm_states.SUSPENDED, + vm_states.ERROR], task_state=[None, task_states.REBOOTING, task_states.REBOOTING_HARD, task_states.RESUMING, @@ -1826,7 +1836,8 @@ class API(base.Base): @wrap_check_policy @check_instance_lock - @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED], + @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED, + vm_states.ERROR], task_state=[None]) def rebuild(self, context, instance, image_href, admin_password, **kwargs): """Rebuild the given instance with the provided attributes.""" @@ -2224,7 +2235,8 @@ class API(base.Base): @wrap_check_policy @check_instance_lock - @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED]) + @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED, + vm_states.ERROR]) def rescue(self, context, instance, rescue_password=None): """Rescue the given instance.""" @@ -3201,7 +3213,8 @@ class SecurityGroupAPI(base.Base, security_group_base.SecurityGroupBase): def trigger_rules_refresh(self, context, id): """Called when a rule is added to or removed from a security_group.""" - security_group = self.db.security_group_get(context, id) + security_group = self.db.security_group_get( + context, id, columns_to_join=['instances']) for instance in security_group['instances']: if instance['host'] is not None: @@ -3227,8 +3240,8 @@ class SecurityGroupAPI(base.Base, security_group_base.SecurityGroupBase): security_groups = set() for rule in security_group_rules: security_group = self.db.security_group_get( - context, - rule['parent_group_id']) + context, rule['parent_group_id'], + columns_to_join=['instances']) security_groups.add(security_group) # ..then we find the instances that are members of these groups.. |