Add instance project_id for arq patch
In order to control the operation of arq with different roles. Use microversion 2.1 for adding project_id for arq patching. Add and modify unittest. Change-Id: Ie331a3b34e7397531ec9737595b8996e38044801
This commit is contained in:
parent
9179ed3053
commit
de0823d198
|
@ -23,6 +23,8 @@ from oslo_log import log
|
|||
from cyborg.api.controllers import base
|
||||
from cyborg.api.controllers import link
|
||||
from cyborg.api.controllers import types
|
||||
from cyborg.api.controllers.v2 import utils
|
||||
from cyborg.api.controllers.v2 import versions
|
||||
from cyborg.api import expose
|
||||
from cyborg.common import constants
|
||||
from cyborg.common import exception
|
||||
|
@ -54,6 +56,8 @@ class ARQ(base.APIBase):
|
|||
|
||||
instance_uuid = wtypes.text
|
||||
"""The UUID of the instance associated with this ARQ, if any"""
|
||||
project_id = wtypes.text
|
||||
"""The UUID of the instance project_id associated with this ARQ, if any"""
|
||||
|
||||
attach_handle_type = wtypes.text
|
||||
attach_handle_info = {wtypes.text: wtypes.text}
|
||||
|
@ -257,6 +261,8 @@ class ARQsController(base.CyborgController):
|
|||
valid_fields = {'hostname': None,
|
||||
'device_rp_uuid': None,
|
||||
'instance_uuid': None}
|
||||
if utils.allow_project_id():
|
||||
valid_fields['project_id'] = None
|
||||
if ((not all(p['op'] == 'add' for p in patch)) and
|
||||
(not all(p['op'] == 'remove' for p in patch))):
|
||||
raise exception.PatchError(
|
||||
|
@ -264,6 +270,12 @@ class ARQsController(base.CyborgController):
|
|||
|
||||
for p in patch:
|
||||
path = p['path'].lstrip('/')
|
||||
if path == 'project_id' and not utils.allow_project_id():
|
||||
raise exception.NotAcceptable(_(
|
||||
"Request not acceptable. The minimal required API "
|
||||
"version should be %(base)s.%(opr)s") %
|
||||
{'base': versions.BASE_VERSION,
|
||||
'opr': versions.MINOR_1_PROJECT_ID})
|
||||
if path not in valid_fields.keys():
|
||||
reason = 'Invalid path in patch {}'.format(p['path'])
|
||||
raise exception.PatchError(reason=reason)
|
||||
|
@ -306,6 +318,7 @@ class ARQsController(base.CyborgController):
|
|||
{"path": "/hostname", "op": ADD/RM, "value": "..."},
|
||||
{"path": "/device_rp_uuid", "op": ADD/RM, "value": "..."},
|
||||
{"path": "/instance_uuid", "op": ADD/RM, "value": "..."},
|
||||
{"path": "/project_id", "op": ADD/RM, "value": "..."},
|
||||
],
|
||||
"$arq_uuid": [...]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
# Copyright 2020 Inspur, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from cyborg import api
|
||||
from cyborg.api.controllers.v2 import versions
|
||||
|
||||
|
||||
def allow_project_id():
|
||||
# v2.1 added project_id for arq patch
|
||||
return api.request.version.minor >= versions.MINOR_1_PROJECT_ID
|
|
@ -23,7 +23,9 @@ BASE_VERSION = 2
|
|||
# explanation of what each version contains.
|
||||
#
|
||||
# v2.0: Initial minor version.
|
||||
# v2.1: Add project_id for arq patch
|
||||
MINOR_0_INITIAL_VERSION = 0
|
||||
MINOR_1_PROJECT_ID = 1
|
||||
|
||||
|
||||
# When adding another version, update:
|
||||
|
@ -32,7 +34,7 @@ MINOR_0_INITIAL_VERSION = 0
|
|||
# explanation of what changed in the new version
|
||||
|
||||
|
||||
MINOR_MAX_VERSION = MINOR_0_INITIAL_VERSION
|
||||
MINOR_MAX_VERSION = MINOR_1_PROJECT_ID
|
||||
|
||||
# String representations of the minor and maximum versions
|
||||
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_0_INITIAL_VERSION)
|
||||
|
|
|
@ -12,6 +12,11 @@ user documentation.
|
|||
This is the initial version of the v2 API which supports
|
||||
microversions.
|
||||
|
||||
2.1
|
||||
---
|
||||
|
||||
Add ``project_id`` for arq.
|
||||
|
||||
A user can specify a header in the API request::
|
||||
|
||||
OpenStack-API-Version: accelerator <microversion>
|
||||
|
|
|
@ -411,3 +411,8 @@ class InvalidType(Invalid):
|
|||
|
||||
class ResourceNotFound(NotFound):
|
||||
_msg_fmt = _("%(resource)s not found %(msg)s")
|
||||
|
||||
|
||||
class NotAcceptable(CyborgException):
|
||||
_msg_fmt = _("Request not acceptable.")
|
||||
code = http_client.NOT_ACCEPTABLE
|
||||
|
|
|
@ -44,6 +44,7 @@ class ARQ(base.CyborgObject, object_base.VersionedObjectDictCompat):
|
|||
'hostname': object_fields.StringField(nullable=True),
|
||||
'device_rp_uuid': object_fields.StringField(nullable=True),
|
||||
'instance_uuid': object_fields.StringField(nullable=True),
|
||||
'project_id': object_fields.StringField(nullable=True),
|
||||
|
||||
# Fields populated by Cyborg after binding
|
||||
'attach_handle_type': object_fields.StringField(nullable=True),
|
||||
|
@ -66,6 +67,6 @@ class ARQ(base.CyborgObject, object_base.VersionedObjectDictCompat):
|
|||
db_extarq['attach_handle_info'] = {}
|
||||
|
||||
for field in arq.fields:
|
||||
arq[field] = db_extarq[field]
|
||||
arq[field] = db_extarq.get(field)
|
||||
arq.obj_reset_changes()
|
||||
return arq
|
||||
|
|
|
@ -278,7 +278,7 @@ class ExtARQ(base.CyborgObject, object_base.VersionedObjectDictCompat,
|
|||
|
||||
for field in extarq.fields:
|
||||
if field != 'arq':
|
||||
extarq[field] = db_extarq[field]
|
||||
extarq[field] = db_extarq.get(field)
|
||||
extarq.arq = objects.ARQ()
|
||||
extarq.arq._from_db_object(extarq.arq, db_extarq)
|
||||
extarq.obj_reset_changes()
|
||||
|
|
|
@ -65,12 +65,17 @@ class ExtARQJobMixin(object):
|
|||
hostname = valid_fields[self.arq.uuid]['hostname']
|
||||
devrp_uuid = valid_fields[self.arq.uuid]['device_rp_uuid']
|
||||
instance_uuid = valid_fields[self.arq.uuid]['instance_uuid']
|
||||
LOG.info('[arqs:objs] bind. hostname: %s, devrp_uuid: %s'
|
||||
'instance: %s', hostname, devrp_uuid, instance_uuid)
|
||||
project_id = valid_fields[self.arq.uuid].get('project_id')
|
||||
LOG.info('[arqs:objs] bind. hostname: %(hostname)s,'
|
||||
' devrp_uuid: %(devrp_uuid)s, instance: %(instance)s, '
|
||||
'project_id: %(project_id)s',
|
||||
{'hostname': hostname, 'devrp_uuid': devrp_uuid,
|
||||
'instance_uuid': instance_uuid, 'project_id': project_id})
|
||||
|
||||
self.arq.hostname = hostname
|
||||
self.arq.device_rp_uuid = devrp_uuid
|
||||
self.arq.instance_uuid = instance_uuid
|
||||
self.arq.project_id = project_id
|
||||
|
||||
# If prog fails, we'll change this ARQ state changes get committed here
|
||||
self.update_check_state(context, constants.ARQ_BIND_STARTED)
|
||||
|
|
|
@ -19,6 +19,7 @@ from unittest import mock
|
|||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from cyborg.api.controllers import base
|
||||
from cyborg.api.controllers.v2 import arqs
|
||||
from cyborg.common import exception
|
||||
from cyborg.tests.unit.api.controllers.v2 import base as v2_test
|
||||
|
@ -255,13 +256,14 @@ class TestARQsController(v2_test.APITestV2):
|
|||
@mock.patch('cyborg.objects.ExtARQ.apply_patch')
|
||||
def test_apply_patch(self, mock_apply_patch, mock_check_if_bound):
|
||||
"""Test the happy path."""
|
||||
patch_list = fake_extarq.get_patch_list()
|
||||
patch_list, device_rp_uuid = fake_extarq.get_patch_list()
|
||||
arq_uuids = list(patch_list.keys())
|
||||
obj_extarq = self.fake_extarqs[0]
|
||||
valid_fields = {
|
||||
arq_uuid: {
|
||||
'hostname': 'myhost',
|
||||
'device_rp_uuid': 'fb16c293-5739-4c84-8590-926f9ab16669',
|
||||
'instance_uuid': '5922a70f-1e06-4cfd-88dd-a332120d7144'}
|
||||
'hostname': obj_extarq.arq.hostname,
|
||||
'device_rp_uuid': device_rp_uuid,
|
||||
'instance_uuid': obj_extarq.arq.instance_uuid}
|
||||
for arq_uuid in arq_uuids}
|
||||
|
||||
self.patch_json(self.ARQ_URL, params=patch_list,
|
||||
|
@ -271,6 +273,41 @@ class TestARQsController(v2_test.APITestV2):
|
|||
valid_fields)
|
||||
mock_check_if_bound.assert_called_once_with(mock.ANY, valid_fields)
|
||||
|
||||
@mock.patch.object(arqs.ARQsController, '_check_if_already_bound')
|
||||
@mock.patch('cyborg.objects.ExtARQ.apply_patch')
|
||||
def test_apply_patch_allow_project_id(
|
||||
self, mock_apply_patch, mock_check_if_bound):
|
||||
patch_list, _ = fake_extarq.get_patch_list()
|
||||
for arq_uuid, patch in patch_list.items():
|
||||
patch.append({'path': '/project_id', 'op': 'add',
|
||||
'value': 'b1c76756ac2e482789a8e1c5f4bf065e'})
|
||||
arq_uuids = list(patch_list.keys())
|
||||
valid_fields = {
|
||||
arq_uuid: {
|
||||
'hostname': 'myhost',
|
||||
'device_rp_uuid': 'fb16c293-5739-4c84-8590-926f9ab16669',
|
||||
'instance_uuid': '5922a70f-1e06-4cfd-88dd-a332120d7144',
|
||||
'project_id': 'b1c76756ac2e482789a8e1c5f4bf065e'}
|
||||
for arq_uuid in arq_uuids}
|
||||
|
||||
self.patch_json(self.ARQ_URL, params=patch_list,
|
||||
headers={base.Version.current_api_version:
|
||||
'accelerator 2.1'})
|
||||
mock_apply_patch.assert_called_once_with(mock.ANY, patch_list,
|
||||
valid_fields)
|
||||
mock_check_if_bound.assert_called_once_with(mock.ANY, valid_fields)
|
||||
|
||||
def test_apply_patch_not_allow_project_id(self):
|
||||
patch_list, _ = fake_extarq.get_patch_list()
|
||||
for arq_uuid, patch in patch_list.items():
|
||||
patch.append({'path': '/project_id', 'op': 'add',
|
||||
'value': 'b1c76756ac2e482789a8e1c5f4bf065e'})
|
||||
response = self.patch_json(self.ARQ_URL, params=patch_list,
|
||||
headers=self.headers,
|
||||
expect_errors=True)
|
||||
self.assertEqual(http_client.NOT_ACCEPTABLE, response.status_code)
|
||||
self.assertTrue(response.json['error_message'])
|
||||
|
||||
# TODO(all): Add exception test cases for apply_patch.
|
||||
|
||||
@mock.patch('cyborg.objects.ExtARQ.list')
|
||||
|
|
|
@ -288,7 +288,8 @@ def get_patch_list(same_device=True):
|
|||
must be for the same device.
|
||||
"""
|
||||
arqs = _get_arqs_as_dict()
|
||||
host_binding = {'path': '/hostname', 'op': 'add', 'value': 'myhost'}
|
||||
host_binding = {'path': '/hostname', 'op': 'add',
|
||||
'value': arqs[0]['hostname']}
|
||||
inst_binding = {'path': '/instance_uuid', 'op': 'add',
|
||||
'value': arqs[0]['instance_uuid']}
|
||||
device_rp_uuid = 'fb16c293-5739-4c84-8590-926f9ab16669'
|
||||
|
@ -298,4 +299,4 @@ def get_patch_list(same_device=True):
|
|||
dev_binding = {'path': '/device_rp_uuid', 'op': 'add',
|
||||
'value': dev_uuid}
|
||||
patch_list[newarq['uuid']] = [host_binding, inst_binding, dev_binding]
|
||||
return patch_list
|
||||
return patch_list, device_rp_uuid
|
||||
|
|
Loading…
Reference in New Issue