OpenStack Compute (Nova)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

161 lines
6.8 KiB

  1. # Copyright 2011 OpenStack Foundation
  2. # All Rights Reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. from webob import exc
  16. from nova.api.openstack import common
  17. from nova.api.openstack.compute.schemas import server_metadata
  18. from nova.api.openstack import wsgi
  19. from nova.api import validation
  20. from nova.compute import api as compute
  21. from nova import exception
  22. from nova.i18n import _
  23. from nova.policies import server_metadata as sm_policies
  24. class ServerMetadataController(wsgi.Controller):
  25. """The server metadata API controller for the OpenStack API."""
  26. def __init__(self):
  27. super(ServerMetadataController, self).__init__()
  28. self.compute_api = compute.API()
  29. def _get_metadata(self, context, server):
  30. try:
  31. # NOTE(mikal): get_instance_metadata sometimes returns
  32. # InstanceNotFound in unit tests, even though the instance is
  33. # fetched on the line above. I blame mocking.
  34. meta = self.compute_api.get_instance_metadata(context, server)
  35. except exception.InstanceNotFound:
  36. msg = _('Server does not exist')
  37. raise exc.HTTPNotFound(explanation=msg)
  38. meta_dict = {}
  39. for key, value in meta.items():
  40. meta_dict[key] = value
  41. return meta_dict
  42. @wsgi.expected_errors(404)
  43. def index(self, req, server_id):
  44. """Returns the list of metadata for a given instance."""
  45. context = req.environ['nova.context']
  46. server = common.get_instance(self.compute_api, context, server_id)
  47. context.can(sm_policies.POLICY_ROOT % 'index',
  48. target={'project_id': server.project_id})
  49. return {'metadata': self._get_metadata(context, server)}
  50. @wsgi.expected_errors((403, 404, 409))
  51. # NOTE(gmann): Returns 200 for backwards compatibility but should be 201
  52. # as this operation complete the creation of metadata.
  53. @validation.schema(server_metadata.create)
  54. def create(self, req, server_id, body):
  55. metadata = body['metadata']
  56. context = req.environ['nova.context']
  57. server = common.get_instance(self.compute_api, context, server_id)
  58. context.can(sm_policies.POLICY_ROOT % 'create',
  59. target={'project_id': server.project_id})
  60. new_metadata = self._update_instance_metadata(context,
  61. server,
  62. metadata,
  63. delete=False)
  64. return {'metadata': new_metadata}
  65. @wsgi.expected_errors((400, 403, 404, 409))
  66. @validation.schema(server_metadata.update)
  67. def update(self, req, server_id, id, body):
  68. context = req.environ['nova.context']
  69. server = common.get_instance(self.compute_api, context, server_id)
  70. context.can(sm_policies.POLICY_ROOT % 'update',
  71. target={'project_id': server.project_id})
  72. meta_item = body['meta']
  73. if id not in meta_item:
  74. expl = _('Request body and URI mismatch')
  75. raise exc.HTTPBadRequest(explanation=expl)
  76. self._update_instance_metadata(context,
  77. server,
  78. meta_item,
  79. delete=False)
  80. return {'meta': meta_item}
  81. @wsgi.expected_errors((403, 404, 409))
  82. @validation.schema(server_metadata.update_all)
  83. def update_all(self, req, server_id, body):
  84. context = req.environ['nova.context']
  85. server = common.get_instance(self.compute_api, context, server_id)
  86. context.can(sm_policies.POLICY_ROOT % 'update_all',
  87. target={'project_id': server.project_id})
  88. metadata = body['metadata']
  89. new_metadata = self._update_instance_metadata(context,
  90. server,
  91. metadata,
  92. delete=True)
  93. return {'metadata': new_metadata}
  94. def _update_instance_metadata(self, context, server, metadata,
  95. delete=False):
  96. try:
  97. return self.compute_api.update_instance_metadata(context,
  98. server,
  99. metadata,
  100. delete)
  101. except exception.QuotaError as error:
  102. raise exc.HTTPForbidden(explanation=error.format_message())
  103. except exception.InstanceIsLocked as e:
  104. raise exc.HTTPConflict(explanation=e.format_message())
  105. except exception.InstanceInvalidState as state_error:
  106. common.raise_http_conflict_for_instance_invalid_state(state_error,
  107. 'update metadata', server.uuid)
  108. @wsgi.expected_errors(404)
  109. def show(self, req, server_id, id):
  110. """Return a single metadata item."""
  111. context = req.environ['nova.context']
  112. server = common.get_instance(self.compute_api, context, server_id)
  113. context.can(sm_policies.POLICY_ROOT % 'show',
  114. target={'project_id': server.project_id})
  115. data = self._get_metadata(context, server)
  116. try:
  117. return {'meta': {id: data[id]}}
  118. except KeyError:
  119. msg = _("Metadata item was not found")
  120. raise exc.HTTPNotFound(explanation=msg)
  121. @wsgi.expected_errors((404, 409))
  122. @wsgi.response(204)
  123. def delete(self, req, server_id, id):
  124. """Deletes an existing metadata."""
  125. context = req.environ['nova.context']
  126. server = common.get_instance(self.compute_api, context, server_id)
  127. context.can(sm_policies.POLICY_ROOT % 'delete',
  128. target={'project_id': server.project_id})
  129. metadata = self._get_metadata(context, server)
  130. if id not in metadata:
  131. msg = _("Metadata item was not found")
  132. raise exc.HTTPNotFound(explanation=msg)
  133. try:
  134. self.compute_api.delete_instance_metadata(context, server, id)
  135. except exception.InstanceIsLocked as e:
  136. raise exc.HTTPConflict(explanation=e.format_message())
  137. except exception.InstanceInvalidState as state_error:
  138. common.raise_http_conflict_for_instance_invalid_state(state_error,
  139. 'delete metadata', server_id)