ed946c4d55
Adds basic support for passing OVN VTEP switch metadata to neutron via Ironic's port.local_link_connection field. Adds microversion 1.90 to Ironic's API, adding support for new schema in port.local_link_connection Bump version of the jsonschema library to ensure consistent behavior with new schema configurations. Add documentation warning: This has not been tested as no Ironic developers have access to the hardware in question. Closes-bug: #2034953 Co-Authored-By: Austin Cormier <acormier@juniper.net> Co-Authored-By: Jay Faulkner <jay@jvf.cc> Change-Id: Ie98dc4552ec2ea16db1e2d382aed54ce9dfef41b
1885 lines
74 KiB
Python
1885 lines
74 KiB
Python
# -*- encoding: utf-8 -*-
|
|
# Copyright 2013 Red Hat, 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.
|
|
|
|
import datetime
|
|
from http import client as http_client
|
|
import io
|
|
from unittest import mock
|
|
|
|
from oslo_config import cfg
|
|
from oslo_utils import uuidutils
|
|
|
|
from ironic import api
|
|
from ironic.api.controllers.v1 import node as api_node
|
|
from ironic.api.controllers.v1 import utils
|
|
from ironic.common import context as ironic_context
|
|
from ironic.common import exception
|
|
from ironic.common import policy
|
|
from ironic.common import states
|
|
from ironic import objects
|
|
from ironic.tests import base
|
|
from ironic.tests.unit.api import utils as test_api_utils
|
|
from ironic.tests.unit.objects import utils as obj_utils
|
|
|
|
CONF = cfg.CONF
|
|
|
|
|
|
class TestApiUtils(base.TestCase):
|
|
|
|
def test_validate_limit(self):
|
|
limit = utils.validate_limit(10)
|
|
self.assertEqual(10, limit)
|
|
|
|
# max limit
|
|
limit = utils.validate_limit(999999999)
|
|
self.assertEqual(CONF.api.max_limit, limit)
|
|
|
|
# negative
|
|
self.assertRaises(exception.ClientSideError, utils.validate_limit, -1)
|
|
|
|
# zero
|
|
self.assertRaises(exception.ClientSideError, utils.validate_limit, 0)
|
|
|
|
def test_validate_sort_dir(self):
|
|
sort_dir = utils.validate_sort_dir('asc')
|
|
self.assertEqual('asc', sort_dir)
|
|
|
|
# invalid sort_dir parameter
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.validate_sort_dir,
|
|
'fake-sort')
|
|
|
|
def test_apply_jsonpatch(self):
|
|
doc = {"foo": {"bar": "baz"}}
|
|
patch = [{"op": "add", "path": "/foo/answer", "value": 42}]
|
|
result = utils.apply_jsonpatch(doc, patch)
|
|
expected = {"foo": {"bar": "baz", "answer": 42}}
|
|
self.assertEqual(expected, result)
|
|
|
|
def test_apply_jsonpatch_no_add_root_attr(self):
|
|
doc = {}
|
|
patch = [{"op": "add", "path": "/foo", "value": 42}]
|
|
self.assertRaisesRegex(exception.ClientSideError,
|
|
"Adding a new attribute",
|
|
utils.apply_jsonpatch, doc, patch)
|
|
|
|
def test_apply_jsonpatch_remove_non_existent(self):
|
|
# Raises a KeyError.
|
|
doc = {}
|
|
patch = [{"op": "remove", "path": "/foo"}]
|
|
self.assertRaises(exception.PatchError,
|
|
utils.apply_jsonpatch, doc, patch)
|
|
|
|
def test_apply_jsonpatch_replace_non_existent_list_item(self):
|
|
# Raises an IndexError.
|
|
doc = []
|
|
patch = [{"op": "replace", "path": "/0", "value": 42}]
|
|
self.assertRaises(exception.PatchError,
|
|
utils.apply_jsonpatch, doc, patch)
|
|
|
|
def test_get_patch_values_no_path(self):
|
|
patch = [{'path': '/name', 'op': 'update', 'value': 'node-0'}]
|
|
path = '/invalid'
|
|
values = utils.get_patch_values(patch, path)
|
|
self.assertEqual([], values)
|
|
|
|
def test_get_patch_values_remove(self):
|
|
patch = [{'path': '/name', 'op': 'remove'}]
|
|
path = '/name'
|
|
values = utils.get_patch_values(patch, path)
|
|
self.assertEqual([], values)
|
|
|
|
def test_get_patch_values_success(self):
|
|
patch = [{'path': '/name', 'op': 'replace', 'value': 'node-x'}]
|
|
path = '/name'
|
|
values = utils.get_patch_values(patch, path)
|
|
self.assertEqual(['node-x'], values)
|
|
|
|
def test_get_patch_values_multiple_success(self):
|
|
patch = [{'path': '/name', 'op': 'replace', 'value': 'node-x'},
|
|
{'path': '/name', 'op': 'replace', 'value': 'node-y'}]
|
|
path = '/name'
|
|
values = utils.get_patch_values(patch, path)
|
|
self.assertEqual(['node-x', 'node-y'], values)
|
|
|
|
def test_is_path_removed_success(self):
|
|
patch = [{'path': '/name', 'op': 'remove'}]
|
|
path = '/name'
|
|
value = utils.is_path_removed(patch, path)
|
|
self.assertTrue(value)
|
|
|
|
def test_is_path_removed_subpath_success(self):
|
|
patch = [{'path': '/local_link_connection/switch_id', 'op': 'remove'}]
|
|
path = '/local_link_connection'
|
|
value = utils.is_path_removed(patch, path)
|
|
self.assertTrue(value)
|
|
|
|
def test_is_path_removed_similar_subpath(self):
|
|
patch = [{'path': '/local_link_connection_info/switch_id',
|
|
'op': 'remove'}]
|
|
path = '/local_link_connection'
|
|
value = utils.is_path_removed(patch, path)
|
|
self.assertFalse(value)
|
|
|
|
def test_is_path_removed_replace(self):
|
|
patch = [{'path': '/name', 'op': 'replace', 'value': 'node-x'}]
|
|
path = '/name'
|
|
value = utils.is_path_removed(patch, path)
|
|
self.assertFalse(value)
|
|
|
|
def test_is_path_updated_success(self):
|
|
patch = [{'path': '/name', 'op': 'remove'}]
|
|
path = '/name'
|
|
value = utils.is_path_updated(patch, path)
|
|
self.assertTrue(value)
|
|
|
|
def test_is_path_updated_subpath_success(self):
|
|
patch = [{'path': '/properties/switch_id', 'op': 'add', 'value': 'id'}]
|
|
path = '/properties'
|
|
value = utils.is_path_updated(patch, path)
|
|
self.assertTrue(value)
|
|
|
|
def test_is_path_updated_similar_subpath(self):
|
|
patch = [{'path': '/properties2/switch_id',
|
|
'op': 'replace', 'value': 'spam'}]
|
|
path = '/properties'
|
|
value = utils.is_path_updated(patch, path)
|
|
self.assertFalse(value)
|
|
|
|
def test_check_for_invalid_fields(self):
|
|
requested = ['field_1', 'field_3']
|
|
supported = ['field_1', 'field_2', 'field_3']
|
|
utils.check_for_invalid_fields(requested, supported)
|
|
|
|
def test_check_for_invalid_fields_fail(self):
|
|
requested = ['field_1', 'field_4']
|
|
supported = ['field_1', 'field_2', 'field_3']
|
|
self.assertRaises(exception.InvalidParameterValue,
|
|
utils.check_for_invalid_fields,
|
|
requested, supported)
|
|
|
|
def test_patch_update_changed_fields(self):
|
|
schema = {
|
|
'properties': {
|
|
'one': {},
|
|
'two': {},
|
|
'three': {},
|
|
'four': {},
|
|
'five_uuid': {},
|
|
}
|
|
}
|
|
fields = [
|
|
'one',
|
|
'two',
|
|
'three',
|
|
'four',
|
|
'five_id'
|
|
]
|
|
|
|
def rpc_object():
|
|
obj = mock.MagicMock()
|
|
items = {
|
|
'one': 1,
|
|
'two': 'ii',
|
|
'three': None,
|
|
'four': [1, 2, 3, 4],
|
|
'five_id': 123
|
|
}
|
|
obj.__getitem__.side_effect = items.__getitem__
|
|
obj.__contains__.side_effect = items.__contains__
|
|
return obj
|
|
|
|
# test no change
|
|
o = rpc_object()
|
|
utils.patch_update_changed_fields({
|
|
'one': 1,
|
|
'two': 'ii',
|
|
'three': None,
|
|
'four': [1, 2, 3, 4],
|
|
}, o, fields, schema, id_map={'five_id': 123})
|
|
o.__setitem__.assert_not_called()
|
|
|
|
# test everything changes, and id_map values override from_dict values
|
|
o = rpc_object()
|
|
utils.patch_update_changed_fields({
|
|
'one': 2,
|
|
'two': 'iii',
|
|
'three': '',
|
|
'four': [2, 3],
|
|
}, o, fields, schema, id_map={'four': [4], 'five_id': 456})
|
|
o.__setitem__.assert_has_calls([
|
|
mock.call('one', 2),
|
|
mock.call('two', 'iii'),
|
|
mock.call('three', ''),
|
|
mock.call('four', [4]),
|
|
mock.call('five_id', 456)
|
|
])
|
|
|
|
# test None fields from None values and missing keys
|
|
# also five_id is untouched with no id_map
|
|
o = rpc_object()
|
|
utils.patch_update_changed_fields({
|
|
'two': None,
|
|
}, o, fields, schema)
|
|
o.__setitem__.assert_has_calls([
|
|
mock.call('two', None),
|
|
])
|
|
|
|
# test fields not in the schema are untouched
|
|
fields = [
|
|
'six',
|
|
'seven',
|
|
'eight'
|
|
]
|
|
o = rpc_object()
|
|
utils.patch_update_changed_fields({
|
|
'six': 2,
|
|
'seven': 'iii',
|
|
'eight': '',
|
|
}, o, fields, schema)
|
|
o.__setitem__.assert_not_called()
|
|
|
|
def test_patched_validate_with_schema(self):
|
|
schema = {
|
|
'properties': {
|
|
'one': {'type': 'string'},
|
|
'two': {'type': 'integer'},
|
|
'three': {'type': 'boolean'},
|
|
}
|
|
}
|
|
|
|
# test non-schema fields removed
|
|
pd = {
|
|
'one': 'one',
|
|
'two': 2,
|
|
'three': True,
|
|
'four': 4,
|
|
'five': 'five'
|
|
}
|
|
utils.patched_validate_with_schema(pd, schema)
|
|
self.assertEqual({
|
|
'one': 'one',
|
|
'two': 2,
|
|
'three': True,
|
|
}, pd)
|
|
|
|
# test fails schema validation
|
|
pd = {
|
|
'one': 1,
|
|
'two': 2,
|
|
'three': False
|
|
}
|
|
e = self.assertRaises(exception.InvalidParameterValue,
|
|
utils.patched_validate_with_schema, pd, schema)
|
|
self.assertIn("1 is not of type 'string'", str(e))
|
|
|
|
# test fails custom validation
|
|
def validate(name, value):
|
|
raise exception.InvalidParameterValue('big ouch')
|
|
|
|
pd = {
|
|
'one': 'one',
|
|
'two': 2,
|
|
'three': False
|
|
}
|
|
e = self.assertRaises(exception.InvalidParameterValue,
|
|
utils.patched_validate_with_schema, pd, schema,
|
|
validate)
|
|
self.assertIn("big ouch", str(e))
|
|
|
|
def test_patch_validate_allowed_fields(self):
|
|
allowed_fields = ['one', 'two', 'three']
|
|
|
|
# patch all
|
|
self.assertEqual(
|
|
{'one', 'two', 'three'},
|
|
utils.patch_validate_allowed_fields([
|
|
{'path': '/one'},
|
|
{'path': '/two'},
|
|
{'path': '/three/four'},
|
|
], allowed_fields))
|
|
|
|
# patch one
|
|
self.assertEqual(
|
|
{'one'},
|
|
utils.patch_validate_allowed_fields([
|
|
{'path': '/one'},
|
|
], allowed_fields))
|
|
|
|
# patch invalid field
|
|
e = self.assertRaises(
|
|
exception.Invalid,
|
|
utils.patch_validate_allowed_fields,
|
|
[{'path': '/four'}],
|
|
allowed_fields)
|
|
self.assertIn("Cannot patch /four. "
|
|
"Only the following can be updated: "
|
|
"one, two, three", str(e))
|
|
|
|
@mock.patch.object(api, 'request', autospec=False)
|
|
def test_sanitize_dict(self, mock_req):
|
|
mock_req.public_url = 'http://192.0.2.1:5050'
|
|
|
|
node = obj_utils.get_test_node(
|
|
self.context,
|
|
created_at=datetime.datetime(2000, 1, 1, 0, 0),
|
|
updated_at=datetime.datetime(2001, 1, 1, 0, 0),
|
|
inspection_started_at=datetime.datetime(2002, 1, 1, 0, 0),
|
|
console_enabled=True,
|
|
tags=['one', 'two', 'three'])
|
|
|
|
expected_links = [{
|
|
'href': 'http://192.0.2.1:5050/v1/node/'
|
|
'1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
|
'rel': 'self'
|
|
}, {
|
|
'href': 'http://192.0.2.1:5050/node/'
|
|
'1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
|
'rel': 'bookmark'
|
|
}]
|
|
|
|
# all fields
|
|
node_dict = utils.object_to_dict(
|
|
node,
|
|
link_resource='node',
|
|
)
|
|
utils.sanitize_dict(node_dict, None)
|
|
self.assertEqual({
|
|
'created_at': '2000-01-01T00:00:00+00:00',
|
|
'links': expected_links,
|
|
'updated_at': '2001-01-01T00:00:00+00:00',
|
|
'uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
|
}, node_dict)
|
|
|
|
# some fields
|
|
node_dict = utils.object_to_dict(
|
|
node,
|
|
link_resource='node',
|
|
)
|
|
utils.sanitize_dict(node_dict, ['uuid', 'created_at'])
|
|
self.assertEqual({
|
|
'created_at': '2000-01-01T00:00:00+00:00',
|
|
'links': expected_links,
|
|
'uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
|
}, node_dict)
|
|
|
|
# no fields
|
|
node_dict = utils.object_to_dict(
|
|
node,
|
|
link_resource='node',
|
|
)
|
|
utils.sanitize_dict(node_dict, [])
|
|
self.assertEqual({
|
|
'links': expected_links,
|
|
}, node_dict)
|
|
|
|
|
|
@mock.patch.object(api, 'request', spec_set=['version'])
|
|
class TestCheckAllowFields(base.TestCase):
|
|
|
|
def test_check_allow_specify_fields(self, mock_request):
|
|
mock_request.version.minor = 8
|
|
self.assertIsNone(utils.check_allow_specify_fields(['foo']))
|
|
|
|
def test_check_allow_specify_fields_fail(self, mock_request):
|
|
mock_request.version.minor = 7
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_specify_fields, ['foo'])
|
|
|
|
def test_check_allowed_fields_network_interface(self, mock_request):
|
|
mock_request.version.minor = 20
|
|
self.assertIsNone(
|
|
utils.check_allowed_fields(['network_interface']))
|
|
|
|
def test_check_allowed_fields_network_interface_fail(self, mock_request):
|
|
mock_request.version.minor = 19
|
|
self.assertRaises(
|
|
exception.NotAcceptable,
|
|
utils.check_allowed_fields,
|
|
['network_interface'])
|
|
|
|
def test_check_allowed_fields_resource_class(self, mock_request):
|
|
mock_request.version.minor = 21
|
|
self.assertIsNone(
|
|
utils.check_allowed_fields(['resource_class']))
|
|
|
|
def test_check_allowed_fields_resource_class_fail(self, mock_request):
|
|
mock_request.version.minor = 20
|
|
self.assertRaises(
|
|
exception.NotAcceptable,
|
|
utils.check_allowed_fields,
|
|
['resource_class'])
|
|
|
|
def test_check_allowed_fields_rescue_interface_fail(self, mock_request):
|
|
mock_request.version.minor = 31
|
|
self.assertRaises(
|
|
exception.NotAcceptable,
|
|
utils.check_allowed_fields,
|
|
['rescue_interface'])
|
|
|
|
def test_check_allowed_portgroup_fields_mode_properties(self,
|
|
mock_request):
|
|
mock_request.version.minor = 26
|
|
self.assertIsNone(
|
|
utils.check_allowed_portgroup_fields(['mode']))
|
|
self.assertIsNone(
|
|
utils.check_allowed_portgroup_fields(['properties']))
|
|
|
|
def test_check_allowed_portgroup_fields_mode_properties_fail(self,
|
|
mock_request):
|
|
mock_request.version.minor = 25
|
|
self.assertRaises(
|
|
exception.NotAcceptable,
|
|
utils.check_allowed_portgroup_fields,
|
|
['mode'])
|
|
self.assertRaises(
|
|
exception.NotAcceptable,
|
|
utils.check_allowed_portgroup_fields,
|
|
['properties'])
|
|
|
|
def test_check_allow_specify_driver(self, mock_request):
|
|
mock_request.version.minor = 16
|
|
self.assertIsNone(utils.check_allow_specify_driver(['fake']))
|
|
|
|
def test_check_allow_specify_driver_fail(self, mock_request):
|
|
mock_request.version.minor = 15
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_specify_driver, ['fake'])
|
|
|
|
def test_check_allow_specify_resource_class(self, mock_request):
|
|
mock_request.version.minor = 21
|
|
self.assertIsNone(utils.check_allow_specify_resource_class(['foo']))
|
|
|
|
def test_check_allow_specify_resource_class_fail(self, mock_request):
|
|
mock_request.version.minor = 20
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_specify_resource_class, ['foo'])
|
|
|
|
def test_check_allow_filter_driver_type(self, mock_request):
|
|
mock_request.version.minor = 30
|
|
self.assertIsNone(utils.check_allow_filter_driver_type('classic'))
|
|
|
|
def test_check_allow_filter_driver_type_none(self, mock_request):
|
|
mock_request.version.minor = 29
|
|
self.assertIsNone(utils.check_allow_filter_driver_type(None))
|
|
|
|
def test_check_allow_filter_driver_type_fail(self, mock_request):
|
|
mock_request.version.minor = 29
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_filter_driver_type, 'classic')
|
|
|
|
def test_check_allow_filter_by_conductor_group(self, mock_request):
|
|
mock_request.version.minor = 46
|
|
self.assertIsNone(utils.check_allow_filter_by_conductor_group('foo'))
|
|
|
|
def test_check_allow_filter_by_conductor_group_none(self, mock_request):
|
|
mock_request.version.minor = 46
|
|
self.assertIsNone(utils.check_allow_filter_by_conductor_group(None))
|
|
|
|
def test_check_allow_filter_by_conductor_group_fail(self, mock_request):
|
|
mock_request.version.minor = 45
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_filter_by_conductor_group, 'foo')
|
|
|
|
def test_check_allow_driver_detail(self, mock_request):
|
|
mock_request.version.minor = 30
|
|
self.assertIsNone(utils.check_allow_driver_detail(True))
|
|
|
|
def test_check_allow_driver_detail_false(self, mock_request):
|
|
mock_request.version.minor = 30
|
|
self.assertIsNone(utils.check_allow_driver_detail(False))
|
|
|
|
def test_check_allow_driver_detail_none(self, mock_request):
|
|
mock_request.version.minor = 29
|
|
self.assertIsNone(utils.check_allow_driver_detail(None))
|
|
|
|
def test_check_allow_driver_detail_fail(self, mock_request):
|
|
mock_request.version.minor = 29
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_driver_detail, True)
|
|
|
|
def test_check_allow_manage_verbs(self, mock_request):
|
|
mock_request.version.minor = 4
|
|
utils.check_allow_management_verbs('manage')
|
|
|
|
def test_check_allow_manage_verbs_fail(self, mock_request):
|
|
mock_request.version.minor = 3
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_management_verbs, 'manage')
|
|
|
|
def test_check_allow_provide_verbs(self, mock_request):
|
|
mock_request.version.minor = 4
|
|
utils.check_allow_management_verbs('provide')
|
|
|
|
def test_check_allow_provide_verbs_fail(self, mock_request):
|
|
mock_request.version.minor = 3
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_management_verbs, 'provide')
|
|
|
|
def test_check_allow_inspect_verbs(self, mock_request):
|
|
mock_request.version.minor = 6
|
|
utils.check_allow_management_verbs('inspect')
|
|
|
|
def test_check_allow_inspect_verbs_fail(self, mock_request):
|
|
mock_request.version.minor = 5
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_management_verbs, 'inspect')
|
|
|
|
def test_check_allow_abort_verbs(self, mock_request):
|
|
mock_request.version.minor = 13
|
|
utils.check_allow_management_verbs('abort')
|
|
|
|
def test_check_allow_abort_verbs_fail(self, mock_request):
|
|
mock_request.version.minor = 12
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_management_verbs, 'abort')
|
|
|
|
def test_check_allow_clean_verbs(self, mock_request):
|
|
mock_request.version.minor = 15
|
|
utils.check_allow_management_verbs('clean')
|
|
|
|
def test_check_allow_clean_verbs_fail(self, mock_request):
|
|
mock_request.version.minor = 14
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_management_verbs, 'clean')
|
|
|
|
def test_check_allow_deploy_verbs(self, mock_request):
|
|
mock_request.version.minor = 73
|
|
utils.check_allow_management_verbs('deploy')
|
|
|
|
def test_check_allow_deploy_verbs_fail(self, mock_request):
|
|
mock_request.version.minor = 72
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_management_verbs, 'deploy')
|
|
|
|
def test_check_allow_undeploy_verbs(self, mock_request):
|
|
mock_request.version.minor = 73
|
|
utils.check_allow_management_verbs('undeploy')
|
|
|
|
def test_check_allow_undeploy_verbs_fail(self, mock_request):
|
|
mock_request.version.minor = 72
|
|
self.assertFalse(
|
|
utils.check_allow_management_verbs('undeploy'))
|
|
|
|
def test_check_allow_unknown_verbs(self, mock_request):
|
|
utils.check_allow_management_verbs('rebuild')
|
|
|
|
def test_allow_inject_nmi(self, mock_request):
|
|
mock_request.version.minor = 29
|
|
self.assertTrue(utils.allow_inject_nmi())
|
|
mock_request.version.minor = 28
|
|
self.assertFalse(utils.allow_inject_nmi())
|
|
|
|
def test_allow_links_node_states_and_driver_properties(self, mock_request):
|
|
mock_request.version.minor = 14
|
|
self.assertTrue(utils.allow_links_node_states_and_driver_properties())
|
|
mock_request.version.minor = 10
|
|
self.assertFalse(utils.allow_links_node_states_and_driver_properties())
|
|
|
|
def test_check_allow_adopt_verbs_fail(self, mock_request):
|
|
mock_request.version.minor = 16
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_management_verbs, 'adopt')
|
|
|
|
def test_check_allow_adopt_verbs(self, mock_request):
|
|
mock_request.version.minor = 17
|
|
utils.check_allow_management_verbs('adopt')
|
|
|
|
def test_allow_port_internal_info(self, mock_request):
|
|
mock_request.version.minor = 18
|
|
self.assertTrue(utils.allow_port_internal_info())
|
|
mock_request.version.minor = 17
|
|
self.assertFalse(utils.allow_port_internal_info())
|
|
|
|
def test_allow_port_advanced_net_fields(self, mock_request):
|
|
mock_request.version.minor = 19
|
|
self.assertTrue(utils.allow_port_advanced_net_fields())
|
|
mock_request.version.minor = 18
|
|
self.assertFalse(utils.allow_port_advanced_net_fields())
|
|
|
|
def test_allow_ramdisk_endpoints(self, mock_request):
|
|
mock_request.version.minor = 22
|
|
self.assertTrue(utils.allow_ramdisk_endpoints())
|
|
mock_request.version.minor = 21
|
|
self.assertFalse(utils.allow_ramdisk_endpoints())
|
|
|
|
def test_allow_portgroups(self, mock_request):
|
|
mock_request.version.minor = 23
|
|
self.assertTrue(utils.allow_portgroups())
|
|
mock_request.version.minor = 22
|
|
self.assertFalse(utils.allow_portgroups())
|
|
|
|
def test_allow_portgroups_subcontrollers(self, mock_request):
|
|
mock_request.version.minor = 24
|
|
self.assertTrue(utils.allow_portgroups_subcontrollers())
|
|
mock_request.version.minor = 23
|
|
self.assertFalse(utils.allow_portgroups_subcontrollers())
|
|
|
|
def test_allow_remove_chassis_uuid(self, mock_request):
|
|
mock_request.version.minor = 25
|
|
self.assertTrue(utils.allow_remove_chassis_uuid())
|
|
mock_request.version.minor = 24
|
|
self.assertFalse(utils.allow_remove_chassis_uuid())
|
|
|
|
def test_allow_portgroup_mode_properties(self, mock_request):
|
|
mock_request.version.minor = 26
|
|
self.assertTrue(utils.allow_portgroup_mode_properties())
|
|
mock_request.version.minor = 25
|
|
self.assertFalse(utils.allow_portgroup_mode_properties())
|
|
|
|
def test_allow_dynamic_drivers(self, mock_request):
|
|
mock_request.version.minor = 30
|
|
self.assertTrue(utils.allow_dynamic_drivers())
|
|
mock_request.version.minor = 29
|
|
self.assertFalse(utils.allow_dynamic_drivers())
|
|
|
|
def test_allow_volume(self, mock_request):
|
|
mock_request.version.minor = 32
|
|
self.assertTrue(utils.allow_volume())
|
|
mock_request.version.minor = 31
|
|
self.assertFalse(utils.allow_volume())
|
|
|
|
def test_allow_storage_interface(self, mock_request):
|
|
mock_request.version.minor = 33
|
|
self.assertTrue(utils.allow_storage_interface())
|
|
mock_request.version.minor = 32
|
|
self.assertFalse(utils.allow_storage_interface())
|
|
|
|
def test_allow_traits(self, mock_request):
|
|
mock_request.version.minor = 37
|
|
self.assertTrue(utils.allow_traits())
|
|
mock_request.version.minor = 36
|
|
self.assertFalse(utils.allow_traits())
|
|
|
|
@mock.patch.object(objects.Port, 'supports_physical_network',
|
|
autospec=True)
|
|
def test_allow_port_physical_network_no_pin(self, mock_spn, mock_request):
|
|
mock_spn.return_value = True
|
|
mock_request.version.minor = 34
|
|
self.assertTrue(utils.allow_port_physical_network())
|
|
mock_request.version.minor = 33
|
|
self.assertFalse(utils.allow_port_physical_network())
|
|
|
|
@mock.patch.object(objects.Port, 'supports_physical_network',
|
|
autospec=True)
|
|
def test_allow_port_physical_network_pin(self, mock_spn, mock_request):
|
|
mock_spn.return_value = False
|
|
mock_request.version.minor = 34
|
|
self.assertFalse(utils.allow_port_physical_network())
|
|
mock_request.version.minor = 33
|
|
self.assertFalse(utils.allow_port_physical_network())
|
|
|
|
def test_allow_node_rebuild_with_configdrive(self, mock_request):
|
|
mock_request.version.minor = 35
|
|
self.assertTrue(utils.allow_node_rebuild_with_configdrive())
|
|
mock_request.version.minor = 34
|
|
self.assertFalse(utils.allow_node_rebuild_with_configdrive())
|
|
|
|
def test_allow_configdrive_vendor_data(self, mock_request):
|
|
mock_request.version.minor = 59
|
|
self.assertTrue(utils.allow_configdrive_vendor_data())
|
|
mock_request.version.minor = 58
|
|
self.assertFalse(utils.allow_configdrive_vendor_data())
|
|
|
|
def test_check_allow_configdrive_fails(self, mock_request):
|
|
mock_request.version.minor = 35
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.check_allow_configdrive, states.DELETED,
|
|
"abcd")
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.check_allow_configdrive, states.ACTIVE,
|
|
{'meta_data': {}})
|
|
mock_request.version.minor = 34
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.check_allow_configdrive, states.REBUILD,
|
|
"abcd")
|
|
|
|
def test_check_allow_configdrive(self, mock_request):
|
|
mock_request.version.minor = 35
|
|
utils.check_allow_configdrive(states.ACTIVE, "abcd")
|
|
utils.check_allow_configdrive(states.REBUILD, "abcd")
|
|
mock_request.version.minor = 34
|
|
utils.check_allow_configdrive(states.ACTIVE, "abcd")
|
|
|
|
def test_check_allow_configdrive_as_dict(self, mock_request):
|
|
mock_request.version.minor = 59
|
|
utils.check_allow_configdrive(states.ACTIVE, {'meta_data': {}})
|
|
utils.check_allow_configdrive(states.ACTIVE, {'meta_data': {},
|
|
'network_data': {},
|
|
'user_data': {},
|
|
'vendor_data': {}})
|
|
utils.check_allow_configdrive(states.ACTIVE, {'user_data': 'foo'})
|
|
utils.check_allow_configdrive(states.ACTIVE, {'user_data': ['foo']})
|
|
|
|
def test_check_allow_configdrive_vendor_data_failed(self, mock_request):
|
|
mock_request.version.minor = 58
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.check_allow_configdrive,
|
|
states.ACTIVE,
|
|
{'meta_data': {},
|
|
'network_data': {},
|
|
'user_data': {},
|
|
'vendor_data': {}})
|
|
|
|
def test_check_allow_configdrive_as_dict_invalid(self, mock_request):
|
|
mock_request.version.minor = 59
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.check_allow_configdrive, states.REBUILD,
|
|
{'foo': 'bar'})
|
|
for key in ['meta_data', 'network_data']:
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.check_allow_configdrive, states.REBUILD,
|
|
{key: 'a string'})
|
|
for key in ['meta_data', 'network_data', 'user_data']:
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.check_allow_configdrive, states.REBUILD,
|
|
{key: 42})
|
|
|
|
def test_allow_rescue_interface(self, mock_request):
|
|
mock_request.version.minor = 38
|
|
self.assertTrue(utils.allow_rescue_interface())
|
|
mock_request.version.minor = 37
|
|
self.assertFalse(utils.allow_rescue_interface())
|
|
|
|
def test_allow_inspect_abort(self, mock_request):
|
|
mock_request.version.minor = 41
|
|
self.assertTrue(utils.allow_inspect_abort())
|
|
mock_request.version.minor = 40
|
|
self.assertFalse(utils.allow_inspect_abort())
|
|
|
|
def test_allow_port_is_smartnic(self, mock_request):
|
|
mock_request.version.minor = 53
|
|
self.assertTrue(utils.allow_port_is_smartnic())
|
|
mock_request.version.minor = 52
|
|
self.assertFalse(utils.allow_port_is_smartnic())
|
|
|
|
def test_allow_deploy_templates(self, mock_request):
|
|
mock_request.version.minor = 55
|
|
self.assertTrue(utils.allow_deploy_templates())
|
|
mock_request.version.minor = 54
|
|
self.assertFalse(utils.allow_deploy_templates())
|
|
|
|
def test_allow_agent_token(self, mock_request):
|
|
mock_request.version.minor = 62
|
|
self.assertTrue(utils.allow_agent_token())
|
|
mock_request.version.minor = 61
|
|
self.assertFalse(utils.allow_agent_token())
|
|
|
|
def test_allow_deploy_steps(self, mock_request):
|
|
mock_request.version.minor = 69
|
|
self.assertTrue(utils.allow_deploy_steps())
|
|
mock_request.version.minor = 68
|
|
self.assertFalse(utils.allow_deploy_steps())
|
|
|
|
def test_check_allow_deploy_steps(self, mock_request):
|
|
mock_request.version.minor = 69
|
|
utils.check_allow_deploy_steps(states.ACTIVE, {'a': 1})
|
|
utils.check_allow_deploy_steps(states.REBUILD, {'a': 1})
|
|
|
|
def test_check_allow_deploy_steps_empty(self, mock_request):
|
|
utils.check_allow_deploy_steps(states.ACTIVE, None)
|
|
|
|
def test_check_allow_deploy_steps_version_older(self, mock_request):
|
|
mock_request.version.minor = 68
|
|
self.assertRaises(exception.NotAcceptable,
|
|
utils.check_allow_deploy_steps,
|
|
states.ACTIVE, {'a': 1})
|
|
|
|
def test_check_allow_deploy_steps_target_unsupported(self, mock_request):
|
|
mock_request.version.minor = 69
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.check_allow_deploy_steps,
|
|
states.MANAGEABLE, {'a': 1})
|
|
|
|
|
|
@mock.patch.object(api, 'request', spec_set=['context', 'version'])
|
|
class TestNodeIdent(base.TestCase):
|
|
|
|
def setUp(self):
|
|
super(TestNodeIdent, self).setUp()
|
|
self.valid_name = 'my-host'
|
|
self.valid_uuid = uuidutils.generate_uuid()
|
|
self.invalid_name = 'Mr Plow'
|
|
self.node = test_api_utils.post_get_test_node()
|
|
|
|
def test_allow_node_logical_names_pre_name(self, mock_pecan_req):
|
|
mock_pecan_req.version.minor = 1
|
|
self.assertFalse(utils.allow_node_logical_names())
|
|
|
|
def test_allow_node_logical_names_post_name(self, mock_pecan_req):
|
|
mock_pecan_req.version.minor = 5
|
|
self.assertTrue(utils.allow_node_logical_names())
|
|
|
|
def test_is_valid_node_name(self, mock_pecan_req):
|
|
mock_pecan_req.version.minor = 10
|
|
self.assertTrue(utils.is_valid_node_name(self.valid_name))
|
|
self.assertFalse(utils.is_valid_node_name(self.invalid_name))
|
|
self.assertFalse(utils.is_valid_node_name(self.valid_uuid))
|
|
|
|
@mock.patch.object(utils, 'allow_node_logical_names', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_uuid', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_name', autospec=True)
|
|
def test_get_rpc_node_expect_uuid(self, mock_gbn, mock_gbu, mock_anln,
|
|
mock_pr):
|
|
mock_anln.return_value = True
|
|
self.node['uuid'] = self.valid_uuid
|
|
mock_gbu.return_value = self.node
|
|
self.assertEqual(self.node, utils.get_rpc_node(self.valid_uuid))
|
|
self.assertEqual(1, mock_gbu.call_count)
|
|
self.assertEqual(0, mock_gbn.call_count)
|
|
|
|
@mock.patch.object(utils, 'allow_node_logical_names', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_uuid', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_name', autospec=True)
|
|
def test_get_rpc_node_expect_name(self, mock_gbn, mock_gbu, mock_anln,
|
|
mock_pr):
|
|
mock_pr.version.minor = 10
|
|
mock_anln.return_value = True
|
|
self.node['name'] = self.valid_name
|
|
mock_gbn.return_value = self.node
|
|
self.assertEqual(self.node, utils.get_rpc_node(self.valid_name))
|
|
self.assertEqual(0, mock_gbu.call_count)
|
|
self.assertEqual(1, mock_gbn.call_count)
|
|
|
|
@mock.patch.object(utils, 'allow_node_logical_names', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_uuid', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_name', autospec=True)
|
|
def test_get_rpc_node_invalid_name(self, mock_gbn, mock_gbu,
|
|
mock_anln, mock_pr):
|
|
mock_pr.version.minor = 10
|
|
mock_anln.return_value = True
|
|
self.assertRaises(exception.InvalidUuidOrName,
|
|
utils.get_rpc_node,
|
|
self.invalid_name)
|
|
|
|
@mock.patch.object(utils, 'allow_node_logical_names', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_uuid', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_name', autospec=True)
|
|
def test_get_rpc_node_by_uuid_no_logical_name(self, mock_gbn, mock_gbu,
|
|
mock_anln, mock_pr):
|
|
# allow_node_logical_name() should have no effect
|
|
mock_anln.return_value = False
|
|
self.node['uuid'] = self.valid_uuid
|
|
mock_gbu.return_value = self.node
|
|
self.assertEqual(self.node, utils.get_rpc_node(self.valid_uuid))
|
|
self.assertEqual(1, mock_gbu.call_count)
|
|
self.assertEqual(0, mock_gbn.call_count)
|
|
|
|
@mock.patch.object(utils, 'allow_node_logical_names', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_uuid', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_name', autospec=True)
|
|
def test_get_rpc_node_by_name_no_logical_name(self, mock_gbn, mock_gbu,
|
|
mock_anln, mock_pr):
|
|
mock_anln.return_value = False
|
|
self.node['name'] = self.valid_name
|
|
mock_gbn.return_value = self.node
|
|
self.assertRaises(exception.NodeNotFound,
|
|
utils.get_rpc_node,
|
|
self.valid_name)
|
|
|
|
@mock.patch.object(objects.Node, 'get_by_id', autospec=True)
|
|
def test_populate_node_uuid(self, mock_gbi, mock_pr):
|
|
port = obj_utils.get_test_port(self.context)
|
|
node = obj_utils.get_test_node(self.context, id=port.node_id)
|
|
mock_gbi.return_value = node
|
|
|
|
# successful lookup
|
|
d = {}
|
|
utils.populate_node_uuid(port, d)
|
|
self.assertEqual({
|
|
'node_uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
|
}, d)
|
|
|
|
# not found, raise exception
|
|
mock_gbi.side_effect = exception.NodeNotFound(node=port.node_id)
|
|
d = {}
|
|
self.assertRaises(exception.NodeNotFound,
|
|
utils.populate_node_uuid, port, d)
|
|
|
|
@mock.patch.object(utils, 'check_owner_policy', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_uuid', autospec=True)
|
|
def test_replace_node_uuid_with_id(self, mock_gbu, mock_check, mock_pr):
|
|
node = obj_utils.get_test_node(self.context, id=1)
|
|
mock_gbu.return_value = node
|
|
to_dict = {'node_uuid': self.valid_uuid}
|
|
|
|
self.assertEqual(node, utils.replace_node_uuid_with_id(to_dict))
|
|
self.assertEqual({'node_id': 1}, to_dict)
|
|
mock_check.assert_called_once_with('node', 'baremetal:node:get',
|
|
None, None,
|
|
conceal_node=mock.ANY)
|
|
|
|
@mock.patch.object(objects.Node, 'get_by_uuid', autospec=True)
|
|
def test_replace_node_uuid_with_id_not_found(self, mock_gbu, mock_pr):
|
|
to_dict = {'node_uuid': self.valid_uuid}
|
|
mock_gbu.side_effect = exception.NodeNotFound(node=self.valid_uuid)
|
|
|
|
e = self.assertRaises(exception.NodeNotFound,
|
|
utils.replace_node_uuid_with_id, to_dict)
|
|
self.assertEqual(400, e.code)
|
|
|
|
@mock.patch.object(objects.Node, 'get_by_id', autospec=True)
|
|
def test_replace_node_id_with_uuid(self, mock_gbi, mock_pr):
|
|
node = obj_utils.get_test_node(self.context, uuid=self.valid_uuid)
|
|
mock_gbi.return_value = node
|
|
to_dict = {'node_id': 1}
|
|
|
|
self.assertEqual(node, utils.replace_node_id_with_uuid(to_dict))
|
|
self.assertEqual({'node_uuid': self.valid_uuid}, to_dict)
|
|
|
|
@mock.patch.object(objects.Node, 'get_by_id', autospec=True)
|
|
def test_replace_node_id_with_uuid_not_found(self, mock_gbi, mock_pr):
|
|
to_dict = {'node_id': 1}
|
|
mock_gbi.side_effect = exception.NodeNotFound(node=1)
|
|
|
|
e = self.assertRaises(exception.NodeNotFound,
|
|
utils.replace_node_id_with_uuid, to_dict)
|
|
self.assertEqual(400, e.code)
|
|
|
|
|
|
class TestVendorPassthru(base.TestCase):
|
|
|
|
def test_method_not_specified(self):
|
|
self.assertRaises(exception.ClientSideError,
|
|
utils.vendor_passthru, 'fake-ident',
|
|
None, 'fake-topic', data='fake-data')
|
|
|
|
@mock.patch.object(api, 'request',
|
|
spec_set=['method', 'context', 'rpcapi'])
|
|
def _vendor_passthru(self, mock_request, async_call=True,
|
|
driver_passthru=False):
|
|
return_value = {
|
|
'return': 'SpongeBob',
|
|
'async': async_call,
|
|
'attach': False
|
|
}
|
|
mock_request.method = 'post'
|
|
mock_request.context = 'fake-context'
|
|
|
|
passthru_mock = None
|
|
if driver_passthru:
|
|
passthru_mock = mock_request.rpcapi.driver_vendor_passthru
|
|
else:
|
|
passthru_mock = mock_request.rpcapi.vendor_passthru
|
|
passthru_mock.return_value = return_value
|
|
|
|
response = utils.vendor_passthru('fake-ident', 'squarepants',
|
|
'fake-topic', data='fake-data',
|
|
driver_passthru=driver_passthru)
|
|
|
|
passthru_mock.assert_called_once_with(
|
|
'fake-context', 'fake-ident', 'squarepants', 'POST',
|
|
'fake-data', 'fake-topic')
|
|
self.assertIsInstance(response, utils.PassthruResponse)
|
|
self.assertEqual('SpongeBob', response.obj)
|
|
sc = http_client.ACCEPTED if async_call else http_client.OK
|
|
self.assertEqual(sc, response.status_code)
|
|
|
|
def test_vendor_passthru_async(self):
|
|
self._vendor_passthru()
|
|
|
|
def test_vendor_passthru_sync(self):
|
|
self._vendor_passthru(async_call=False)
|
|
|
|
def test_driver_vendor_passthru_async(self):
|
|
self._vendor_passthru(driver_passthru=True)
|
|
|
|
def test_driver_vendor_passthru_sync(self):
|
|
self._vendor_passthru(async_call=False, driver_passthru=True)
|
|
|
|
@mock.patch.object(api, 'request',
|
|
spec_set=['method', 'context', 'rpcapi'])
|
|
def _test_vendor_passthru_attach(self, return_value, expct_return_value,
|
|
mock_request):
|
|
return_ = {'return': return_value, 'async': False, 'attach': True}
|
|
mock_request.method = 'get'
|
|
mock_request.context = 'fake-context'
|
|
mock_request.rpcapi.driver_vendor_passthru.return_value = return_
|
|
response = utils.vendor_passthru('fake-ident', 'bar',
|
|
'fake-topic', data='fake-data',
|
|
driver_passthru=True)
|
|
mock_request.rpcapi.driver_vendor_passthru.assert_called_once_with(
|
|
'fake-context', 'fake-ident', 'bar', 'GET',
|
|
'fake-data', 'fake-topic')
|
|
|
|
# Assert file was attached to the response object
|
|
self.assertIsInstance(response.obj, io.BytesIO)
|
|
self.assertEqual(expct_return_value, response.obj.read())
|
|
# Assert response message is none
|
|
self.assertIsInstance(response, utils.PassthruResponse)
|
|
self.assertEqual(http_client.OK, response.status_code)
|
|
|
|
def test_vendor_passthru_attach(self):
|
|
self._test_vendor_passthru_attach('foo', b'foo')
|
|
|
|
def test_vendor_passthru_attach_unicode_to_byte(self):
|
|
self._test_vendor_passthru_attach(u'não', b'n\xc3\xa3o')
|
|
|
|
def test_vendor_passthru_attach_byte_to_byte(self):
|
|
self._test_vendor_passthru_attach(b'\x00\x01', b'\x00\x01')
|
|
|
|
def test_get_controller_reserved_names(self):
|
|
expected = ['maintenance', 'management', 'states',
|
|
'vendor_passthru', 'validate', 'detail']
|
|
self.assertEqual(sorted(expected),
|
|
sorted(utils.get_controller_reserved_names(
|
|
api_node.NodesController)))
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_policy(self, mock_authorize, mock_pr):
|
|
fake_context = ironic_context.RequestContext()
|
|
mock_pr.context = fake_context
|
|
expected_target = dict(fake_context.to_policy_values())
|
|
utils.check_policy('fake-policy')
|
|
mock_authorize.assert_called_once_with('fake-policy', expected_target,
|
|
fake_context)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_policy_forbidden(self, mock_authorize, mock_pr):
|
|
mock_authorize.side_effect = exception.HTTPForbidden(resource='fake')
|
|
self.assertRaises(exception.HTTPForbidden,
|
|
utils.check_policy, 'fake-policy')
|
|
|
|
|
|
class TestPortgroupIdent(base.TestCase):
|
|
def setUp(self):
|
|
super(TestPortgroupIdent, self).setUp()
|
|
self.valid_name = 'my-portgroup'
|
|
self.valid_uuid = uuidutils.generate_uuid()
|
|
self.invalid_name = 'My Portgroup'
|
|
self.portgroup = test_api_utils.post_get_test_portgroup()
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(objects.Portgroup, 'get_by_name', autospec=True)
|
|
def test_get_rpc_portgroup_name(self, mock_gbn, mock_pr):
|
|
mock_gbn.return_value = self.portgroup
|
|
self.assertEqual(self.portgroup, utils.get_rpc_portgroup(
|
|
self.valid_name))
|
|
mock_gbn.assert_called_once_with(mock_pr.context, self.valid_name)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(objects.Portgroup, 'get_by_uuid', autospec=True)
|
|
def test_get_rpc_portgroup_uuid(self, mock_gbu, mock_pr):
|
|
self.portgroup['uuid'] = self.valid_uuid
|
|
mock_gbu.return_value = self.portgroup
|
|
self.assertEqual(self.portgroup, utils.get_rpc_portgroup(
|
|
self.valid_uuid))
|
|
mock_gbu.assert_called_once_with(mock_pr.context, self.valid_uuid)
|
|
|
|
def test_get_rpc_portgroup_invalid_name(self):
|
|
self.assertRaises(exception.InvalidUuidOrName,
|
|
utils.get_rpc_portgroup,
|
|
self.invalid_name)
|
|
|
|
|
|
class TestCheckOwnerPolicy(base.TestCase):
|
|
def setUp(self):
|
|
super(TestCheckOwnerPolicy, self).setUp()
|
|
self.valid_node_uuid = uuidutils.generate_uuid()
|
|
self.node = test_api_utils.post_get_test_node()
|
|
self.node['owner'] = '12345'
|
|
self.node['lessee'] = '54321'
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_owner_policy(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
fake_context = ironic_context.RequestContext()
|
|
mock_pr.version.minor = 50
|
|
mock_pr.context = fake_context
|
|
expected_target = dict(fake_context.to_policy_values())
|
|
expected_target['node.owner'] = '12345'
|
|
expected_target['node.lessee'] = '54321'
|
|
|
|
utils.check_owner_policy(
|
|
'node', 'fake_policy', self.node['owner'], self.node['lessee']
|
|
)
|
|
mock_authorize.assert_called_once_with(
|
|
'fake_policy', expected_target, fake_context)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_owner_policy_forbidden(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.version.minor = 50
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_authorize.side_effect = exception.HTTPForbidden(resource='fake')
|
|
|
|
self.assertRaises(
|
|
exception.HTTPForbidden,
|
|
utils.check_owner_policy,
|
|
'node',
|
|
'fake-policy',
|
|
self.node
|
|
)
|
|
|
|
|
|
class TestCheckNodePolicyAndRetrieve(base.TestCase):
|
|
def setUp(self):
|
|
super(TestCheckNodePolicyAndRetrieve, self).setUp()
|
|
self.valid_node_uuid = uuidutils.generate_uuid()
|
|
self.node = test_api_utils.post_get_test_node()
|
|
self.node['owner'] = '12345'
|
|
self.node['lessee'] = '54321'
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_node', autospec=True)
|
|
@mock.patch.object(utils, 'get_rpc_node_with_suffix', autospec=True)
|
|
def test_check_node_policy_and_retrieve(
|
|
self, mock_grnws, mock_grn, mock_authorize, mock_pr
|
|
):
|
|
fake_context = ironic_context.RequestContext()
|
|
expected_target = dict(fake_context.to_policy_values())
|
|
expected_target['node.owner'] = '12345'
|
|
expected_target['node.lessee'] = '54321'
|
|
mock_pr.context = fake_context
|
|
|
|
mock_pr.version.minor = 50
|
|
mock_grn.return_value = self.node
|
|
|
|
rpc_node = utils.check_node_policy_and_retrieve(
|
|
'fake_policy', self.valid_node_uuid
|
|
)
|
|
authorize_calls = [
|
|
mock.call('baremetal:node:get', expected_target, fake_context),
|
|
mock.call('fake_policy', expected_target, fake_context)]
|
|
|
|
mock_grn.assert_called_once_with(self.valid_node_uuid)
|
|
mock_grnws.assert_not_called()
|
|
mock_authorize.assert_has_calls(authorize_calls)
|
|
self.assertEqual(self.node, rpc_node)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_node', autospec=True)
|
|
@mock.patch.object(utils, 'get_rpc_node_with_suffix', autospec=True)
|
|
def test_check_node_policy_and_retrieve_with_suffix(
|
|
self, mock_grnws, mock_grn, mock_authorize, mock_pr
|
|
):
|
|
fake_context = ironic_context.RequestContext()
|
|
expected_target = fake_context.to_policy_values()
|
|
expected_target['node.owner'] = '12345'
|
|
expected_target['node.lessee'] = '54321'
|
|
mock_pr.context = fake_context
|
|
mock_pr.version.minor = 50
|
|
mock_grnws.return_value = self.node
|
|
|
|
rpc_node = utils.check_node_policy_and_retrieve(
|
|
'fake_policy', self.valid_node_uuid, True
|
|
)
|
|
mock_grn.assert_not_called()
|
|
mock_grnws.assert_called_once_with(self.valid_node_uuid)
|
|
authorize_calls = [
|
|
mock.call('baremetal:node:get', expected_target, fake_context),
|
|
mock.call('fake_policy', expected_target, fake_context)]
|
|
mock_authorize.assert_has_calls(authorize_calls)
|
|
self.assertEqual(self.node, rpc_node)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_node', autospec=True)
|
|
def test_check_node_policy_and_retrieve_no_node_policy_notfound(
|
|
self, mock_grn, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_authorize.side_effect = exception.HTTPForbidden(resource='fake')
|
|
mock_grn.side_effect = exception.NodeNotFound(
|
|
node=self.valid_node_uuid)
|
|
|
|
self.assertRaises(
|
|
exception.NodeNotFound,
|
|
utils.check_node_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_node_uuid
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_node', autospec=True)
|
|
def test_check_node_policy_and_retrieve_no_node(
|
|
self, mock_grn, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_grn.side_effect = exception.NodeNotFound(
|
|
node=self.valid_node_uuid)
|
|
|
|
self.assertRaises(
|
|
exception.NodeNotFound,
|
|
utils.check_node_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_node_uuid
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_node', autospec=True)
|
|
def test_check_node_policy_and_retrieve_policy_forbidden(
|
|
self, mock_grn, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.version.minor = 50
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_authorize.side_effect = exception.HTTPForbidden(resource='fake')
|
|
mock_grn.return_value = self.node
|
|
|
|
self.assertRaises(
|
|
exception.NodeNotFound,
|
|
utils.check_node_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_node_uuid
|
|
)
|
|
|
|
|
|
class TestCheckAllocationPolicyAndRetrieve(base.TestCase):
|
|
def setUp(self):
|
|
super(TestCheckAllocationPolicyAndRetrieve, self).setUp()
|
|
self.valid_allocation_uuid = uuidutils.generate_uuid()
|
|
self.allocation = test_api_utils.allocation_post_data()
|
|
self.allocation['owner'] = '12345'
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_allocation_with_suffix', autospec=True)
|
|
def test_check_node_policy_and_retrieve(
|
|
self, mock_graws, mock_authorize, mock_pr
|
|
):
|
|
fake_context = ironic_context.RequestContext()
|
|
expected_target = dict(fake_context.to_policy_values())
|
|
expected_target['allocation.owner'] = '12345'
|
|
mock_pr.version.minor = 60
|
|
mock_pr.context = fake_context
|
|
mock_graws.return_value = self.allocation
|
|
|
|
rpc_allocation = utils.check_allocation_policy_and_retrieve(
|
|
'fake_policy', self.valid_allocation_uuid
|
|
)
|
|
mock_graws.assert_called_once_with(self.valid_allocation_uuid)
|
|
|
|
expected = [mock.call('baremetal:allocation:get',
|
|
expected_target, fake_context),
|
|
mock.call('fake_policy', expected_target,
|
|
fake_context)]
|
|
mock_authorize.assert_has_calls(expected)
|
|
self.assertEqual(self.allocation, rpc_allocation)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_allocation_with_suffix', autospec=True)
|
|
def test_check_alloc_policy_and_retrieve_no_alloc_policy_not_found(
|
|
self, mock_graws, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_authorize.side_effect = exception.HTTPForbidden(resource='fake')
|
|
mock_graws.side_effect = exception.AllocationNotFound(
|
|
allocation=self.valid_allocation_uuid)
|
|
|
|
self.assertRaises(
|
|
exception.AllocationNotFound,
|
|
utils.check_allocation_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_allocation_uuid
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_allocation_with_suffix', autospec=True)
|
|
def test_check_allocation_policy_and_retrieve_no_allocation(
|
|
self, mock_graws, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_graws.side_effect = exception.AllocationNotFound(
|
|
allocation=self.valid_allocation_uuid)
|
|
|
|
self.assertRaises(
|
|
exception.AllocationNotFound,
|
|
utils.check_allocation_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_allocation_uuid
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(utils, 'get_rpc_allocation_with_suffix', autospec=True)
|
|
def test_check_allocation_policy_and_retrieve_policy_not_found(
|
|
self, mock_graws, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.version.minor = 50
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_authorize.side_effect = exception.HTTPForbidden(resource='fake')
|
|
mock_graws.return_value = self.allocation
|
|
|
|
self.assertRaises(
|
|
exception.AllocationNotFound,
|
|
utils.check_allocation_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_allocation_uuid
|
|
)
|
|
|
|
|
|
class TestCheckMultipleNodePoliciesAndRetrieve(base.TestCase):
|
|
def setUp(self):
|
|
super(TestCheckMultipleNodePoliciesAndRetrieve, self).setUp()
|
|
self.valid_node_uuid = uuidutils.generate_uuid()
|
|
self.node = test_api_utils.post_get_test_node()
|
|
self.node['owner'] = '12345'
|
|
self.node['lessee'] = '54321'
|
|
|
|
@mock.patch.object(utils, 'check_node_policy_and_retrieve', autospec=True)
|
|
@mock.patch.object(utils, 'check_owner_policy', autospec=True)
|
|
def test_check_multiple_node_policies_and_retrieve(
|
|
self, mock_cop, mock_cnpar
|
|
):
|
|
mock_cnpar.return_value = self.node
|
|
mock_cop.return_value = True
|
|
|
|
rpc_node = utils.check_multiple_node_policies_and_retrieve(
|
|
['fake_policy_1', 'fake_policy_2'], self.valid_node_uuid
|
|
)
|
|
mock_cnpar.assert_called_once_with('fake_policy_1',
|
|
self.valid_node_uuid, False)
|
|
mock_cop.assert_called_once_with(
|
|
'node', 'fake_policy_2', '12345', '54321')
|
|
self.assertEqual(self.node, rpc_node)
|
|
|
|
@mock.patch.object(utils, 'check_node_policy_and_retrieve', autospec=True)
|
|
@mock.patch.object(utils, 'check_owner_policy', autospec=True)
|
|
def test_check_multiple_node_policies_and_retrieve_first_fail(
|
|
self, mock_cop, mock_cnpar
|
|
):
|
|
mock_cnpar.side_effect = exception.HTTPForbidden(resource='fake')
|
|
mock_cop.return_value = True
|
|
|
|
self.assertRaises(
|
|
exception.HTTPForbidden,
|
|
utils.check_multiple_node_policies_and_retrieve,
|
|
['fake_policy_1', 'fake_policy_2'],
|
|
self.valid_node_uuid
|
|
)
|
|
mock_cnpar.assert_called_once_with('fake_policy_1',
|
|
self.valid_node_uuid, False)
|
|
mock_cop.assert_not_called()
|
|
|
|
@mock.patch.object(utils, 'check_node_policy_and_retrieve', autospec=True)
|
|
@mock.patch.object(utils, 'check_owner_policy', autospec=True)
|
|
def test_check_node_policy_and_retrieve_no_node(
|
|
self, mock_cop, mock_cnpar
|
|
):
|
|
mock_cnpar.return_value = self.node
|
|
mock_cop.side_effect = exception.HTTPForbidden(resource='fake')
|
|
|
|
self.assertRaises(
|
|
exception.HTTPForbidden,
|
|
utils.check_multiple_node_policies_and_retrieve,
|
|
['fake_policy_1', 'fake_policy_2'],
|
|
self.valid_node_uuid
|
|
)
|
|
mock_cnpar.assert_called_once_with('fake_policy_1',
|
|
self.valid_node_uuid, False)
|
|
mock_cop.assert_called_once_with(
|
|
'node', 'fake_policy_2', '12345', '54321')
|
|
|
|
|
|
class TestCheckListPolicy(base.TestCase):
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_list_policy(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {
|
|
'project_id': '12345'
|
|
}
|
|
mock_pr.version.minor = 50
|
|
|
|
owner = utils.check_list_policy('node')
|
|
self.assertIsNone(owner)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_list_policy_with_owner(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {
|
|
'project_id': '12345'
|
|
}
|
|
mock_pr.version.minor = 50
|
|
|
|
owner = utils.check_list_policy('node', '12345')
|
|
self.assertEqual(owner, '12345')
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_list_policy_forbidden(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
def mock_authorize_function(rule, target, creds):
|
|
raise exception.HTTPForbidden(resource='fake')
|
|
mock_authorize.side_effect = mock_authorize_function
|
|
mock_pr.context.to_policy_values.return_value = {
|
|
'project_id': '12345'
|
|
}
|
|
mock_pr.version.minor = 50
|
|
|
|
self.assertRaises(
|
|
exception.HTTPForbidden,
|
|
utils.check_list_policy,
|
|
'node'
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_list_policy_forbidden_no_project(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
def mock_authorize_function(rule, target, creds):
|
|
if rule == 'baremetal:node:list_all':
|
|
raise exception.HTTPForbidden(resource='fake')
|
|
return True
|
|
mock_authorize.side_effect = mock_authorize_function
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_pr.version.minor = 50
|
|
|
|
self.assertRaises(
|
|
exception.HTTPForbidden,
|
|
utils.check_list_policy,
|
|
'node'
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_list_policy_non_admin(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
def mock_authorize_function(rule, target, creds):
|
|
if rule == 'baremetal:node:list_all':
|
|
raise exception.HTTPForbidden(resource='fake')
|
|
return True
|
|
mock_authorize.side_effect = mock_authorize_function
|
|
mock_pr.context.to_policy_values.return_value = {
|
|
'project_id': '12345'
|
|
}
|
|
mock_pr.version.minor = 50
|
|
|
|
owner = utils.check_list_policy('node')
|
|
self.assertEqual(owner, '12345')
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_list_policy_non_admin_owner_proj_mismatch(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
def mock_authorize_function(rule, target, creds):
|
|
if rule == 'baremetal:node:list_all':
|
|
raise exception.HTTPForbidden(resource='fake')
|
|
return True
|
|
mock_authorize.side_effect = mock_authorize_function
|
|
mock_pr.context.to_policy_values.return_value = {
|
|
'project_id': '12345'
|
|
}
|
|
mock_pr.version.minor = 50
|
|
|
|
self.assertRaises(
|
|
exception.HTTPForbidden,
|
|
utils.check_list_policy,
|
|
'node',
|
|
'54321'
|
|
)
|
|
|
|
|
|
class TestCheckPortPolicyAndRetrieve(base.TestCase):
|
|
def setUp(self):
|
|
super(TestCheckPortPolicyAndRetrieve, self).setUp()
|
|
self.valid_port_uuid = uuidutils.generate_uuid()
|
|
self.node = test_api_utils.post_get_test_node()
|
|
self.node['owner'] = '12345'
|
|
self.node['lessee'] = '54321'
|
|
self.port = objects.Port(self.context, node_id=42)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(objects.Port, 'get_by_uuid', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_id', autospec=True)
|
|
def test_check_port_policy_and_retrieve(
|
|
self, mock_ngbi, mock_pgbu, mock_authorize, mock_pr
|
|
):
|
|
fake_context = ironic_context.RequestContext()
|
|
expected_target = fake_context.to_policy_values()
|
|
expected_target['node.owner'] = '12345'
|
|
expected_target['node.lessee'] = '54321'
|
|
mock_pr.context = fake_context
|
|
mock_pr.version.minor = 50
|
|
mock_pgbu.return_value = self.port
|
|
mock_ngbi.return_value = self.node
|
|
|
|
rpc_port, rpc_node = utils.check_port_policy_and_retrieve(
|
|
'fake_policy', self.valid_port_uuid
|
|
)
|
|
mock_pgbu.assert_called_once_with(mock_pr.context,
|
|
self.valid_port_uuid)
|
|
mock_ngbi.assert_called_once_with(mock_pr.context, 42)
|
|
expected = [
|
|
mock.call('baremetal:node:get', expected_target, fake_context),
|
|
mock.call('fake_policy', expected_target, fake_context)]
|
|
|
|
mock_authorize.assert_has_calls(expected)
|
|
|
|
self.assertEqual(self.port, rpc_port)
|
|
self.assertEqual(self.node, rpc_node)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(objects.Port, 'get_by_uuid', autospec=True)
|
|
def test_check_port_policy_and_retrieve_no_port_policy_forbidden(
|
|
self, mock_pgbu, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_authorize.side_effect = exception.HTTPForbidden(resource='fake')
|
|
mock_pgbu.side_effect = exception.PortNotFound(
|
|
port=self.valid_port_uuid)
|
|
|
|
self.assertRaises(
|
|
exception.PortNotFound,
|
|
utils.check_port_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_port_uuid
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(objects.Port, 'get_by_uuid', autospec=True)
|
|
def test_check_port_policy_and_retrieve_no_port(
|
|
self, mock_pgbu, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_pgbu.side_effect = exception.PortNotFound(
|
|
port=self.valid_port_uuid)
|
|
|
|
self.assertRaises(
|
|
exception.PortNotFound,
|
|
utils.check_port_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_port_uuid
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
@mock.patch.object(objects.Port, 'get_by_uuid', autospec=True)
|
|
@mock.patch.object(objects.Node, 'get_by_id', autospec=True)
|
|
def test_check_port_policy_and_retrieve_policy_notfound(
|
|
self, mock_ngbi, mock_pgbu, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.version.minor = 50
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_authorize.side_effect = exception.HTTPForbidden(resource='fake')
|
|
mock_pgbu.return_value = self.port
|
|
mock_ngbi.return_value = self.node
|
|
|
|
self.assertRaises(
|
|
exception.PortNotFound,
|
|
utils.check_port_policy_and_retrieve,
|
|
'fake-policy',
|
|
self.valid_port_uuid
|
|
)
|
|
|
|
|
|
class TestCheckPortListPolicy(base.TestCase):
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_port_list_policy(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
mock_pr.context.to_policy_values.return_value = {
|
|
'project_id': '12345'
|
|
}
|
|
mock_pr.version.minor = 50
|
|
|
|
owner = utils.check_port_list_policy()
|
|
self.assertIsNone(owner)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_port_list_policy_forbidden(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
def mock_authorize_function(rule, target, creds):
|
|
raise exception.HTTPForbidden(resource='fake')
|
|
mock_authorize.side_effect = mock_authorize_function
|
|
mock_pr.context.to_policy_values.return_value = {
|
|
'project_id': '12345'
|
|
}
|
|
mock_pr.version.minor = 50
|
|
|
|
self.assertRaises(
|
|
exception.HTTPForbidden,
|
|
utils.check_port_list_policy,
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_port_list_policy_forbidden_no_project(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
def mock_authorize_function(rule, target, creds):
|
|
if rule == 'baremetal:port:list_all':
|
|
raise exception.HTTPForbidden(resource='fake')
|
|
return True
|
|
mock_authorize.side_effect = mock_authorize_function
|
|
mock_pr.context.to_policy_values.return_value = {}
|
|
mock_pr.version.minor = 50
|
|
|
|
self.assertRaises(
|
|
exception.HTTPForbidden,
|
|
utils.check_port_list_policy,
|
|
)
|
|
|
|
@mock.patch.object(api, 'request', spec_set=["context", "version"])
|
|
@mock.patch.object(policy, 'authorize', spec=True)
|
|
def test_check_port_list_policy_non_admin(
|
|
self, mock_authorize, mock_pr
|
|
):
|
|
def mock_authorize_function(rule, target, creds):
|
|
if rule == 'baremetal:port:list_all':
|
|
raise exception.HTTPForbidden(resource='fake')
|
|
return True
|
|
mock_authorize.side_effect = mock_authorize_function
|
|
mock_pr.context.to_policy_values.return_value = {
|
|
'project_id': '12345'
|
|
}
|
|
mock_pr.version.minor = 50
|
|
|
|
owner = utils.check_port_list_policy()
|
|
self.assertEqual(owner, '12345')
|
|
|
|
|
|
class TestObjectToDict(base.TestCase):
|
|
|
|
def setUp(self):
|
|
super(TestObjectToDict, self).setUp()
|
|
self.node = obj_utils.get_test_node(
|
|
self.context,
|
|
created_at=datetime.datetime(2000, 1, 1, 0, 0),
|
|
updated_at=datetime.datetime(2001, 1, 1, 0, 0),
|
|
inspection_started_at=datetime.datetime(2002, 1, 1, 0, 0),
|
|
console_enabled=True)
|
|
|
|
p = mock.patch.object(api, 'request', autospec=False)
|
|
mock_req = p.start()
|
|
mock_req.public_url = 'http://192.0.2.1:5050'
|
|
self.addCleanup(p.stop)
|
|
|
|
def test_no_args(self):
|
|
self.assertEqual({
|
|
'created_at': '2000-01-01T00:00:00+00:00',
|
|
'updated_at': '2001-01-01T00:00:00+00:00',
|
|
'uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
|
}, utils.object_to_dict(self.node))
|
|
|
|
def test_no_base_attributes(self):
|
|
self.assertEqual({}, utils.object_to_dict(
|
|
self.node,
|
|
include_created_at=False,
|
|
include_updated_at=False,
|
|
include_uuid=False)
|
|
)
|
|
|
|
def test_fields(self):
|
|
self.assertEqual({
|
|
'conductor_group': '',
|
|
'console_enabled': True,
|
|
'created_at': '2000-01-01T00:00:00+00:00',
|
|
'driver': 'fake-hardware',
|
|
'inspection_finished_at': None,
|
|
'inspection_started_at': '2002-01-01T00:00:00+00:00',
|
|
'maintenance': False,
|
|
'updated_at': '2001-01-01T00:00:00+00:00',
|
|
'uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123'
|
|
}, utils.object_to_dict(
|
|
self.node,
|
|
fields=[
|
|
'conductor_group',
|
|
'console_enabled',
|
|
'driver',
|
|
'inspection_finished_at',
|
|
'inspection_started_at',
|
|
'maintenance',
|
|
])
|
|
)
|
|
|
|
def test_links(self):
|
|
self.assertEqual({
|
|
'created_at': '2000-01-01T00:00:00+00:00',
|
|
'links': [{
|
|
'href': 'http://192.0.2.1:5050/v1/node/'
|
|
'1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
|
'rel': 'self'
|
|
}, {
|
|
'href': 'http://192.0.2.1:5050/node/'
|
|
'1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
|
'rel': 'bookmark'
|
|
}],
|
|
'updated_at': '2001-01-01T00:00:00+00:00',
|
|
'uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
|
}, utils.object_to_dict(self.node, link_resource='node'))
|
|
|
|
self.assertEqual({
|
|
'created_at': '2000-01-01T00:00:00+00:00',
|
|
'links': [{
|
|
'href': 'http://192.0.2.1:5050/v1/node/foo',
|
|
'rel': 'self'
|
|
}, {
|
|
'href': 'http://192.0.2.1:5050/node/foo',
|
|
'rel': 'bookmark'
|
|
}],
|
|
'updated_at': '2001-01-01T00:00:00+00:00',
|
|
'uuid': '1be26c0b-03f2-4d2e-ae87-c02d7f33c123',
|
|
}, utils.object_to_dict(
|
|
self.node,
|
|
link_resource='node',
|
|
link_resource_args='foo'))
|
|
|
|
|
|
class TestLocalLinkValidation(base.TestCase):
|
|
|
|
def test_local_link_connection_type(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'switch_id': '0a:1b:2c:3d:4e:5f',
|
|
'port_id': 'value2',
|
|
'switch_info': 'value3'}
|
|
self.assertEqual(value, v('l', value))
|
|
|
|
def test_local_link_connection_type_datapath_id(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'switch_id': '0000000000000000',
|
|
'port_id': 'value2',
|
|
'switch_info': 'value3'}
|
|
self.assertEqual(value, v('l', value))
|
|
|
|
def test_local_link_connection_type_not_mac_or_datapath_id(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'switch_id': 'badid',
|
|
'port_id': 'value2',
|
|
'switch_info': 'value3'}
|
|
self.assertRaises(exception.InvalidSwitchID, v, 'l', value)
|
|
|
|
def test_local_link_connection_type_invalid_key(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'switch_id': '0a:1b:2c:3d:4e:5f',
|
|
'port_id': 'value2',
|
|
'switch_info': 'value3',
|
|
'invalid_key': 'value'}
|
|
self.assertRaisesRegex(
|
|
exception.Invalid,
|
|
'Additional properties are not allowed',
|
|
v, 'l', value)
|
|
|
|
def test_local_link_connection_type_missing_local_link_mandatory_key(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'switch_id': '0a:1b:2c:3d:4e:5f',
|
|
'switch_info': 'value3'}
|
|
self.assertRaisesRegex(exception.Invalid, 'is a required property',
|
|
v, 'l', value)
|
|
|
|
def test_local_link_connection_type_local_link_keys_mandatory(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'switch_id': '0a:1b:2c:3d:4e:5f',
|
|
'port_id': 'value2'}
|
|
self.assertEqual(value, v('l', value))
|
|
|
|
def test_local_link_connection_type_empty_value(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {}
|
|
self.assertEqual(value, v('l', value))
|
|
|
|
def test_local_link_connection_type_smart_nic_keys_mandatory(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
vs = utils.LOCAL_LINK_SMART_NIC_VALIDATOR
|
|
value = {'port_id': 'rep0-0',
|
|
'hostname': 'hostname'}
|
|
self.assertEqual(value, vs('l', value))
|
|
self.assertEqual(value, v('l', value))
|
|
|
|
def test_local_link_connection_type_smart_nic_keys_with_optional(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
vs = utils.LOCAL_LINK_SMART_NIC_VALIDATOR
|
|
value = {'port_id': 'rep0-0',
|
|
'hostname': 'hostname',
|
|
'switch_id': '0a:1b:2c:3d:4e:5f',
|
|
'switch_info': 'sw_info'}
|
|
self.assertEqual(value, vs('l', value))
|
|
self.assertEqual(value, v('l', value))
|
|
|
|
def test_local_link_connection_type_smart_nic_keys_hostname_missing(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
vs = utils.LOCAL_LINK_SMART_NIC_VALIDATOR
|
|
value = {'port_id': 'rep0-0'}
|
|
self.assertRaises(exception.Invalid, vs, 'l', value)
|
|
self.assertRaises(exception.Invalid, v, 'l', value)
|
|
|
|
def test_local_link_connection_type_smart_nic_keys_port_id_missing(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
vs = utils.LOCAL_LINK_SMART_NIC_VALIDATOR
|
|
value = {'hostname': 'hostname'}
|
|
self.assertRaises(exception.Invalid, vs, 'l', value)
|
|
self.assertRaises(exception.Invalid, v, 'l', value)
|
|
|
|
def test_local_link_connection_net_type_unmanaged(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'network_type': 'unmanaged'}
|
|
self.assertEqual(value, v('l', value))
|
|
|
|
def test_local_link_connection_net_type_unmanaged_combine_ok(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'network_type': 'unmanaged',
|
|
'switch_id': '0a:1b:2c:3d:4e:5f',
|
|
'port_id': 'rep0-0'}
|
|
self.assertEqual(value, v('l', value))
|
|
|
|
def test_local_link_connection_net_type_invalid(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'network_type': 'invalid'}
|
|
self.assertRaises(exception.Invalid, v, 'l', value)
|
|
|
|
def test_local_link_connection_cant_set_only_physical(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'port_id': '42',
|
|
'vtep-physical-switch': 'jswitch',
|
|
'switch_id': '0a:1b:2c:3d:4e:5f'}
|
|
self.assertRaisesRegex(
|
|
exception.Invalid,
|
|
'is a dependency of',
|
|
v, 'l', value)
|
|
|
|
def test_local_link_connection_cant_set_only_logical(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'port_id': '42',
|
|
'vtep-logical-switch': 'jswitch',
|
|
'switch_id': '0a:1b:2c:3d:4e:5f'}
|
|
self.assertRaisesRegex(
|
|
exception.Invalid,
|
|
'is a dependency of',
|
|
v, 'l', value
|
|
)
|
|
|
|
def test_local_link_connection_set_both_switches(self):
|
|
v = utils.LOCAL_LINK_VALIDATOR
|
|
value = {'port_id': '42',
|
|
'vtep-logical-switch': 'lswitch',
|
|
'vtep-physical-switch': 'pswitch'}
|
|
self.assertEqual(value, v('l', value))
|