Fix flavor profile API handling of None updates

The current flavor profile API does not properly handle
clearing/reseting values on update. Some mandatory fields would
return a database "cannot be Null" error. This patch raises the
proper invalid option execption.

Story: 2005374
Task: 33542

Change-Id: I5253c48871a8bb3bf91f82aa7791585cc4a6d529
This commit is contained in:
Michael Johnson 2019-05-30 15:10:53 -07:00
parent 21a54d6b78
commit ab8a263674
4 changed files with 47 additions and 8 deletions

View File

@ -111,6 +111,24 @@ class FlavorProfileController(base.BaseController):
db_flavor_profile, profile_types.FlavorProfileResponse) db_flavor_profile, profile_types.FlavorProfileResponse)
return profile_types.FlavorProfileRootResponse(flavorprofile=result) return profile_types.FlavorProfileRootResponse(flavorprofile=result)
def _validate_update_fp(self, context, id, flavorprofile):
if flavorprofile.name is None:
raise exceptions.InvalidOption(value=None, option=constants.NAME)
if flavorprofile.provider_name is None:
raise exceptions.InvalidOption(value=None,
option=constants.PROVIDER_NAME)
if flavorprofile.flavor_data is None:
raise exceptions.InvalidOption(value=None,
option=constants.FLAVOR_DATA)
# Don't allow changes to the flavor_data or provider_name if it
# is in use.
if (not isinstance(flavorprofile.flavor_data, wtypes.UnsetType) or
not isinstance(flavorprofile.provider_name, wtypes.UnsetType)):
if self.repositories.flavor.count(context.session,
flavor_profile_id=id) > 0:
raise exceptions.ObjectInUse(object='Flavor profile', id=id)
@wsme_pecan.wsexpose(profile_types.FlavorProfileRootResponse, @wsme_pecan.wsexpose(profile_types.FlavorProfileRootResponse,
wtypes.text, status_code=200, wtypes.text, status_code=200,
body=profile_types.FlavorProfileRootPUT) body=profile_types.FlavorProfileRootPUT)
@ -121,13 +139,7 @@ class FlavorProfileController(base.BaseController):
self._auth_validate_action(context, context.project_id, self._auth_validate_action(context, context.project_id,
constants.RBAC_PUT) constants.RBAC_PUT)
# Don't allow changes to the flavor_data or provider_name if it self._validate_update_fp(context, id, flavorprofile)
# is in use.
if (not isinstance(flavorprofile.flavor_data, wtypes.UnsetType) or
not isinstance(flavorprofile.provider_name, wtypes.UnsetType)):
if self.repositories.flavor.count(context.session,
flavor_profile_id=id) > 0:
raise exceptions.ObjectInUse(object='Flavor profile', id=id)
if not isinstance(flavorprofile.flavor_data, wtypes.UnsetType): if not isinstance(flavorprofile.flavor_data, wtypes.UnsetType):
# Do a basic JSON validation on the metadata # Do a basic JSON validation on the metadata

View File

@ -340,6 +340,8 @@ BYTES_IN = 'bytes_in'
BYTES_OUT = 'bytes_out' BYTES_OUT = 'bytes_out'
REQUEST_ERRORS = 'request_errors' REQUEST_ERRORS = 'request_errors'
TOTAL_CONNECTIONS = 'total_connections' TOTAL_CONNECTIONS = 'total_connections'
NAME = 'name'
PROVIDER_NAME = 'provider_name'
CERT_ROTATE_AMPHORA_FLOW = 'octavia-cert-rotate-amphora-flow' CERT_ROTATE_AMPHORA_FLOW = 'octavia-cert-rotate-amphora-flow'
CREATE_AMPHORA_FLOW = 'octavia-create-amphora-flow' CREATE_AMPHORA_FLOW = 'octavia-create-amphora-flow'

View File

@ -328,7 +328,7 @@ class TestFlavorProfiles(base.BaseAPITest):
self.assertEqual('{"hello": "world"}', self.assertEqual('{"hello": "world"}',
response.get(constants.FLAVOR_DATA)) response.get(constants.FLAVOR_DATA))
def test_update_none(self): def test_update_nothing(self):
fp = self.create_flavor_profile('test_profile', 'noop_driver', fp = self.create_flavor_profile('test_profile', 'noop_driver',
'{"x": "y"}') '{"x": "y"}')
body = self._build_body({}) body = self._build_body({})
@ -340,6 +340,25 @@ class TestFlavorProfiles(base.BaseAPITest):
self.assertEqual('{"x": "y"}', self.assertEqual('{"x": "y"}',
response.get(constants.FLAVOR_DATA)) response.get(constants.FLAVOR_DATA))
def test_update_name_none(self):
self._test_update_param_none(constants.NAME)
def test_update_provider_name_none(self):
self._test_update_param_none(constants.PROVIDER_NAME)
def test_update_flavor_data_none(self):
self._test_update_param_none(constants.FLAVOR_DATA)
def _test_update_param_none(self, param_name):
fp = self.create_flavor_profile('test_profile', 'noop_driver',
'{"x": "y"}')
expect_error_msg = ("None is not a valid option for %s" %
param_name)
body = self._build_body({param_name: None})
response = self.put(self.FP_PATH.format(fp_id=fp.get('id')), body,
status=400)
self.assertEqual(expect_error_msg, response.json['faultstring'])
def test_update_no_flavor_data(self): def test_update_no_flavor_data(self):
fp = self.create_flavor_profile('test_profile', 'noop_driver', fp = self.create_flavor_profile('test_profile', 'noop_driver',
'{"x": "y"}') '{"x": "y"}')

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fixed the API handling of None (JSON null) on object update calls. The
API will now either clear the value from the field or will reset the value
of the field to the API default.