Coverage for nova/api/openstack/compute/shelve.py: 85%

81 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-17 15:08 +0000

1# Copyright 2013 Rackspace Hosting 

2# 

3# Licensed under the Apache License, Version 2.0 (the "License"); you may 

4# not use this file except in compliance with the License. You may obtain 

5# a copy of the License at 

6# 

7# http://www.apache.org/licenses/LICENSE-2.0 

8# 

9# Unless required by applicable law or agreed to in writing, software 

10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 

11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 

12# License for the specific language governing permissions and limitations 

13# under the License. 

14 

15"""The shelved mode extension.""" 

16 

17from oslo_log import log as logging 

18from webob import exc 

19 

20from nova.api.openstack import api_version_request 

21from nova.api.openstack import common 

22from nova.api.openstack.compute.schemas import shelve as schema 

23from nova.api.openstack import wsgi 

24from nova.api import validation 

25from nova.compute import api as compute 

26from nova import exception 

27from nova.policies import shelve as shelve_policies 

28 

29LOG = logging.getLogger(__name__) 

30 

31 

32class ShelveController(wsgi.Controller): 

33 def __init__(self): 

34 super(ShelveController, self).__init__() 

35 self.compute_api = compute.API() 

36 

37 @wsgi.response(202) 

38 @wsgi.expected_errors((404, 403, 409, 400)) 

39 @wsgi.action('shelve') 

40 @validation.schema(schema.shelve) 

41 @validation.response_body_schema(schema.shelve_response) 

42 def _shelve(self, req, id, body): 

43 """Move an instance into shelved mode.""" 

44 context = req.environ["nova.context"] 

45 

46 instance = common.get_instance(self.compute_api, context, id) 

47 context.can(shelve_policies.POLICY_ROOT % 'shelve', 

48 target={'user_id': instance.user_id, 

49 'project_id': instance.project_id}) 

50 try: 

51 self.compute_api.shelve(context, instance) 

52 except ( 

53 exception.InstanceIsLocked, 

54 exception.UnexpectedTaskStateError, 

55 ) as e: 

56 raise exc.HTTPConflict(explanation=e.format_message()) 

57 except exception.InstanceInvalidState as state_error: 

58 common.raise_http_conflict_for_instance_invalid_state(state_error, 

59 'shelve', id) 

60 except exception.ForbiddenPortsWithAccelerator as e: 

61 raise exc.HTTPBadRequest(explanation=e.format_message()) 

62 except ( 

63 exception.ForbiddenSharesNotSupported, 

64 exception.ForbiddenWithShare) as e: 

65 raise exc.HTTPConflict(explanation=e.format_message()) 

66 

67 @wsgi.response(202) 

68 @wsgi.expected_errors((400, 404, 409)) 

69 @wsgi.action('shelveOffload') 

70 @validation.schema(schema.shelve_offload) 

71 @validation.response_body_schema(schema.shelve_offload_response) 

72 def _shelve_offload(self, req, id, body): 

73 """Force removal of a shelved instance from the compute node.""" 

74 context = req.environ["nova.context"] 

75 instance = common.get_instance(self.compute_api, context, id) 

76 context.can(shelve_policies.POLICY_ROOT % 'shelve_offload', 

77 target={'user_id': instance.user_id, 

78 'project_id': instance.project_id}) 

79 try: 

80 self.compute_api.shelve_offload(context, instance) 

81 except exception.InstanceIsLocked as e: 

82 raise exc.HTTPConflict(explanation=e.format_message()) 

83 except exception.InstanceInvalidState as state_error: 

84 common.raise_http_conflict_for_instance_invalid_state(state_error, 

85 'shelveOffload', 

86 id) 

87 

88 except exception.ForbiddenPortsWithAccelerator as e: 

89 raise exc.HTTPBadRequest(explanation=e.format_message()) 

90 

91 @wsgi.response(202) 

92 @wsgi.expected_errors((400, 403, 404, 409)) 

93 @wsgi.action('unshelve') 

94 @validation.schema(schema.unshelve, '2.1', '2.76') 

95 # In microversion 2.77 we support specifying 'availability_zone' to 

96 # unshelve a server. But before 2.77 there is no request body 

97 # schema validation (because of body=null). 

98 @validation.schema(schema.unshelve_v277, '2.77', '2.90') 

99 # In microversion 2.91 we support specifying 'host' to 

100 # unshelve an instance to a specific hostself. 

101 # 'availability_zone' = None is supported as well to unpin the 

102 # availability zone of an instance bonded to this availability_zone 

103 @validation.schema(schema.unshelve_v291, '2.91') 

104 @validation.response_body_schema(schema.unshelve_response) 

105 def _unshelve(self, req, id, body): 

106 """Restore an instance from shelved mode.""" 

107 context = req.environ["nova.context"] 

108 instance = common.get_instance(self.compute_api, context, id) 

109 context.can( 

110 shelve_policies.POLICY_ROOT % 'unshelve', 

111 target={'project_id': instance.project_id} 

112 ) 

113 

114 unshelve_args = {} 

115 

116 unshelve_dict = body.get('unshelve') 

117 support_az = api_version_request.is_supported( 

118 req, '2.77') 

119 support_host = api_version_request.is_supported( 

120 req, '2.91') 

121 if unshelve_dict: 

122 if support_az and 'availability_zone' in unshelve_dict: 

123 unshelve_args['new_az'] = ( 

124 unshelve_dict['availability_zone'] 

125 ) 

126 if support_host: 

127 unshelve_args['host'] = unshelve_dict.get('host') 

128 

129 try: 

130 self.compute_api.unshelve( 

131 context, 

132 instance, 

133 **unshelve_args, 

134 ) 

135 except ( 

136 exception.InstanceIsLocked, 

137 exception.UnshelveInstanceInvalidState, 

138 exception.UnshelveHostNotInAZ, 

139 exception.MismatchVolumeAZException, 

140 ) as e: 

141 raise exc.HTTPConflict(explanation=e.format_message()) 

142 except exception.InstanceInvalidState as state_error: 

143 common.raise_http_conflict_for_instance_invalid_state( 

144 state_error, 'unshelve', id) 

145 except ( 

146 exception.InvalidRequest, 

147 exception.ExtendedResourceRequestOldCompute, 

148 exception.ComputeHostNotFound, 

149 ) as e: 

150 raise exc.HTTPBadRequest(explanation=e.format_message()) 

151 except exception.OverQuota as e: 

152 raise exc.HTTPForbidden(explanation=e.format_message())