Merge "Add support for updating instance type"
This commit is contained in:
commit
6065ebfa10
@ -94,6 +94,53 @@ Response
|
|||||||
.. literalinclude:: ../../doc/api_samples/types/type-create-post-resp.json
|
.. literalinclude:: ../../doc/api_samples/types/type-create-post-resp.json
|
||||||
:language: javascript
|
:language: javascript
|
||||||
|
|
||||||
|
Update Type
|
||||||
|
===========
|
||||||
|
|
||||||
|
.. rest_method:: PUT /types/{type_uuid}
|
||||||
|
|
||||||
|
Updates a type.
|
||||||
|
|
||||||
|
Updating a type is typically only available to administrators of a
|
||||||
|
cloud because this has implications for scheduling efficiently in the cloud.
|
||||||
|
|
||||||
|
Normal response codes: 200
|
||||||
|
|
||||||
|
Error response codes: badRequest(400), unauthorized(401), forbidden(403),
|
||||||
|
conflict(409)
|
||||||
|
|
||||||
|
Request
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters.yaml
|
||||||
|
|
||||||
|
- name: type_name
|
||||||
|
- description: type_description
|
||||||
|
- is_public: type_is_public_not_required
|
||||||
|
|
||||||
|
**Example Update Type**
|
||||||
|
|
||||||
|
.. literalinclude:: ../../doc/api_samples/types/type-update-put-req.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
|
Response
|
||||||
|
--------
|
||||||
|
|
||||||
|
.. rest_parameters:: parameters.yaml
|
||||||
|
|
||||||
|
- name: type_name
|
||||||
|
- links: links
|
||||||
|
- description: type_description
|
||||||
|
- uuid: type_uuid
|
||||||
|
- created_at: created_at
|
||||||
|
- updated_at: updated_at
|
||||||
|
- is_public: type_is_public
|
||||||
|
- extra_specs: type_extra_specs
|
||||||
|
|
||||||
|
**Example Update type**
|
||||||
|
|
||||||
|
.. literalinclude:: ../../doc/api_samples/types/type-update-put-resp.json
|
||||||
|
:language: javascript
|
||||||
|
|
||||||
Show Type Details
|
Show Type Details
|
||||||
===================
|
===================
|
||||||
|
5
doc/api_samples/types/type-update-put-req.json
Normal file
5
doc/api_samples/types/type-update-put-req.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "updated_type",
|
||||||
|
"description": "this is a type to be updated",
|
||||||
|
"is_public": false
|
||||||
|
}
|
19
doc/api_samples/types/type-update-put-resp.json
Normal file
19
doc/api_samples/types/type-update-put-resp.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"description": "this is a type to be updated",
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"href": "http://10.3.150.17:6688/v1/types/7de2859d-ec6d-42c7-bb86-9d630ba5ac94",
|
||||||
|
"rel": "self"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"href": "http://10.3.150.17:6688/types/7de2859d-ec6d-42c7-bb86-9d630ba5ac94",
|
||||||
|
"rel": "bookmark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"created_at": "2016-09-27T02:37:21.966342+00:00",
|
||||||
|
"uuid": "7de2859d-ec6d-42c7-bb86-9d630ba5ac94",
|
||||||
|
"updated_at": null,
|
||||||
|
"extra_specs": {},
|
||||||
|
"is_public": false,
|
||||||
|
"name": "updated_type"
|
||||||
|
}
|
@ -23,6 +23,8 @@ from nimble.api.controllers import base
|
|||||||
from nimble.api.controllers import link
|
from nimble.api.controllers import link
|
||||||
from nimble.api.controllers.v1 import types
|
from nimble.api.controllers.v1 import types
|
||||||
from nimble.api import expose
|
from nimble.api import expose
|
||||||
|
from nimble.common import exception
|
||||||
|
from nimble.common.i18n import _
|
||||||
from nimble import objects
|
from nimble import objects
|
||||||
|
|
||||||
|
|
||||||
@ -161,6 +163,34 @@ class InstanceTypeController(rest.RestController):
|
|||||||
new_instance_type.uuid)
|
new_instance_type.uuid)
|
||||||
return InstanceType.convert_with_links(new_instance_type)
|
return InstanceType.convert_with_links(new_instance_type)
|
||||||
|
|
||||||
|
@expose.expose(InstanceType, types.uuid, body=InstanceType)
|
||||||
|
def put(self, instance_type_uuid, instance_type):
|
||||||
|
"""Update an instance type.
|
||||||
|
|
||||||
|
:param instance_type_uuid: the uuid of instance_type to be updated.
|
||||||
|
:param instance_type: a instance type within the request body.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
inst_type_in_db = objects.InstanceType.get(
|
||||||
|
pecan.request.context, instance_type_uuid)
|
||||||
|
except exception.InstanceTypeNotFound:
|
||||||
|
msg = (_("InstanceType %s could not be found") %
|
||||||
|
instance_type_uuid)
|
||||||
|
raise wsme.exc.ClientSideError(
|
||||||
|
msg, status_code=http_client.BAD_REQUEST)
|
||||||
|
need_to_update = False
|
||||||
|
for attr in ('name', 'description', 'is_public'):
|
||||||
|
if getattr(instance_type, attr) != wtypes.Unset:
|
||||||
|
need_to_update = True
|
||||||
|
setattr(inst_type_in_db, attr, getattr(instance_type, attr))
|
||||||
|
# don't need to call db_api if no update
|
||||||
|
if need_to_update:
|
||||||
|
inst_type_in_db.save()
|
||||||
|
# Set the HTTP Location Header
|
||||||
|
pecan.response.location = link.build_url('instance_type',
|
||||||
|
inst_type_in_db.uuid)
|
||||||
|
return InstanceType.convert_with_links(inst_type_in_db)
|
||||||
|
|
||||||
@expose.expose(None, types.uuid, status_code=http_client.NO_CONTENT)
|
@expose.expose(None, types.uuid, status_code=http_client.NO_CONTENT)
|
||||||
def delete(self, instance_type_uuid):
|
def delete(self, instance_type_uuid):
|
||||||
"""Delete an instance type.
|
"""Delete an instance type.
|
||||||
|
@ -50,6 +50,9 @@ class Connection(object):
|
|||||||
def instance_type_get(self, context, instance_type_uuid):
|
def instance_type_get(self, context, instance_type_uuid):
|
||||||
"""Get instance type by uuid."""
|
"""Get instance type by uuid."""
|
||||||
|
|
||||||
|
def instance_type_update(self, context, instance_type_id, values):
|
||||||
|
"""Update an instance type."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def instance_type_get_all(self, context):
|
def instance_type_get_all(self, context):
|
||||||
"""Get all instance types."""
|
"""Get all instance types."""
|
||||||
|
@ -137,6 +137,19 @@ class Connection(api.Connection):
|
|||||||
raise exception.InstanceTypeNotFound(
|
raise exception.InstanceTypeNotFound(
|
||||||
instance_type=instance_type_uuid)
|
instance_type=instance_type_uuid)
|
||||||
|
|
||||||
|
def instance_type_update(self, context, instance_type_id, values):
|
||||||
|
with _session_for_write():
|
||||||
|
query = model_query(context, models.InstanceTypes)
|
||||||
|
query = add_identity_filter(query, instance_type_id)
|
||||||
|
try:
|
||||||
|
ref = query.with_lockmode('update').one()
|
||||||
|
except NoResultFound:
|
||||||
|
raise exception.InstanceTypeNotFound(
|
||||||
|
instance_type=instance_type_id)
|
||||||
|
|
||||||
|
ref.update(values)
|
||||||
|
return ref
|
||||||
|
|
||||||
def instance_type_get_all(self, context):
|
def instance_type_get_all(self, context):
|
||||||
results = model_query(context, models.InstanceTypes)
|
results = model_query(context, models.InstanceTypes)
|
||||||
return [_dict_with_extra_specs(i) for i in results]
|
return [_dict_with_extra_specs(i) for i in results]
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
from oslo_versionedobjects import base as object_base
|
from oslo_versionedobjects import base as object_base
|
||||||
|
|
||||||
from nimble.common import exception
|
|
||||||
from nimble.db import api as dbapi
|
from nimble.db import api as dbapi
|
||||||
from nimble.objects import base
|
from nimble.objects import base
|
||||||
from nimble.objects import fields as object_fields
|
from nimble.objects import fields as object_fields
|
||||||
@ -92,10 +91,6 @@ class InstanceType(base.NimbleObject, object_base.VersionedObjectDictCompat):
|
|||||||
def save(self, context=None):
|
def save(self, context=None):
|
||||||
updates = self.obj_get_changes()
|
updates = self.obj_get_changes()
|
||||||
extra_specs = updates.pop('extra_specs', None)
|
extra_specs = updates.pop('extra_specs', None)
|
||||||
if updates:
|
|
||||||
raise exception.ObjectActionError(
|
|
||||||
action='save', reason='read-only fields were changed')
|
|
||||||
|
|
||||||
if extra_specs is not None:
|
if extra_specs is not None:
|
||||||
deleted_keys = (set(self._orig_extra_specs.keys()) -
|
deleted_keys = (set(self._orig_extra_specs.keys()) -
|
||||||
set(extra_specs.keys()))
|
set(extra_specs.keys()))
|
||||||
@ -105,6 +100,7 @@ class InstanceType(base.NimbleObject, object_base.VersionedObjectDictCompat):
|
|||||||
|
|
||||||
if added_keys or deleted_keys:
|
if added_keys or deleted_keys:
|
||||||
self.save_extra_specs(context, self.extra_specs, deleted_keys)
|
self.save_extra_specs(context, self.extra_specs, deleted_keys)
|
||||||
|
self.dbapi.instance_type_update(context, self.uuid, updates)
|
||||||
|
|
||||||
def save_extra_specs(self, context, to_add=None, to_delete=None):
|
def save_extra_specs(self, context, to_add=None, to_delete=None):
|
||||||
"""Add or delete extra_specs.
|
"""Add or delete extra_specs.
|
||||||
|
@ -69,6 +69,19 @@ class TestInstanceType(v1_test.APITestV1):
|
|||||||
resp = self.get_json('/types')
|
resp = self.get_json('/types')
|
||||||
self.assertEqual(3, len(resp['types']))
|
self.assertEqual(3, len(resp['types']))
|
||||||
|
|
||||||
|
def test_instance_type_update(self):
|
||||||
|
self._prepare_instance_types()
|
||||||
|
resp = self.get_json('/types/' + self.TYPE_UUIDS[0])
|
||||||
|
self.assertEqual('test0', resp['name'])
|
||||||
|
self.assertEqual('just test0', resp['description'])
|
||||||
|
values = {"name": "update_name", "description": "updated_description",
|
||||||
|
"is_public": False}
|
||||||
|
self.put_json('/types/' + self.TYPE_UUIDS[0], values, status=200)
|
||||||
|
resp = self.get_json('/types/' + self.TYPE_UUIDS[0])
|
||||||
|
self.assertEqual('update_name', resp['name'])
|
||||||
|
self.assertEqual('updated_description', resp['description'])
|
||||||
|
self.assertEqual(False, resp['is_public'])
|
||||||
|
|
||||||
|
|
||||||
class TestInstanceTypeExtra(v1_test.APITestV1):
|
class TestInstanceTypeExtra(v1_test.APITestV1):
|
||||||
TYPE_UUID = 'ff28b5a2-73e5-431c-b4b7-1b96b74bca7b'
|
TYPE_UUID = 'ff28b5a2-73e5-431c-b4b7-1b96b74bca7b'
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
import mock
|
import mock
|
||||||
from oslo_context import context
|
from oslo_context import context
|
||||||
|
|
||||||
from nimble.common import exception
|
|
||||||
from nimble import objects
|
from nimble import objects
|
||||||
from nimble.tests.unit.db import base
|
from nimble.tests.unit.db import base
|
||||||
from nimble.tests.unit.db import utils
|
from nimble.tests.unit.db import utils
|
||||||
@ -72,8 +71,15 @@ class TestInstanceTypeObject(base.DbTestCase):
|
|||||||
mock_type_destroy.assert_called_once_with(self.context, uuid)
|
mock_type_destroy.assert_called_once_with(self.context, uuid)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
|
uuid = self.fake_type['uuid']
|
||||||
|
with mock.patch.object(self.dbapi, 'instance_type_update',
|
||||||
|
autospec=True) as mock_inst_type_update:
|
||||||
instance_type = objects.InstanceType(self.context,
|
instance_type = objects.InstanceType(self.context,
|
||||||
**self.fake_type)
|
**self.fake_type)
|
||||||
self.assertRaises(exception.ObjectActionError,
|
instance_type.name = 'changed_name'
|
||||||
instance_type.save,
|
updates = instance_type.obj_get_changes()
|
||||||
self.context)
|
instance_type.save(self.context)
|
||||||
|
updates.pop('extra_specs', None)
|
||||||
|
mock_inst_type_update.return_value = self.fake_type
|
||||||
|
mock_inst_type_update.assert_called_once_with(
|
||||||
|
self.context, uuid, updates)
|
||||||
|
Loading…
Reference in New Issue
Block a user