From 697c2d89ee5a4c2fc45f34ad28165f397c602399 Mon Sep 17 00:00:00 2001 From: Chris Dent Date: Wed, 22 Mar 2017 20:05:37 +0000 Subject: [PATCH] [placement] Idempotent PUT /resource_classes/{name} In a microversion 1.7 change PUT /resource_classes/{name} so that creation and existence validation of a custom resource class can happen in a single request and prevent the previous behavior of being able to update a single resource class to a new name, which is not desirable. The previous update_resource_class is still in place to support microversion 1.2-1.6. The original resource-classs.yaml sets the default microversion header to 'latest' so for those existing tests that are using the old style of PUT, a '1.6' header has been added. New files for version 1.6 (to add a "no 1.7 behavior here" test) and 1.7 (testing the new PUT behavior and explicitly verifying POST to create is still around) are added. Change-Id: I95f62ab2cb1ab76d18fb52b93f87ed28e4e7b5f3 Implements: bp placement-put-resource-class --- .../placement/handlers/resource_class.py | 37 +++++++++++++- nova/api/openstack/placement/microversion.py | 1 + .../placement/rest_api_version_history.rst | 10 ++++ .../placement/gabbits/microversion.yaml | 4 +- .../gabbits/resource-classes-1-6.yaml | 21 ++++++++ .../gabbits/resource-classes-1-7.yaml | 49 +++++++++++++++++++ .../placement/gabbits/resource-classes.yaml | 5 ++ ...t-put-resource-class-dc7a267c823b7995.yaml | 10 ++++ 8 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 nova/tests/functional/api/openstack/placement/gabbits/resource-classes-1-6.yaml create mode 100644 nova/tests/functional/api/openstack/placement/gabbits/resource-classes-1-7.yaml create mode 100644 releasenotes/notes/idempotent-put-resource-class-dc7a267c823b7995.yaml diff --git a/nova/api/openstack/placement/handlers/resource_class.py b/nova/api/openstack/placement/handlers/resource_class.py index f224d147ed70..32b56fe1aedb 100644 --- a/nova/api/openstack/placement/handlers/resource_class.py +++ b/nova/api/openstack/placement/handlers/resource_class.py @@ -164,7 +164,7 @@ def list_resource_classes(req): @wsgi_wrapper.PlacementWsgify -@microversion.version_handler('1.2') +@microversion.version_handler('1.2', '1.6') @util.require_content('application/json') def update_resource_class(req): """PUT to update a single resource class. @@ -199,3 +199,38 @@ def update_resource_class(req): req.response.status = 200 req.response.content_type = 'application/json' return req.response + + +@wsgi_wrapper.PlacementWsgify # noqa +@microversion.version_handler('1.7') +def update_resource_class(req): + """PUT to create or validate the existence of single resource class. + + On a successful create return 201. Return 204 if the class already + exists. If the resource class is not a custom resource class, return + a 400. 409 might be a better choice, but 400 aligns with previous code. + """ + name = util.wsgi_path_item(req.environ, 'name') + context = req.environ['placement.context'] + + # Use JSON validation to validation resource class name. + util.extract_json('{"name": "%s"}' % name, PUT_RC_SCHEMA_V1_2) + + status = 204 + try: + rc = objects.ResourceClass.get_by_name(context, name) + except exception.NotFound: + try: + rc = objects.ResourceClass(context, name=name) + rc.create() + status = 201 + # We will not see ResourceClassCannotUpdateStandard because + # that was already caught when validating the {name}. + except exception.ResourceClassExists: + # Someone just now created the class, so stick with 204 + pass + + req.response.status = status + req.response.content_type = None + req.response.location = util.resource_class_url(req.environ, rc) + return req.response diff --git a/nova/api/openstack/placement/microversion.py b/nova/api/openstack/placement/microversion.py index 0771c7143683..fac180853e12 100644 --- a/nova/api/openstack/placement/microversion.py +++ b/nova/api/openstack/placement/microversion.py @@ -43,6 +43,7 @@ VERSIONS = [ '1.5', # Adds DELETE /resource_providers/{uuid}/inventories '1.6', # Adds /traits and /resource_providers{uuid}/traits resource # endpoints + '1.7', # PUT /resource_classes/{name} is bodiless create or update ] diff --git a/nova/api/openstack/placement/rest_api_version_history.rst b/nova/api/openstack/placement/rest_api_version_history.rst index 293ea43bc734..dfd5f9de6823 100644 --- a/nova/api/openstack/placement/rest_api_version_history.rst +++ b/nova/api/openstack/placement/rest_api_version_history.rst @@ -113,3 +113,13 @@ The following new routes are added: Custom traits must begin with the prefix "CUSTOM\_" and contain only the letters A through Z, the numbers 0 through 9 and the underscore "\_" character. + +1.7 Idempotent PUT /resource_classes/{name} +------------------------------------------- + +The 1.7 version changes handling of `PUT /resource_classes/{name}` to be a +create or verification of the resource class with `{name}`. If the resource +class is a custom resource class and does not already exist it will be created +and a ``201`` response code returned. If the class already exists the response +code will be ``204``. This makes it possible to check or create a resource +class in one request. diff --git a/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml b/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml index 2b293f5cdb21..50a9ec57785e 100644 --- a/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml +++ b/nova/tests/functional/api/openstack/placement/gabbits/microversion.yaml @@ -39,13 +39,13 @@ tests: response_json_paths: $.errors[0].title: Not Acceptable -- name: latest microversion is 1.6 +- name: latest microversion is 1.7 GET: / request_headers: openstack-api-version: placement latest response_headers: vary: /OpenStack-API-Version/ - openstack-api-version: placement 1.6 + openstack-api-version: placement 1.7 - name: other accept header bad version GET: / diff --git a/nova/tests/functional/api/openstack/placement/gabbits/resource-classes-1-6.yaml b/nova/tests/functional/api/openstack/placement/gabbits/resource-classes-1-6.yaml new file mode 100644 index 000000000000..30f250d62ab9 --- /dev/null +++ b/nova/tests/functional/api/openstack/placement/gabbits/resource-classes-1-6.yaml @@ -0,0 +1,21 @@ +# Confirm that 1.7 behavior of PUT resource classes is not in +# microversion 1.6. +fixtures: + - APIFixture + +defaults: + request_headers: + x-auth-token: admin + accept: application/json + content-type: application/json + OpenStack-API-Version: placement 1.6 + +tests: + +- name: bodiless put + PUT: /resource_classes/CUSTOM_COW + status: 400 + response_strings: + # We don't check much of this string because it is different + # between python 2 and 3. + - "Malformed JSON:" diff --git a/nova/tests/functional/api/openstack/placement/gabbits/resource-classes-1-7.yaml b/nova/tests/functional/api/openstack/placement/gabbits/resource-classes-1-7.yaml new file mode 100644 index 000000000000..2187c24da3eb --- /dev/null +++ b/nova/tests/functional/api/openstack/placement/gabbits/resource-classes-1-7.yaml @@ -0,0 +1,49 @@ +fixtures: + - APIFixture + +defaults: + request_headers: + x-auth-token: admin + accept: application/json + content-type: application/json + OpenStack-API-Version: placement 1.7 + +tests: + +- name: create new custom class with put + PUT: /resource_classes/CUSTOM_COW + status: 201 + response_headers: + location: //resource_classes/CUSTOM_COW/ + +- name: verify that class with put + PUT: /resource_classes/CUSTOM_COW + status: 204 + response_headers: + location: //resource_classes/CUSTOM_COW/ + +- name: fail to put non custom class + PUT: /resource_classes/COW + status: 400 + response_strings: + - "Failed validating 'pattern'" + +- name: try to put standard class + PUT: /resource_classes/VCPU + status: 400 + response_strings: + - "Failed validating 'pattern'" + +- name: try to put too long class + PUT: /resource_classes/CUSTOM_SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS + status: 400 + response_strings: + - "Failed validating 'maxLength'" + +- name: post to create still works + POST: /resource_classes + data: + name: CUSTOM_SHEEP + status: 201 + response_headers: + location: //resource_classes/CUSTOM_SHEEP/ diff --git a/nova/tests/functional/api/openstack/placement/gabbits/resource-classes.yaml b/nova/tests/functional/api/openstack/placement/gabbits/resource-classes.yaml index 9c3dd5ea3c3e..8fd204515984 100644 --- a/nova/tests/functional/api/openstack/placement/gabbits/resource-classes.yaml +++ b/nova/tests/functional/api/openstack/placement/gabbits/resource-classes.yaml @@ -125,6 +125,7 @@ tests: PUT: /resource_classes/VCPU request_headers: content-type: application/json + OpenStack-API-Version: placement 1.6 data: name: VCPU_ALTERNATE status: 400 @@ -137,6 +138,7 @@ tests: PUT: /resource_classes/$ENVIRON['CUSTOM_RES_CLASS'] request_headers: content-type: application/json + OpenStack-API-Version: placement 1.6 data: name: VCPU status: 400 @@ -157,6 +159,7 @@ tests: PUT: /resource_classes/CUSTOM_NFV_FOO request_headers: content-type: application/json + OpenStack-API-Version: placement 1.6 data: name: $ENVIRON['CUSTOM_RES_CLASS'] status: 409 @@ -170,6 +173,7 @@ tests: PUT: /resource_classes/$ENVIRON['CUSTOM_RES_CLASS'] request_headers: content-type: application/json + OpenStack-API-Version: placement 1.6 data: name: CUSTOM_NFV_BAR status: 200 @@ -234,6 +238,7 @@ tests: PUT: /resource_classes/$ENVIRON['CUSTOM_RES_CLASS'] request_headers: content-type: application/json + OpenStack-API-Version: placement 1.6 data: name: *name_exceeds_max_length_check status: 400 diff --git a/releasenotes/notes/idempotent-put-resource-class-dc7a267c823b7995.yaml b/releasenotes/notes/idempotent-put-resource-class-dc7a267c823b7995.yaml new file mode 100644 index 000000000000..3892d27f5181 --- /dev/null +++ b/releasenotes/notes/idempotent-put-resource-class-dc7a267c823b7995.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + The 1.7 version of the placement API changes handling of + `PUT /resource_classes/{name}` to be a create or verification of the + resource class with `{name}`. If the resource class is a custom resource + class and does not already exist it will be created and a ``201`` response + code returned. If the class already exists the response code will be + ``204``. This makes it possible to check or create a resource class in one + request.