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.

152 lines
6.3KB

  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_id):
  30. server = common.get_instance(self.compute_api, context, server_id)
  31. try:
  32. # NOTE(mikal): get_instance_metadata sometimes returns
  33. # InstanceNotFound in unit tests, even though the instance is
  34. # fetched on the line above. I blame mocking.
  35. meta = self.compute_api.get_instance_metadata(context, server)
  36. except exception.InstanceNotFound:
  37. msg = _('Server does not exist')
  38. raise exc.HTTPNotFound(explanation=msg)
  39. meta_dict = {}
  40. for key, value in meta.items():
  41. meta_dict[key] = value
  42. return meta_dict
  43. @wsgi.expected_errors(404)
  44. def index(self, req, server_id):
  45. """Returns the list of metadata for a given instance."""
  46. context = req.environ['nova.context']
  47. context.can(sm_policies.POLICY_ROOT % 'index')
  48. return {'metadata': self._get_metadata(context, server_id)}
  49. @wsgi.expected_errors((403, 404, 409))
  50. # NOTE(gmann): Returns 200 for backwards compatibility but should be 201
  51. # as this operation complete the creation of metadata.
  52. @validation.schema(server_metadata.create)
  53. def create(self, req, server_id, body):
  54. metadata = body['metadata']
  55. context = req.environ['nova.context']
  56. context.can(sm_policies.POLICY_ROOT % 'create')
  57. new_metadata = self._update_instance_metadata(context,
  58. server_id,
  59. metadata,
  60. delete=False)
  61. return {'metadata': new_metadata}
  62. @wsgi.expected_errors((400, 403, 404, 409))
  63. @validation.schema(server_metadata.update)
  64. def update(self, req, server_id, id, body):
  65. context = req.environ['nova.context']
  66. context.can(sm_policies.POLICY_ROOT % 'update')
  67. meta_item = body['meta']
  68. if id not in meta_item:
  69. expl = _('Request body and URI mismatch')
  70. raise exc.HTTPBadRequest(explanation=expl)
  71. self._update_instance_metadata(context,
  72. server_id,
  73. meta_item,
  74. delete=False)
  75. return {'meta': meta_item}
  76. @wsgi.expected_errors((403, 404, 409))
  77. @validation.schema(server_metadata.update_all)
  78. def update_all(self, req, server_id, body):
  79. context = req.environ['nova.context']
  80. context.can(sm_policies.POLICY_ROOT % 'update_all')
  81. metadata = body['metadata']
  82. new_metadata = self._update_instance_metadata(context,
  83. server_id,
  84. metadata,
  85. delete=True)
  86. return {'metadata': new_metadata}
  87. def _update_instance_metadata(self, context, server_id, metadata,
  88. delete=False):
  89. server = common.get_instance(self.compute_api, context, server_id)
  90. try:
  91. return self.compute_api.update_instance_metadata(context,
  92. server,
  93. metadata,
  94. delete)
  95. except exception.QuotaError as error:
  96. raise exc.HTTPForbidden(explanation=error.format_message())
  97. except exception.InstanceIsLocked as e:
  98. raise exc.HTTPConflict(explanation=e.format_message())
  99. except exception.InstanceInvalidState as state_error:
  100. common.raise_http_conflict_for_instance_invalid_state(state_error,
  101. 'update metadata', server_id)
  102. @wsgi.expected_errors(404)
  103. def show(self, req, server_id, id):
  104. """Return a single metadata item."""
  105. context = req.environ['nova.context']
  106. context.can(sm_policies.POLICY_ROOT % 'show')
  107. data = self._get_metadata(context, server_id)
  108. try:
  109. return {'meta': {id: data[id]}}
  110. except KeyError:
  111. msg = _("Metadata item was not found")
  112. raise exc.HTTPNotFound(explanation=msg)
  113. @wsgi.expected_errors((404, 409))
  114. @wsgi.response(204)
  115. def delete(self, req, server_id, id):
  116. """Deletes an existing metadata."""
  117. context = req.environ['nova.context']
  118. context.can(sm_policies.POLICY_ROOT % 'delete')
  119. metadata = self._get_metadata(context, server_id)
  120. if id not in metadata:
  121. msg = _("Metadata item was not found")
  122. raise exc.HTTPNotFound(explanation=msg)
  123. server = common.get_instance(self.compute_api, context, server_id)
  124. try:
  125. self.compute_api.delete_instance_metadata(context, server, id)
  126. except exception.InstanceIsLocked as e:
  127. raise exc.HTTPConflict(explanation=e.format_message())
  128. except exception.InstanceInvalidState as state_error:
  129. common.raise_http_conflict_for_instance_invalid_state(state_error,
  130. 'delete metadata', server_id)