Coverage for nova/api/openstack/compute/shelve.py: 85%
81 statements
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +0000
« prev ^ index » next coverage.py v7.6.12, created at 2025-04-24 11:16 +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.
15"""The shelved mode extension."""
17from oslo_log import log as logging
18from webob import exc
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
29LOG = logging.getLogger(__name__)
32class ShelveController(wsgi.Controller):
33 def __init__(self):
34 super(ShelveController, self).__init__()
35 self.compute_api = compute.API()
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"]
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())
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)
88 except exception.ForbiddenPortsWithAccelerator as e:
89 raise exc.HTTPBadRequest(explanation=e.format_message())
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 )
114 unshelve_args = {}
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')
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())