diff options
author | Harald Jensås <hjensas@redhat.com> | 2019-01-19 23:33:31 +0100 |
---|---|---|
committer | Harald Jensås <hjensas@redhat.com> | 2019-02-15 06:17:02 +0100 |
commit | 50205ca0724c9f03731e3441bc525b5c3650369f (patch) | |
tree | bec2803b8eb6c7aa080f01febb0612c6c3fd7685 /ironic/tests/unit/api/controllers | |
parent | e34941c3279907a013b2b6ae023a089e58c3ed11 (diff) | |
download | ironic-50205ca0724c9f03731e3441bc525b5c3650369f.tar.gz |
API - Implement /events endpoint
Implements the POST /events endpoint. Allows external
entities to send events to Ironic. The default policy
for the new endpoint is "rule:is_admin".
Initial support for 'network' events properties implemented
but the EventsController will simply log a debug entry.
Story: 1304673
Task: 28988
Change-Id: I2cfebf2d0bedd35a33db7af60eaec0e5083fe16f
Diffstat (limited to 'ironic/tests/unit/api/controllers')
-rw-r--r-- | ironic/tests/unit/api/controllers/v1/test_event.py | 176 | ||||
-rw-r--r-- | ironic/tests/unit/api/controllers/v1/test_types.py | 58 |
2 files changed, 234 insertions, 0 deletions
diff --git a/ironic/tests/unit/api/controllers/v1/test_event.py b/ironic/tests/unit/api/controllers/v1/test_event.py new file mode 100644 index 000000000..7ca81dce8 --- /dev/null +++ b/ironic/tests/unit/api/controllers/v1/test_event.py @@ -0,0 +1,176 @@ +# 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. +""" +Tests for the API /events methods. +""" + +import mock +from six.moves import http_client + +from ironic.api.controllers import base as api_base +from ironic.api.controllers.v1 import types +from ironic.api.controllers.v1 import versions +from ironic.tests.unit.api import base as test_api_base +from ironic.tests.unit.api.utils import fake_event_validator + + +def get_fake_port_event(): + return {'event': 'network.bind_port', + 'port_id': '11111111-aaaa-bbbb-cccc-555555555555', + 'mac_address': 'de:ad:ca:fe:ba:be', + 'status': 'ACTIVE', + 'device_id': '22222222-aaaa-bbbb-cccc-555555555555', + 'binding:host_id': '22222222-aaaa-bbbb-cccc-555555555555', + 'binding:vnic_type': 'baremetal'} + + +class TestPost(test_api_base.BaseApiTest): + + def setUp(self): + super(TestPost, self).setUp() + self.headers = {api_base.Version.string: str( + versions.max_version_string())} + + @mock.patch.object(types.EventType, 'event_validators', + {'valid.event': fake_event_validator}) + def test_events(self): + events_dict = {'events': [{'event': 'valid.event'}]} + response = self.post_json('/events', events_dict, headers=self.headers) + self.assertEqual(http_client.NO_CONTENT, response.status_int) + + @mock.patch.object(types.EventType, 'event_validators', + {'valid.event1': fake_event_validator, + 'valid.event2': fake_event_validator, + 'valid.event3': fake_event_validator}) + def test_multiple_events(self): + events_dict = {'events': [{'event': 'valid.event1'}, + {'event': 'valid.event2'}, + {'event': 'valid.event3'}]} + response = self.post_json('/events', events_dict, headers=self.headers) + self.assertEqual(http_client.NO_CONTENT, response.status_int) + + def test_events_does_not_contain_event(self): + events_dict = {'events': [{'INVALID': 'fake.event'}]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + @mock.patch.object(types.EventType, 'event_validators', + {'valid.event': fake_event_validator}) + def test_events_invalid_event(self): + events_dict = {'events': [{'event': 'invalid.event'}]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + def test_network_unknown_event_property(self): + events_dict = {'events': [{'event': 'network.unbind_port', + 'UNKNOWN': 'EVENT_PROPERTY'}]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + def test_network_bind_port_events(self): + events_dict = {'events': [get_fake_port_event()]} + response = self.post_json('/events', events_dict, headers=self.headers) + self.assertEqual(http_client.NO_CONTENT, response.status_int) + + def test_network_unbind_port_events(self): + events_dict = {'events': [get_fake_port_event()]} + events_dict['events'][0].update({'event': 'network.unbind_port'}) + response = self.post_json('/events', events_dict, headers=self.headers) + self.assertEqual(http_client.NO_CONTENT, response.status_int) + + def test_network_delete_port_events(self): + events_dict = {'events': [get_fake_port_event()]} + events_dict['events'][0].update({'event': 'network.delete_port'}) + response = self.post_json('/events', events_dict, headers=self.headers) + self.assertEqual(http_client.NO_CONTENT, response.status_int) + + def test_network_port_event_invalid_mac_address(self): + port_evt = get_fake_port_event() + port_evt.update({'mac_address': 'INVALID_MAC_ADDRESS'}) + events_dict = {'events': [port_evt]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + def test_network_port_event_invalid_device_id(self): + port_evt = get_fake_port_event() + port_evt.update({'device_id': 'DEVICE_ID_SHOULD_BE_UUID'}) + events_dict = {'events': [port_evt]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + def test_network_port_event_invalid_port_id(self): + port_evt = get_fake_port_event() + port_evt.update({'port_id': 'PORT_ID_SHOULD_BE_UUID'}) + events_dict = {'events': [port_evt]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + def test_network_port_event_invalid_status(self): + port_evt = get_fake_port_event() + port_evt.update({'status': ['status', 'SHOULD', 'BE', 'TEXT']}) + events_dict = {'events': [port_evt]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + def test_network_port_event_invalid_binding_vnic_type(self): + port_evt = get_fake_port_event() + port_evt.update({'binding:vnic_type': ['binding:vnic_type', 'SHOULD', + 'BE', 'TEXT']}) + events_dict = {'events': [port_evt]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + def test_network_port_event_invalid_binding_host_id(self): + port_evt = get_fake_port_event() + port_evt.update({'binding:host_id': ['binding:host_id', 'IS', + 'NODE_UUID', 'IN', 'IRONIC']}) + events_dict = {'events': [port_evt]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=self.headers) + self.assertEqual(http_client.BAD_REQUEST, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) + + @mock.patch.object(types.EventType, 'event_validators', + {'valid.event': fake_event_validator}) + def test_events_unsupported_api_version(self): + headers = {api_base.Version.string: '1.50'} + events_dict = {'events': [{'event': 'valid.event'}]} + response = self.post_json('/events', events_dict, expect_errors=True, + headers=headers) + self.assertEqual(http_client.NOT_FOUND, response.status_int) + self.assertEqual('application/json', response.content_type) + self.assertTrue(response.json['error_message']) diff --git a/ironic/tests/unit/api/controllers/v1/test_types.py b/ironic/tests/unit/api/controllers/v1/test_types.py index 7c85c363e..9d5600f90 100644 --- a/ironic/tests/unit/api/controllers/v1/test_types.py +++ b/ironic/tests/unit/api/controllers/v1/test_types.py @@ -27,6 +27,7 @@ from ironic.api.controllers.v1 import types from ironic.common import exception from ironic.common import utils from ironic.tests import base +from ironic.tests.unit.api.utils import fake_event_validator class TestMacAddressType(base.TestCase): @@ -393,3 +394,60 @@ class TestVifType(base.TestCase): v = types.viftype self.assertRaises(exception.InvalidUuidOrName, v.frombasetype, {'id': 5678}) + + +class TestEventType(base.TestCase): + + def setUp(self): + super(TestEventType, self).setUp() + self.v = types.eventtype + + @mock.patch.object(types.EventType, 'event_validators', + {'valid.event': fake_event_validator}) + def test_simple_event_type(self): + value = {'event': 'valid.event'} + self.assertItemsEqual(value, self.v.validate(value)) + + @mock.patch.object(types.EventType, 'event_validators', + {'valid.event': fake_event_validator}) + def test_invalid_event_type(self): + value = {'event': 'invalid.event'} + self.assertRaisesRegex(exception.Invalid, 'invalid.event is not a ' + 'valid event.', + self.v.validate, value) + + def test_event_missing_madatory_field(self): + value = {'invalid': 'invalid'} + self.assertRaisesRegex(exception.Invalid, 'Missing mandatory keys:', + self.v.validate, value) + + def test_network_port_event(self): + value = {'event': 'network.bind_port', + 'port_id': '11111111-aaaa-bbbb-cccc-555555555555', + 'mac_address': 'de:ad:ca:fe:ba:be', + 'status': 'ACTIVE', + 'device_id': '22222222-aaaa-bbbb-cccc-555555555555', + 'binding:host_id': '22222222-aaaa-bbbb-cccc-555555555555', + 'binding:vnic_type': 'baremetal' + } + self.assertItemsEqual(value, self.v.validate(value)) + + def test_invalid_mac_network_port_event(self): + value = {'event': 'network.bind_port', + 'port_id': '11111111-aaaa-bbbb-cccc-555555555555', + 'mac_address': 'INVALID_MAC_ADDRESS', + 'status': 'ACTIVE', + 'device_id': '22222222-aaaa-bbbb-cccc-555555555555', + 'binding:host_id': '22222222-aaaa-bbbb-cccc-555555555555', + 'binding:vnic_type': 'baremetal' + } + self.assertRaises(exception.InvalidMAC, self.v.validate, value) + + def test_missing_mandatory_fields_network_port_event(self): + value = {'event': 'network.bind_port', + 'device_id': '22222222-aaaa-bbbb-cccc-555555555555', + 'binding:host_id': '22222222-aaaa-bbbb-cccc-555555555555', + 'binding:vnic_type': 'baremetal' + } + self.assertRaisesRegex(exception.Invalid, 'Missing mandatory keys:', + self.v.validate, value) |