Add trait check for POST Device Profile
At present, when creating a device profile, the function _validate_post_request only checks if the keys of groups matches that in ["resources:", "trait:", "accel:"] without checking the string following "trait:","resources:",this introduces risks. For example, the string followed by "trait:" default should be "CUSTOM_", what follows "resources:" should be a valid resource type, but the fact is that users may make typos such as adding extra spaces like "resources: FPGA", or other format errors. This kind of error cannot be detected by the current _validate_post_request, and it won't be exposed until nova parses the rc of cyborg. This is not in compliance with the process specification and potentially introduces greater risks. Therefore, it is necessary to implement a more strict inspection of rc in device profile POST API. This patch added the following,rc check will be followed in the next patch. 1.Check the legality of traits name 2.Space removal in traits 3.Trait value check Change-Id: Ie3a2c6340e2b41edd8d943ac9e0bda0a1e504d3c Story: 2008143 Task: 40884
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import copy
|
||||
import pecan
|
||||
import re
|
||||
from six.moves import http_client
|
||||
@@ -133,17 +134,32 @@ class DeviceProfilesController(base.CyborgController,
|
||||
groups = req_devprof.get("groups")
|
||||
if not groups:
|
||||
raise exception.DeviceProfileGroupsExpected()
|
||||
else:
|
||||
for group in groups:
|
||||
for key, value in group.items():
|
||||
if not re.match(GROUP_KEYS, key):
|
||||
|
||||
for group in groups:
|
||||
tmp_group = copy.deepcopy(group)
|
||||
for key, value in tmp_group.items():
|
||||
# check resource and trait prefix format
|
||||
if not re.match(GROUP_KEYS, key):
|
||||
raise exception.InvalidParameterValue(
|
||||
err="Device profile group keys must be of "
|
||||
" the form %s" % GROUP_KEYS)
|
||||
# check trait name and it's value
|
||||
if key.startswith("trait:"):
|
||||
inner_origin_trait = ":".join(key.split(":")[1:])
|
||||
inner_trait = inner_origin_trait.strip(" ")
|
||||
if not inner_trait.startswith('CUSTOM_'):
|
||||
raise exception.InvalidParameterValue(
|
||||
err="Device profile group keys must be of "
|
||||
" the form %s" % GROUP_KEYS)
|
||||
if key.startswith("trait:") and value not in TRAIT_VALUES:
|
||||
err="Unsupported trait name format %s, should "
|
||||
"start with CUSTOM_" % inner_trait)
|
||||
if value not in TRAIT_VALUES:
|
||||
raise exception.InvalidParameterValue(
|
||||
err="Device profile trait values must be one "
|
||||
"among %s" % TRAIT_VALUES)
|
||||
err="Unsupported trait value %s, the value must"
|
||||
" be one among %s" % TRAIT_VALUES)
|
||||
# strip " " and update old group key.
|
||||
if inner_origin_trait != inner_trait:
|
||||
del group[key]
|
||||
standard_key = "trait:" + inner_trait
|
||||
group[standard_key] = value
|
||||
|
||||
def _get_device_profile_list(self, names=None, uuid=None):
|
||||
"""Get a list of API objects representing device profiles."""
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
from six.moves import http_client
|
||||
from unittest import mock
|
||||
import webtest
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
@@ -82,6 +83,47 @@ class TestDeviceProfileController(v2_test.APITestV2):
|
||||
self.assertEqual(http_client.CREATED, response.status_int)
|
||||
self._validate_dp(dp[0], out_dp)
|
||||
|
||||
def test_create_with_unsupported_trait(self):
|
||||
test_unsupport_dp = self.fake_dps[0]
|
||||
|
||||
# generate special trait for test
|
||||
del test_unsupport_dp['groups'][0][
|
||||
'trait:CUSTOM_FPGA_INTEL_PAC_ARRIA10']
|
||||
test_unsupport_dp['groups'][0]['trait:FAKE_TRAIT'] = 'required'
|
||||
dp = [test_unsupport_dp]
|
||||
dp[0]['created_at'] = str(dp[0]['created_at'])
|
||||
self.assertRaisesRegex(
|
||||
webtest.app.AppError,
|
||||
".*Unsupported trait name format FAKE_TRAIT.*",
|
||||
self.post_json,
|
||||
self.DP_URL,
|
||||
dp,
|
||||
headers=self.headers)
|
||||
|
||||
@mock.patch('cyborg.conductor.rpcapi.ConductorAPI.device_profile_create')
|
||||
def test_create_with_extra_space_in_trait(self, mock_cond_dp):
|
||||
test_unsupport_dp = self.fake_dps[0]
|
||||
|
||||
# generate a requested dp which has extra space in trait
|
||||
del test_unsupport_dp['groups'][0][
|
||||
'trait:CUSTOM_FPGA_INTEL_PAC_ARRIA10']
|
||||
test_unsupport_dp['groups'][0][
|
||||
'trait: CUSTOM_FPGA_INTEL_PAC_ARRIA10'] = 'required'
|
||||
|
||||
dp = [test_unsupport_dp]
|
||||
mock_cond_dp.return_value = self.fake_dp_objs[0]
|
||||
dp[0]['created_at'] = str(dp[0]['created_at'])
|
||||
|
||||
response = self.post_json(self.DP_URL, dp, headers=self.headers)
|
||||
out_dp = jsonutils.loads(response.controller_output)
|
||||
|
||||
# check that the extra space in trait:
|
||||
# {'trait: CUSTOM_FPGA_INTEL_PAC_ARRIA10': 'required'} is
|
||||
# successful stripped by the _validate_post_request function, and
|
||||
# the created device_profile has no extra space in trait
|
||||
# {'trait:CUSTOM_FPGA_INTEL_PAC_ARRIA10': 'required}
|
||||
self.assertTrue(out_dp['groups'] == self.fake_dp_objs[0]['groups'])
|
||||
|
||||
@mock.patch('cyborg.conductor.rpcapi.ConductorAPI.device_profile_delete')
|
||||
@mock.patch('cyborg.objects.DeviceProfile.get_by_name')
|
||||
@mock.patch('cyborg.objects.DeviceProfile.get_by_uuid')
|
||||
|
||||
Reference in New Issue
Block a user